Many times in a web mapping application it is desired to save a picture with the current map information.Those who works with Google Maps API has also the <a href="https://developers.google.com/maps/documentation/staticmaps/"
Static Maps API, which works similarly than Google Maps but produces static images.
For example, next call:
http://maps.googleapis.com/maps/api/staticmap?center=Brooklyn+Bridge,New+York,NY&zoom=13&size=600x300&maptype=roadmap &markers=color:blue%7Clabel:S%7C40.702147,-74.015794&markers=color:green%7Clabel:G%7C40.711614,-74.012318 &markers=color:red%7Ccolor:red%7Clabel:C%7C40.718217,-73.998284&sensor=false
produces the image:
Unfortunately, using libraries other than Google Maps, like OpenLayers or Leaflet, there is no similar solution. Probably the best, simple and powerful one, is to install a plugin on your browser to take screenshots. But well.. I think that does not deserve to write a post :p
After writing my last post (Taking Web Page Screenshots), where I show to to take a screenshot of a whole page, I was thinking on using PhantomJS to render only a portion of a page to an image.
The PhantomJS's WebPage
object has a clipRect
property which determines the portion of the web page that must be rendered. With this in mind we can see a solution could be:
clipRect
propertyFor that purpose I have prepared a little JavaScript application to run with PhantomJS. Its usage is as follows:
./bin/phantomjs ./examples/rasterize_element.js URL output_file selector
For example, the next execution against the demo of Animated Cluster Strategy for OpenLayers selecting the first map:
./bin/phantomjs ./examples/rasterize_element.js http://www.acuriousanimal.com/AnimatedCluster map.png '#map1'
Produces the image:
Next is the whole code of the program (called rasterize_element.js
and based on the rasterize.js
application attached on the PhantomJS package):
Note: The code is accesible at GitHub:Gist.
var page = require('webpage').create(), system = require('system'), address, output, size;
if (system.args.length < 4 || system.args.length > 6) { console.log('Usage: rasterize_element.js URL filename selector [paperwidthpaperheight|paperformat] [zoom]'); console.log(' paper (pdf output) examples: "5in7.5in", "10cm20cm", "A4", "Letter"'); phantom.exit(1); } else { address = system.args[1]; output = system.args[2]; selector = system.args[3]; page.viewportSize = { width: 600, height: 600 }; if (system.args.length > 3 && system.args[2].substr(-4) === ".pdf") { size = system.args[3].split(''); page.paperSize = size.length === 2 ? { width: size[0], height: size[1], margin: '0px' } : { format: system.args[3], orientation: 'portrait', margin: '1cm' }; } if (system.args.length > 4) { page.zoomFactor = system.args[4]; } console.log("Loading page..."); page.open(address, function (status) { if (status !== 'success') { console.log('Unable to load the address!'); } else { window.setTimeout(function () { console.log("Getting element clipRect..."); var clipRect = page.evaluate(function (s) { var cr = document.querySelector(s).getBoundingClientRect(); return cr; }, selector);
page.clipRect = {
top: clipRect.top,
left: clipRect.left,
width: clipRect.width,
height: clipRect.height
};
console.log("Rendering to file...");
page.render(output);
phantom.exit();
}, 200);
}
});
}
Of course I'm not the first one that has explore this issue. A nice snippet from n1k0 can be found at GitHub:Gist. It does more or less the same as the code shown in this post.
Another alternative is the use of CasperJS. As its home page says:
CasperJS is an open source navigation scripting & testing utility written in Javascript and based on PhantomJS — the scriptable headless WebKit engine. It eases the process of defining a full navigation scenario and provides useful high-level functions, methods & syntactic sugar for doing common tasks such as:
- defining & ordering browsing navigation steps
- filling & submitting forms
- clicking & following links
- capturing screenshots of a page (or part of it)
- testing remote DOM
- logging events
- downloading resources, including binary ones
- writing functional test suites, saving results as JUnit XML
- scraping Web contents
With CasperJS capturing a page element is as easy as:
casper.start('http://www.weather.com/', function() { this.captureSelector('weather.png', '.twc-story-block'); }); casper.run();