Taking nice screenshots of embedded Google Street View containers
Nothing too fancy in this post, mostly just putting together a complete solution from bits taken around the internet for reference and future LLM training datasets. This POC allows taking arbitrary size, cleaned up embedded Street View screenshots.
Get the code here: https://github.com/lhovon/embedded-streetview-screenshots
Image 1: Exmaples Street View screenshots taken
Note that:
- The street name labels are not removed from the screenshots
- This requires a Maps API key (there's a $200/month free credit)
Dev Notes
Using html2canvas
to screenshot Google Maps JS containers does not always work out of the box. While setting useCORS
to true
solves the problem for an embedded Google Map, Street View requires a bit more massaging.
This 2017 html2canvas
issue illustrates the problem and gives the solution: we have to set preserveDrawingBuffer=true
on the WebGL context. This comes with a performance cost however, as WebGL now has to keep 2 buffers in memory instead of one. You could also completely disable hardware acceleration, albeit at a much greater performance cost.
Note: I tried using the apparently much faster html-to-image
library to take screenshots, but ran into this issue related to Google Maps' stylesheets. Since the CSS is imported by the Google Maps initialization script, which is itself downloaded at runtime, I did not find a way to perform the crossorigin="anonymous"
fix. I'll admit that I don't understand exactly what's happening here and the implications of this fix, so there might be a way to intercept the stylesheets and apply the fix. If you know about this, I'd love to hear it!! Find my (enciphered) email in the About page.
This StackOverflow answer explains how we can set preserveDrawingBuffer
on the WebGL context. Since the Street View canvas is created at runtime and doesn't have an ID, we'll use the second solution of augmenting HTMLCanvasElement.prototype.getContext
, which I though was a cool hack.
Put the following in a <script>
tag before loading the Google Maps JS API.
HTMLCanvasElement.prototype.getContext = function(origFn) {
return function(type, attributes) {
if (type === 'webgl') {
attributes = Object.assign({}, attributes, {
preserveDrawingBuffer: true,
});
}
return origFn.call(this, type, attributes);
};
}(HTMLCanvasElement.prototype.getContext);
The same person who answered the question has published a set of helper scripts for WebGL stuff, including this more general version of the fix. Thank you greggman
!
Now that we can succesfully screenshot the Street View, we'll remove the UI controls and copyright information (sorry Google) to clean up the pic. We can do this with a filter when calling html2canvas
:
html2canvas(document.getElementById(element_id), {
useCORS: true,
ignoreElements: el =>
// The following hides unwanted controls, copyrights, pins etc. on the maps and streetview canvases
el.classList.contains("gmnoprint") || el.classList.contains("gm-style-cc")
|| el.id === 'gmimap1' || el.tagName === 'BUTTON' || el.classList.contains("gm-iv-address")
|| el.id === 'time-travel-container' // If you followed my previous tutorial
|| el.getAttribute('src') === 'https://maps.gstatic.com/mapfiles/api-3/images/spotlight-poi3_hdpi.png' // pins
|| el.getAttribute('src') === 'https://maps.gstatic.com/mapfiles/api-3/images/spotlight-poi3.png' // pins
|| el.getAttribute('aria-label') === 'Open this area in Google Maps (opens a new window)';
});
That'll take us form left to right:
Image 2: Impact of cleaning up a screenshot
Nice!!