Merge pull request #10537 from mike-000/patch-3
Correct resolution used for scale bar and add dpi option
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
"common": false,
|
||||
"createMapboxStreetsV6Style": false,
|
||||
"d3": false,
|
||||
"domtoimage": false,
|
||||
"geojsonvt": false,
|
||||
"GyroNorm": false,
|
||||
"jsPDF": false,
|
||||
|
||||
6
examples/print-to-scale.css
Normal file
6
examples/print-to-scale.css
Normal file
@@ -0,0 +1,6 @@
|
||||
.wrapper {
|
||||
max-width: 566px;
|
||||
width: 100%;
|
||||
height: 400px;
|
||||
overflow: hidden;
|
||||
}
|
||||
47
examples/print-to-scale.html
Normal file
47
examples/print-to-scale.html
Normal file
@@ -0,0 +1,47 @@
|
||||
---
|
||||
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 <a href="https://github.com/MrRio/jsPDF" target="_blank">jsPDF</a> library.
|
||||
Unlike the <a href="export-pdf.html">Export PDF example</a> the on screen map is only used to set the center and rotation.
|
||||
The extent printed depends on the scale and page size. To print the scale bar and attributions the example uses the
|
||||
<a href="https://github.com/1904labs/dom-to-image-more" target="_blank">dom-to-image-more</a> library. Due to browser
|
||||
limitations and restrictions <b>Internet Explorer and Safari are not supported</b>.
|
||||
tags: "print, printing, scale, scaleline, export, pdf"
|
||||
resources:
|
||||
- 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
|
||||
---
|
||||
<div class="wrapper">
|
||||
<div id="map" class="map"></div>
|
||||
</div>
|
||||
<form class="form">
|
||||
<label>Page size </label>
|
||||
<select id="format">
|
||||
<option value="a0">A0 (slow)</option>
|
||||
<option value="a1">A1</option>
|
||||
<option value="a2">A2</option>
|
||||
<option value="a3">A3</option>
|
||||
<option value="a4" selected>A4</option>
|
||||
<option value="a5">A5 (fast)</option>
|
||||
</select>
|
||||
<label>Resolution </label>
|
||||
<select id="resolution">
|
||||
<option value="72">72 dpi (fast)</option>
|
||||
<option value="150">150 dpi</option>
|
||||
<option value="200" selected>200 dpi</option>
|
||||
<option value="300">300 dpi (slow)</option>
|
||||
</select>
|
||||
<label>Scale </label>
|
||||
<select id="scale">
|
||||
<option value="500">1:500000</option>
|
||||
<option value="250" selected>1:250000</option>
|
||||
<option value="100">1:100000</option>
|
||||
<option value="50">1:50000</option>
|
||||
<option value="25">1:25000</option>
|
||||
<option value="10">1:10000</option>
|
||||
</select>
|
||||
</form>
|
||||
<button id="export-pdf">Export PDF</button>
|
||||
128
examples/print-to-scale.js
Normal file
128
examples/print-to-scale.js
Normal file
@@ -0,0 +1,128 @@
|
||||
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';
|
||||
|
||||
|
||||
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, minWidth: 125});
|
||||
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;
|
||||
domtoimage.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();
|
||||
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);
|
||||
map.getTargetElement().style.width = width + 'px';
|
||||
map.getTargetElement().style.height = height + 'px';
|
||||
map.updateSize();
|
||||
map.getView().setResolution(scaleResolution);
|
||||
|
||||
}, false);
|
||||
@@ -34,11 +34,18 @@ export const Units = {
|
||||
*/
|
||||
const LEADING_DIGITS = [1, 2, 5];
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {number}
|
||||
*/
|
||||
const DEFAULT_DPI = 25.4 / 0.28;
|
||||
|
||||
|
||||
/**
|
||||
* @typedef {Object} Options
|
||||
* @property {string} [className='ol-scale-line'] CSS Class name.
|
||||
* @property {number} [minWidth=64] Minimum width in pixels.
|
||||
* @property {number} [minWidth=64] Minimum width in pixels at the OGC default dpi. The width will be
|
||||
* adjusted to match the dpi used.
|
||||
* @property {function(import("../MapEvent.js").default)} [render] Function called when the control
|
||||
* should be re-rendered. This is called in a `requestAnimationFrame` callback.
|
||||
* @property {HTMLElement|string} [target] Specify a target if you want the control
|
||||
@@ -49,6 +56,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 +155,12 @@ class ScaleLine extends Control {
|
||||
*/
|
||||
this.scaleBarText_ = options.text || false;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {number|undefined}
|
||||
*/
|
||||
this.dpi_ = options.dpi || undefined;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -176,6 +191,15 @@ 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
@@ -199,7 +223,9 @@ class ScaleLine extends Control {
|
||||
let pointResolution =
|
||||
getPointResolution(projection, viewState.resolution, center, pointResolutionUnits);
|
||||
|
||||
let nominalCount = this.minWidth_ * pointResolution;
|
||||
const minWidth = this.minWidth_ * (this.dpi_ || DEFAULT_DPI) / DEFAULT_DPI;
|
||||
|
||||
let nominalCount = minWidth * pointResolution;
|
||||
let suffix = '';
|
||||
if (units == Units.DEGREES) {
|
||||
const metersPerDegree = METERS_PER_UNIT[ProjUnits.DEGREES];
|
||||
@@ -256,7 +282,7 @@ class ScaleLine extends Control {
|
||||
}
|
||||
|
||||
let i = 3 * Math.floor(
|
||||
Math.log(this.minWidth_ * pointResolution) / Math.log(10));
|
||||
Math.log(minWidth * pointResolution) / Math.log(10));
|
||||
let count, width, decimalCount;
|
||||
while (true) {
|
||||
decimalCount = Math.floor(i / 3);
|
||||
@@ -267,7 +293,7 @@ class ScaleLine extends Control {
|
||||
this.element.style.display = 'none';
|
||||
this.renderedVisible_ = false;
|
||||
return;
|
||||
} else if (width >= this.minWidth_) {
|
||||
} else if (width >= minWidth) {
|
||||
break;
|
||||
}
|
||||
++i;
|
||||
@@ -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_ || DEFAULT_DPI;
|
||||
const mpu = this.viewState_.projection.getMetersPerUnit();
|
||||
const inchesPerMeter = 39.37;
|
||||
return parseFloat(resolution.toString()) * mpu * inchesPerMeter * dpi;
|
||||
|
||||
Reference in New Issue
Block a user