27

I'm using Mapbox GL JS version 0.32. Is there a way to export the map to a high-res PNG or PDF?

Obviously, I can just screenshot, but it would be nice if there was a more formal way.

I found this repo, but it looks old and isn't clear how it works.

I tried using the preserveDrawingBuffer option:

var map = new mapboxgl.Map({
    container: 'map',
    style: 'mapbox://styles/mapbox/light-v9',
    minZoom: 4,
    maxZoom: 14,
    center: [-2.0, 53.3],
    preserveDrawingBuffer: true
});
console.log(map.getCanvas().toDataURL());

This outputs a long data URL in the console, but copying and pasting it into a base64 converter just seems to produce an empty image.

UPDATE: This is my new code, in full:

mapboxgl.accessToken = 'pk.eyXXX';
var map = new mapboxgl.Map({
    container: 'map',
    style: 'mapbox://styles/mapbox/light-v9',
    minZoom: 4,
    maxZoom: 14,
    center: [-2.0, 53.3],
    preserveDrawingBuffer: true
});
var dpi = 300;
Object.defineProperty(window, 'devicePixelRatio', {
    get: function() {return dpi / 96}
});

map.on('load', function () {
    var content = map.getCanvas().toDataURL();
    console.log(content)
});

The output to the console is this: http://pastebin.com/raw/KhyJkJWJ

Richard
  • 62,943
  • 126
  • 334
  • 542
  • 3
    Well, it is working. Your console output of `content` looks like Ireland, UK and Western Europe to me. Most browsers can view it by simply c&p'ing it in the address bar. To save the png, use [`toBlob()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob) instead of `toDataURL()`. And last not least: to render that map in high-res, have a look into https://github.com/mpetroff/print-maps for details. – sgelb Mar 03 '17 at 19:05
  • 1
    Thanks. I eventually worked out that Firefox will do this, but Chrome won't - hence my confusion! – Richard Mar 05 '17 at 19:46
  • 1
    Is there a way to do this 'headless' I don't have any need for any actual map to be drawn in the browser, I just wanna loop through a bunch of layers taking snapshots to use as thumbnails / previews? – maxwell Sep 28 '18 at 17:27
  • Yes! The `preserveDrawingBuffer: true` is required!! :) – Worm Sep 08 '22 at 19:50

3 Answers3

32

There are two main questions:

1. How do I get the map canvas as an image?

Actually, you are doing the right thing, but just too early. Give that map some time to load and fetch the image data when the load event is triggered:

map.on('load', () => console.log(map.getCanvas().toDataURL()));

2. How do I get that image in high-res?

By changing window.devicePixelRatio according to your destination dpi, you can trick your browser into generating high-res output. I found that solution in an implementation created by Matthew Petroff, see his code on https://github.com/mpetroff/print-maps. This is the trick he's using for generating high-res output:

Object.defineProperty(window, 'devicePixelRatio', {
    get: function() {return dpi / 96}
});

Source

sgelb
  • 575
  • 4
  • 9
  • Thanks! I still have the same problem though even after moving the line to after image load. If I paste the output into an online decoder it says it's invalid: if I paste it into a text file and save it with a `.png` extension, I can't open the file. Are others able to successfully use this method to get PNGs? – Richard Mar 03 '17 at 10:03
  • 12
    for people wondering why the image is blank... make sure you have `preserveDrawingBuffer: true` in mapbox gl options – Vic Jan 25 '18 at 18:33
5

I created a simple working example for anybody stumbling upon this thread:

(Thanks @Vic for pointing out the preserveDrawingBuffer-option in Mapbox GL JS)

<!DOCTYPE html>
<html>

<head>
    <meta charset='utf-8' />
    <title>Display a map</title>
    <meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
    <script src='https://api.tiles.mapbox.com/mapbox-gl-js/v0.44.1/mapbox-gl.js'></script>
    <link href='https://api.tiles.mapbox.com/mapbox-gl-js/v0.44.1/mapbox-gl.css' rel='stylesheet' />
    <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
    <style>
    #map {
        margin: auto;
        width: 400px;
        height: 400px;
    }
    </style>
</head>

<body>
    <div id='map'></div>
    <a id="downloadLink" href="" download="map.png">Download ↓</a>
    <div id="image"></div>
    <script>
    mapboxgl.accessToken = 'your-token-here';
    var map = new mapboxgl.Map({
        container: 'map',
        style: 'mapbox://styles/mapbox/streets-v9',
        center: [-74.50, 40],
        zoom: 9,
        preserveDrawingBuffer: true
    });

    $('#downloadLink').click(function() {
        var img = map.getCanvas().toDataURL('image/png')
        this.href = img
    })
    </script>
</body>

</html>
st_phan
  • 715
  • 9
  • 23
0

For better results, you can implement the @watergis/mapbox-gl-export package, it can gives you more options and feature than direct data URL.

Check this link : mapbox-gl-export

Posting this answer if someone get help from this.

Aryadev
  • 11
  • 3