🏠 back to Observable

save a map (Openlayers)

Hi., I have a map created with openlayers.
how can I save it in a good quaity ?
PDF or PNG or SVG …

I found few examples on the web but couldn’t mak it work on observable…


If you could please link in your attempt and also your examples, it might be helpful when trying to troubleshoot.

To begin with the easiest: have you seen that you can download your current view to PNG just by clicking the three dots next to (left of) the map cell and selecting that option? Here’s a download I just took using this method after zooming in on the US Great Lakes:

If you’re looking for great control over printing options or print to PDF, here’s a start (using a notebook forked from one I linked in our other thread):

It’s not fully working. The code is from the OpenLayers: Export PDF example page.

I think the part that I don’t have working correctly relates to this line:

import {toJpeg} from 'html-to-image;

In my notebook, I tried to get this working first by importing hmtl-to-image as follows:

html_to_image = require(‘html-to-image@0.1.1/lib/index.js’).catch(() => window["__importDefault"])

then, in place of the import {component} from approach, I tried html_to_image.toJpeg … but this might not be correct.

… if that’s not the issue, then my guess it’s either about the exportButton.addEventListener('click', function() { section, or it’s about how I linked dim to my vectorLayer (whereas this was a manually defined box in the OpenLayers.org example… and might be affecting the page size calculations).

Note that when you click the button, it gets disabled and the cursor shows ‘progress’ (as intended), but the download never happens.

Anyway… maybe a start? Would be fun to see where you got to! :wink:

Hope this helps nudge things forward. I probably can’t be of much more help beyond this, so let’s hope someone else takes this on. Good luck!

Thanks for pointing the “Dowload png” option in the menu of the cell of observablehq. that is a start.

looking for high resolution download now…

here my test notebook:

I also look at openlayers examples but the problem (in your example also) is that html-to-image do not load properly, so we cannot use the function toPng or toJpeg… I also get problem to load jsPDF

You can get html-to-image and jsPdf to load by using https://bundle.run:

html_to_image = require('https://bundle.run/html-to-image@0.1.1')

jsPDF = require('https://bundle.run/jspdf@1.5.3')

Unfortunately, I haven’t gotten the code to work; there’s a console error saying: “TypeError: dim is undefined”…

1 Like

What is happening when one uses an arrow function in the ‘Global Leaks Pattern’? And should we still be using this pattern with bundle.run?

html_to_image = require('https://bundle.run/html-to-image@0.1.1/lib/index.js').catch(() => window["__importDefault"])

By the way, I updated the code in my notebook using bundle.run, and now I get different errors… but also a consistent one, namely that of a ‘module within a module’ not being defined / failing to be required in (in this case exports. Isn’t this the same issue as I was hitting up against in are there non-portable npm modules? or can something be done about them?

With regard to the error on dims, I am not sure if I botched this or not, but I substituted out this vector square used in the example for my own vector layer:

var dim = dims[format];


var dim = vectorLayer[format];

(and maybe this broke my code; I should try to revert to the original)

You shouldn’t need to use this with bundle.run. The .catch is a Promise method which runs the function inside after the Promise object is rejected. The “Global Leaks Pattern” relies on the fact that even though the require fails, the loaded script still attaches some module object to the global window object, and the arrow function simply returns the reference to that (or undefined if that wasn’t present). Generally, though the module object won’t be named something weird like __importDefault, it will usually have a name that’s similar to the package name. When the require debugger suggests pulling something strange from window, it usually means that the require won’t work.

I can’t reproduce the module within a module error. Here’s my fork:

As far as I can tell the trouble happens here:

      var format = document.getElementById('format').value;
      // ...
      var dim = vectorLayer[format];

The format element is part of the dropdown form (which I might recommend rewriting using Observable’s views, so that one doesn’t have to rely on the anti-pattern of document.getElementById). As far as I can tell (see my console.log line), dim is still properly set to 'a4' (or whatever the dropdown shows) when the Export PDF button is clicked.

The issue is that the vectorLayer object has no 'a4' (or ‘a3’, etc.) property, so when the button is clicked, dim ends up undefined, and then stuff later on breaks. I have no experience with openLayers so I’m afraid I can’t quickly figure out where such properties are supposed to come from.

Oh, I see this line is from a modification you just described. Yes, if there’s a dims object as well with an 'a4' property then I think this error will go away.

Indeed, in my fork, I changed the dim line to:

      var dim = [100, 100];

and now I can save a PDF.

(Sorry for the stream-of-consciousness at the end. I saw you updating your post while I was writing mine, so I just added more stuff as I tried it.)

1 Like

Very cool! And sorry for updating my response while you were typing… I should be more patient and methodical (as you appear to be ).

My console error went away when I re-checked just now… not sure what was up.

This is terrific! And I think you just answered the OPs question! I’ll try re-working my code to add in the Natural Earth vector layer correctly. Thanks for all these generous lessons!

1 Like

PS: I did totally botch the dims part! This was shorthand for dimensions - it set the page size dimensions for printing (i just guessed incorrectly that these were coordinates).

I’ve re-published an update that restores the original code, and now all printing options work. Thanks again!

1 Like

Hey guys,
Thanks for the help… I suceed to import the modules as expected but I still have some issue of resolution with PDF. I updated my notebook

The issue is more visible when chose 150dpi. When I click on Export, the pdf is created but the map is very small at the corner and the majority of the page is blank (filled with my backround color). If I modify the export callback function ( for example changing the i value at the begining) then the export behave strangely !
When I click on Export (after an edit of the function), the previous listener export the old map that is now taking the full page. Then the new listener export the new map that is small in the corner…
I Don’t find the proper way to clean my listener and resizing to acheve the download of fuul page map in one go …

1 Like