Wednesday, 9 September 2015

OpenLayers generating clientside PDFs



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.



Answer



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);

No comments:

Post a Comment

arcpy - Changing output name when exporting data driven pages to JPG?

Is there a way to save the output JPG, changing the output file name to the page name, instead of page number? I mean changing the script fo...