1

I am currently working on a solution to generate clientside PDFs from OpenLayers content. The experience is okay so far, but I am having a bit of a hassle.

The abstract is as follows:

  • Calculating the amount of pixels needed to fill a given space on paper
  • Temporarily set the map to given resolution
  • read canvas
  • do print stuff
  • reset map to previous resolution

In code:

      var mapSizeForPrint = [
        // in pixel
        Math.round(this.pdf.width * pixelsPerMapMillimeter),
        Math.round(this.pdf.height * pixelsPerMapMillimeter)
      ];
      ...
      var map = this.openLayersMap();
      ...
      this.mapExtent = map.getView().calculateExtent(this.mapSize);
      ...
      this.rendercompleteListener = map.once("rendercomplete", event => {
       //Do printing magic
      }
      map.setSize(mapSizeForPrint);
      map.getView().fit(this.mapExtent, { size: mapSizeForPrint });

If anyone is interested in the (dirty) details: full code is available as open source here

That works to a certain degree.

  • scaling to 80 DPI, I get an expected subset of the map
  • scaling to 120 DPI, I get a relatively expected subset of the map
  • scaling to 200 DPI, getting an unexpected result

application 80DPI 200DPI

Currently I assume, that I get the "same" dataset for any given resolution - i.e. the visible center of the 80DPI version is "the same" as the 200DPI only that it doesn't fit on screen; but that seems not to be the case.

Does anybody give me a hint in which direction to look? Perhaps I have some false assumptions which I currently do not see or do not see, how to overcome.

4

It's because in OpenLayers 5 by default fit constrains the resolution to an integer zoom level. That can be fixed by adding constrainResolution: false to the options. Also note that calculateExtent() and fit() do not give the desired result when the view is rotated so you could replace them completely by setting resolution so in this example https://openlayers.org/en/latest/examples/export-pdf.html

    var extent = map.getView().calculateExtent(size);

    map.once('rendercomplete', function(event) {
      var canvas = event.context.canvas;
      var data = canvas.toDataURL('image/jpeg');
      var pdf = new jsPDF('landscape', undefined, format);
      pdf.addImage(data, 'JPEG', 0, 0, dim[0], dim[1]);
      pdf.save('map.pdf');
      // Reset original map size
      map.setSize(size);
      map.getView().fit(extent, {size: size});
      exportButton.disabled = false;
      document.body.style.cursor = 'auto';
    });

    // Set print size
    var printSize = [width, height];
    map.setSize(printSize);
    map.getView().fit(extent, {size: printSize});

becomes

    var viewResolution = map.getView().getResolution();

    map.once('rendercomplete', function(event) {
      var canvas = event.context.canvas;
      var data = canvas.toDataURL('image/jpeg');
      var pdf = new jsPDF('landscape', undefined, format);
      pdf.addImage(data, 'JPEG', 0, 0, dim[0], dim[1]);
      pdf.save('map.pdf');
      // Reset original map size
      map.setSize(size);
      map.getView().setResolution(viewResolution);
      exportButton.disabled = false;
      document.body.style.cursor = 'auto';
    });

    // Set print size
    var printSize = [width, height];
    map.setSize(printSize);
    var scaling = Math.min(width / size[0], height / size[1]);
    map.getView().setResolution(viewResolution / scaling);
  • Thank you very much! Works like a charm. – Thomas Junk Jul 17 at 10:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.