From e2ac566c05c6670704b577c316998e9dbf4f4c4f Mon Sep 17 00:00:00 2001
From: mike-000 <49240900+mike-000@users.noreply.github.com>
Date: Thu, 16 Jan 2020 23:41:52 +0000
Subject: [PATCH 1/8] Correct resolution used for scale. Add dpi option. Also
add setDpi and setMinWidth methods
Add Print to scale example
---
examples/print-to-scale.css | 6 ++
examples/print-to-scale.html | 44 ++++++++++++
examples/print-to-scale.js | 132 +++++++++++++++++++++++++++++++++++
src/ol/control/ScaleLine.js | 34 ++++++++-
4 files changed, 214 insertions(+), 2 deletions(-)
create mode 100644 examples/print-to-scale.css
create mode 100644 examples/print-to-scale.html
create mode 100644 examples/print-to-scale.js
diff --git a/examples/print-to-scale.css b/examples/print-to-scale.css
new file mode 100644
index 0000000000..383a21eb6b
--- /dev/null
+++ b/examples/print-to-scale.css
@@ -0,0 +1,6 @@
+.container {
+ max-width: 566px;
+ width: 100%;
+ height: 400px;
+ overflow: hidden;
+}
diff --git a/examples/print-to-scale.html b/examples/print-to-scale.html
new file mode 100644
index 0000000000..7f4068e41a
--- /dev/null
+++ b/examples/print-to-scale.html
@@ -0,0 +1,44 @@
+---
+layout: example.html
+title: Print to scale example
+shortdesc: Example of printing a map to a specified scale.
+docs: >
+ Example of printing a map to a specified scale.
+ The print is exported as a PDF using the jsPDF library.
+ Unlike the Export PDF example the on screen map is only used to set the center and rotation.
+ The extent printed depends on the scale and page size.
+tags: "print, printing, scale, scaleline, export, pdf"
+resources:
+ - https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.2.61/jspdf.min.js
+---
+
+
+Export PDF
diff --git a/examples/print-to-scale.js b/examples/print-to-scale.js
new file mode 100644
index 0000000000..80e8e30e4d
--- /dev/null
+++ b/examples/print-to-scale.js
@@ -0,0 +1,132 @@
+import Map from '../src/ol/Map.js';
+import View from '../src/ol/View.js';
+import {defaults as defaultControls, ScaleLine} from '../src/ol/control.js';
+import WMTSCapabilities from '../src/ol/format/WMTSCapabilities.js';
+import TileLayer from '../src/ol/layer/Tile.js';
+import {get as getProjection, getPointResolution} from '../src/ol/proj.js';
+import {register} from '../src/ol/proj/proj4.js';
+import WMTS, {optionsFromCapabilities} from '../src/ol/source/WMTS.js';
+import proj4 from 'proj4';
+
+import {toJpeg} from 'html-to-image';
+
+
+proj4.defs('EPSG:27700', '+proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 ' +
+ '+x_0=400000 +y_0=-100000 +ellps=airy ' +
+ '+towgs84=446.448,-125.157,542.06,0.15,0.247,0.842,-20.489 ' +
+ '+units=m +no_defs');
+
+register(proj4);
+
+const proj27700 = getProjection('EPSG:27700');
+proj27700.setExtent([0, 0, 700000, 1300000]);
+
+const raster = new TileLayer();
+
+const url = 'https://tiles.arcgis.com/tiles/qHLhLQrcvEnxjtPr/arcgis/rest/services/OS_Open_Raster/MapServer/WMTS';
+fetch(url)
+ .then(function(response) {
+ return response.text();
+ })
+ .then(function(text) {
+ const result = new WMTSCapabilities().read(text);
+ const options = optionsFromCapabilities(result, {
+ layer: 'OS_Open_Raster'
+ });
+ options.attributions = 'Contains OS data © Crown Copyright and database right ' + new Date().getFullYear();
+ options.crossOrigin = '';
+ options.projection = proj27700;
+ options.wrapX = false;
+ raster.setSource(new WMTS(options));
+ });
+
+
+const map = new Map({
+ layers: [raster],
+ controls: defaultControls({
+ attributionOptions: {collapsible: false}
+ }),
+ target: 'map',
+ view: new View({
+ center: [373500, 436500],
+ projection: proj27700,
+ zoom: 7
+ })
+});
+
+const scaleLine = new ScaleLine({bar: true, text: true});
+map.addControl(scaleLine);
+
+
+const dims = {
+ a0: [1189, 841],
+ a1: [841, 594],
+ a2: [594, 420],
+ a3: [420, 297],
+ a4: [297, 210],
+ a5: [210, 148]
+};
+
+
+// export options for html-to-image.
+// See: https://github.com/bubkoo/html-to-image#options
+const exportOptions = {
+ filter: function(element) {
+ const className = element.className || '';
+ return (
+ className.indexOf('ol-control') === -1 ||
+ className.indexOf('ol-scale') > -1 ||
+ (className.indexOf('ol-attribution') > -1 &&
+ className.indexOf('ol-uncollapsible'))
+ );
+ }
+};
+
+const exportButton = document.getElementById('export-pdf');
+
+exportButton.addEventListener('click', function() {
+
+ exportButton.disabled = true;
+ document.body.style.cursor = 'progress';
+
+ const format = document.getElementById('format').value;
+ const resolution = document.getElementById('resolution').value;
+ const scale = document.getElementById('scale').value;
+ const dim = dims[format];
+ const width = Math.round(dim[0] * resolution / 25.4);
+ const height = Math.round(dim[1] * resolution / 25.4);
+ const viewResolution = map.getView().getResolution();
+ const scaleResolution = scale / getPointResolution(
+ map.getView().getProjection(),
+ resolution / 25.4,
+ map.getView().getCenter()
+ );
+
+ map.once('rendercomplete', function() {
+ exportOptions.width = width;
+ exportOptions.height = height;
+ toJpeg(map.getViewport(), exportOptions).then(function(dataUrl) {
+ const pdf = new jsPDF('landscape', undefined, format);
+ pdf.addImage(dataUrl, 'JPEG', 0, 0, dim[0], dim[1]);
+ pdf.save('map.pdf');
+ // Reset original map size
+ scaleLine.setDpi();
+ scaleLine.setMinWidth();
+ map.getTargetElement().style.width = '';
+ map.getTargetElement().style.height = '';
+ map.updateSize();
+ map.getView().setResolution(viewResolution);
+ exportButton.disabled = false;
+ document.body.style.cursor = 'auto';
+ });
+ });
+
+ // Set print size
+ scaleLine.setDpi(resolution);
+ scaleLine.setMinWidth(resolution * 4 / 2.54); // 4cm
+ map.getTargetElement().style.width = width + 'px';
+ map.getTargetElement().style.height = height + 'px';
+ map.updateSize();
+ map.getView().setResolution(scaleResolution);
+
+}, false);
diff --git a/src/ol/control/ScaleLine.js b/src/ol/control/ScaleLine.js
index e0d7733ea3..be645ea4f9 100644
--- a/src/ol/control/ScaleLine.js
+++ b/src/ol/control/ScaleLine.js
@@ -49,6 +49,8 @@ const LEADING_DIGITS = [1, 2, 5];
* for best results. Only applies when `bar` is `true`.
* @property {boolean} [text=false] Render the text scale above of the scalebar. Only applies
* when `bar` is `true`.
+ * @property {number|undefined} [dpi=undefined] dpi of output device such as printer. Only applies
+ * when `bar` is `true`. If undefined the OGC default screen pixel size of 0.28mm will be assumed.
*/
@@ -146,6 +148,12 @@ class ScaleLine extends Control {
*/
this.scaleBarText_ = options.text || false;
+ /**
+ * @private
+ * @type {number|undefined}
+ */
+ this.dpi_ = options.dpi || undefined;
+
}
/**
@@ -176,6 +184,24 @@ class ScaleLine extends Control {
this.set(UNITS_PROP, units);
}
+ /**
+ * Specify the dpi of output device such as printer.
+ * @param {number|undefined} dpi The dpi of output device.
+ * @api
+ */
+ setDpi(dpi) {
+ this.dpi_ = dpi;
+ }
+
+ /**
+ * Set the minimum width.
+ * @param {number|undefined} minWidth The ninimum width in pixels.
+ * @api
+ */
+ setMinWidth(minWidth) {
+ this.minWidth_ = minWidth !== undefined ? minWidth : 64;
+ }
+
/**
* @private
*/
@@ -406,8 +432,12 @@ class ScaleLine extends Control {
* @return {number} The appropriate scale.
*/
getScaleForResolution() {
- const resolution = this.getMap().getView().getResolution();
- const dpi = 25.4 / 0.28;
+ const resolution = getPointResolution(
+ this.viewState_.projection,
+ this.viewState_.resolution,
+ this.viewState_.center
+ );
+ const dpi = this.dpi_ || (25.4 / 0.28);
const mpu = this.viewState_.projection.getMetersPerUnit();
const inchesPerMeter = 39.37;
return parseFloat(resolution.toString()) * mpu * inchesPerMeter * dpi;
From e6658aec1eed84b202a374eb787bdc4a1f0218e7 Mon Sep 17 00:00:00 2001
From: mike-000 <49240900+mike-000@users.noreply.github.com>
Date: Thu, 2 Apr 2020 13:47:46 +0100
Subject: [PATCH 2/8] add domtoimage
---
examples/.eslintrc | 1 +
1 file changed, 1 insertion(+)
diff --git a/examples/.eslintrc b/examples/.eslintrc
index 769357d35a..c404b2e6e6 100644
--- a/examples/.eslintrc
+++ b/examples/.eslintrc
@@ -5,6 +5,7 @@
"common": false,
"createMapboxStreetsV6Style": false,
"d3": false,
+ "domtoimage": false,
"geojsonvt": false,
"GyroNorm": false,
"jsPDF": false,
From b56ca954d51ef05ff3103cc1252dac91ee87bc51 Mon Sep 17 00:00:00 2001
From: mike-000 <49240900+mike-000@users.noreply.github.com>
Date: Thu, 2 Apr 2020 13:55:17 +0100
Subject: [PATCH 3/8] avoid classname conflict with example template
---
examples/print-to-scale.css | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/examples/print-to-scale.css b/examples/print-to-scale.css
index 383a21eb6b..7911e178ab 100644
--- a/examples/print-to-scale.css
+++ b/examples/print-to-scale.css
@@ -1,4 +1,4 @@
-.container {
+.wrapper {
max-width: 566px;
width: 100%;
height: 400px;
From ab006abf25b770e938aa99da8cd23eda2bf67477 Mon Sep 17 00:00:00 2001
From: mike-000 <49240900+mike-000@users.noreply.github.com>
Date: Thu, 2 Apr 2020 14:16:51 +0100
Subject: [PATCH 4/8] use dom-to-image-more
---
examples/print-to-scale.html | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/examples/print-to-scale.html b/examples/print-to-scale.html
index 7f4068e41a..54d88db427 100644
--- a/examples/print-to-scale.html
+++ b/examples/print-to-scale.html
@@ -6,12 +6,15 @@ docs: >
Example of printing a map to a specified scale.
The print is exported as a PDF using the jsPDF library.
Unlike the Export PDF example the on screen map is only used to set the center and rotation.
- The extent printed depends on the scale and page size.
+ The extent printed depends on the scale and page size. To print the scale bar and attributions the example uses the
+ dom-to-image-more library. Due to browser
+ limitations and restrictions Internet Explorer and Safari are not supported .
tags: "print, printing, scale, scaleline, export, pdf"
resources:
- - https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.2.61/jspdf.min.js
+ - https://unpkg.com/dom-to-image-more@2.8.0/dist/dom-to-image-more.min.js
+ - https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.5.3/jspdf.min.js
---
-