humangeo / leaflet-dvf

Leaflet Data Visualization Framework
MIT License
689 stars 153 forks source link

Export to image #89

Open testuserx opened 8 years ago

testuserx commented 8 years ago

I am wondering how to export/save, for example, choropleth map with charts (http://humangeo.github.io/leaflet-dvf/examples/html/election2012results.html) to PNG or any other image?

With Leaflet plugins, like "Leaflet-Save-Map-to-PNG" or "leaflet-image" is not possible to achieve this result.

Thanks!

sfairgrieve commented 8 years ago

I'll look into this. Unfortunately, it might not be easy at the moment without some work on your end. Theses layers are SVG/vector layers, and I believe most of the export-to-image plugins require actual raster image-based layers, or the ability to have these layers rendered on Canvas instead of using SVG. Currently, the DVF chart layers don't have corresponding Canvas renderer methods, which means that they wouldn't work if Leaflet's Canvas renderer is used. I'll do some research, though.

snkashis commented 8 years ago

I'd recommend using PhantomJS (or another headless tool like it) to capture to PNG.

rickj33 commented 8 years ago

What I did to accomplish this was to create my own SaveImage object and custom renders for tile,canvas, svg.

the SaveImage.createImage function takes the map as a parameter.

The SVG custom renderer

If you would like, I could expose the repository of my modifications, keeping in mind I am fairly new to JS.

Rick

sfairgrieve commented 8 years ago

Thanks @rickj33! I would definitely be interested in seeing it.

rickj33 commented 8 years ago

Ok, I will try to clean up the code and put it in a public repo

rickj33 commented 8 years ago

Just letting you know, I hope to have the code cleaned up and an example made by monday 8/1/2017.

sfairgrieve commented 8 years ago

Awesome, thanks @rickj33!

rickj33 commented 8 years ago

@sfairgrieve I was able to pull out the code I use to capture map images with leaflet. The repo is here. (https://github.com/rickj33/captureLeafletImage) (the demo is testimage.html) to get it to work, you will need to run npm install to install the required js files.

I originally wrote this about a year ago and really has not been looked at since. There was major issue we ran across and that is related to zoom animation. When animation is enabled, and the map is zoomed in/out or panned, then when I captured the image, the svg markers were not in the correct position (https://github.com/Leaflet/Leaflet/issues/3313) . I did run across this article that may be related to the positioning (https://blog.sumbera.com/2015/08/14/svg-fast-scaled-overlay-on-leaflet-1-0/). For our needs, we just turn off the animation on the map.

To make this work, I created "virtual renderers" to handle the process of creating the final canvas. To start, i just took the current renderers and copy the code, I thought about trying to extend the current renderers but at the time it was easier to copy the internals, as these renderers would not be drawing on the display.

The logic of the image service is pretty simple. You pass in the map object, then the service loops through all the layers looking for tile layers, layers with a canvas renderer, layers with svg renderer. It then off loads the processing of the 3 layer types to the virtual renders. when done, then the service takes the results and combines them on a single canvas to return.

I used a cloned version of canvg (https://github.com/rickj33/canvg) where i added some code to fix some memory leaks and added support for the transform to be defined in a style property instead of an svg attribute.

I am sure there is plenty of cleanup and improvements which can be made. Thanks for the great svg markers. Rick