Edit

Print to scale example

print1 printing1 scale2 scaleline2 export3 pdf2

Example of printing a map to a specified scale.

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. 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.

main.js
import 'ol/ol.css';
import Map from 'ol/Map';
import TileLayer from 'ol/layer/Tile';
import View from 'ol/View';
import WMTS, {optionsFromCapabilities} from 'ol/source/WMTS';
import WMTSCapabilities from 'ol/format/WMTSCapabilities';
import proj4 from 'proj4';
import {ScaleLine, defaults as defaultControls} from 'ol/control';
import {getPointResolution, get as getProjection} from 'ol/proj';
import {register} from 'ol/proj/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);

var proj27700 = getProjection('EPSG:27700');
proj27700.setExtent([0, 0, 700000, 1300000]);

var raster = new TileLayer();

var 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) {
    var result = new WMTSCapabilities().read(text);
    var 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));
  });

var map = new Map({
  layers: [raster],
  controls: defaultControls({
    attributionOptions: {collapsible: false},
  }),
  target: 'map',
  view: new View({
    center: [373500, 436500],
    projection: proj27700,
    zoom: 7,
  }),
});

var scaleLine = new ScaleLine({bar: true, text: true, minWidth: 125});
map.addControl(scaleLine);

var 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
var exportOptions = {
  filter: function (element) {
    var className = element.className || '';
    return (
      className.indexOf('ol-control') === -1 ||
      className.indexOf('ol-scale') > -1 ||
      (className.indexOf('ol-attribution') > -1 &&
        className.indexOf('ol-uncollapsible'))
    );
  },
};

var exportButton = document.getElementById('export-pdf');

exportButton.addEventListener(
  'click',
  function () {
    exportButton.disabled = true;
    document.body.style.cursor = 'progress';

    var format = document.getElementById('format').value;
    var resolution = document.getElementById('resolution').value;
    var scale = document.getElementById('scale').value;
    var dim = dims[format];
    var width = Math.round((dim[0] * resolution) / 25.4);
    var height = Math.round((dim[1] * resolution) / 25.4);
    var viewResolution = map.getView().getResolution();
    var 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) {
          var 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
);
index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>Print to scale example</title>
    <!-- Pointer events polyfill for old browsers, see https://caniuse.com/#feat=pointer -->
    <script src="https://unpkg.com/elm-pep"></script>
    <!-- The line below is only needed for old environments like Internet Explorer and Android 4.x -->
    <script src="https://cdn.polyfill.io/v3/polyfill.min.js?features=fetch,requestAnimationFrame,Element.prototype.classList,URL,TextDecoder"></script>
    <script src="https://unpkg.com/dom-to-image-more@2.8.0/dist/dom-to-image-more.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.5.3/jspdf.min.js"></script>
    <style>
      .map {
        width: 100%;
        height:400px;
      }
      .wrapper {
        max-width: 566px;
        width: 100%;
        height: 400px;
        overflow: hidden;
      }
    </style>
  </head>
  <body>
    <div class="wrapper">
      <div id="map" class="map"></div>
    </div>
    <form class="form">
      <label for="format">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 for="resolution">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 for="scale">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>
    <script src="main.js"></script>
  </body>
</html>
package.json
{
  "name": "print-to-scale",
  "dependencies": {
    "ol": "6.5.0",
    "proj4": "2.6.3"
  },
  "devDependencies": {
    "parcel": "^2.0.0-beta.1"
  },
  "scripts": {
    "start": "parcel index.html",
    "build": "parcel build --public-url . index.html"
  }
}