Edit

Disable Image Smoothing

disableimagesmoothing1 xyz8 maptiler24 reprojection6

Smoothing Disabled

Uncorrected Comparison

Example of disabling image smoothing

Example of disabling image smoothing when using raster DEM (digital elevation model) data. The imageSmoothing: false setting is used to disable canvas image smoothing during reprojection and rendering. Elevation data is calculated from the pixel value returned by forEachLayerAtPixel. For comparison a second map with smoothing enabled returns inaccuate elevations which are very noticeable close to 3107 meters due to how elevation is calculated from the pixel value.

main.js
import 'ol/ol.css';
import Map from 'ol/Map';
import TileLayer from 'ol/layer/Tile';
import View from 'ol/View';
import XYZ from 'ol/source/XYZ';

var key = 'Get your own API key at https://www.maptiler.com/cloud/';
var attributions =
  '<a href="https://www.maptiler.com/copyright/" target="_blank">&copy; MapTiler</a> ' +
  '<a href="https://www.openstreetmap.org/copyright" target="_blank">&copy; OpenStreetMap contributors</a>';

var disabledLayer = new TileLayer({
  // specify className so forEachLayerAtPixel can distinguish layers
  className: 'ol-layer-dem',
  source: new XYZ({
    attributions: attributions,
    url:
      'https://api.maptiler.com/tiles/terrain-rgb/{z}/{x}/{y}.png?key=' + key,
    maxZoom: 10,
    crossOrigin: '',
    imageSmoothing: false,
  }),
});

var imagery = new TileLayer({
  className: 'ol-layer-imagery',
  source: new XYZ({
    attributions: attributions,
    url: 'https://api.maptiler.com/tiles/satellite/{z}/{x}/{y}.jpg?key=' + key,
    maxZoom: 20,
    crossOrigin: '',
  }),
});

var enabledLayer = new TileLayer({
  source: new XYZ({
    attributions: attributions,
    url:
      'https://api.maptiler.com/tiles/terrain-rgb/{z}/{x}/{y}.png?key=' + key,
    maxZoom: 10,
    crossOrigin: '',
  }),
});

imagery.on('prerender', function (evt) {
  // use opaque background to conceal DEM while fully opaque imagery renders
  if (imagery.getOpacity() === 1) {
    evt.context.fillStyle = 'white';
    evt.context.fillRect(
      0,
      0,
      evt.context.canvas.width,
      evt.context.canvas.height
    );
  }
});

var control = document.getElementById('opacity');
var output = document.getElementById('output');
control.addEventListener('input', function () {
  output.innerText = control.value;
  imagery.setOpacity(control.value / 100);
});
output.innerText = control.value;
imagery.setOpacity(control.value / 100);

var view = new View({
  center: [6.893, 45.8295],
  zoom: 16,
  projection: 'EPSG:4326',
});

var map1 = new Map({
  target: 'map1',
  layers: [disabledLayer, imagery],
  view: view,
});

var map2 = new Map({
  target: 'map2',
  layers: [enabledLayer],
  view: view,
});

var info1 = document.getElementById('info1');
var info2 = document.getElementById('info2');

var showElevations = function (evt) {
  if (evt.dragging) {
    return;
  }
  map1.forEachLayerAtPixel(
    evt.pixel,
    function (layer, pixel) {
      var height =
        -10000 + (pixel[0] * 256 * 256 + pixel[1] * 256 + pixel[2]) * 0.1;
      info1.innerText = height.toFixed(1);
    },
    {
      layerFilter: function (layer) {
        return layer === disabledLayer;
      },
    }
  );
  map2.forEachLayerAtPixel(
    evt.pixel,
    function (layer, pixel) {
      var height =
        -10000 + (pixel[0] * 256 * 256 + pixel[1] * 256 + pixel[2]) * 0.1;
      info2.innerText = height.toFixed(1);
    },
    {
      layerFilter: function (layer) {
        return layer === enabledLayer;
      },
    }
  );
};

map1.on('pointermove', showElevations);
map2.on('pointermove', showElevations);
index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Disable Image Smoothing</title>
    <!-- Pointer events polyfill for old browsers, see https://caniuse.com/#feat=pointer -->
    <script src="https://unpkg.com/elm-pep"></script>
    <style>
      .map {
        width: 100%;
        height:400px;
      }
      @media (min-width: 800px) {
        .wrapper {
          display: flex;
        }
        .half {
          padding: 0 10px;
          width: 50%;
          float: left;
        }
      }
      #opacity {
        display: inline-block;
        width: 150px;
        vertical-align: text-bottom;
      }
    </style>
  </head>
  <body>
    <div class="wrapper">
      <div class="half">
        <h4>Smoothing Disabled</h4>
        <div id="map1" class="map"></div>
        <div>
          <label>
            Elevation
            <span id="info1">0.0</span> meters
          </label>
        </div>
        <div>
          <label>
            Imagery opacity
            <input id="opacity" type="range" min="0" max="100" value="80" />
            <span id="output"></span> %
          </label>
        </div>
      </div>
      <div class="half">
        <h4>Uncorrected Comparison</h4>
        <div id="map2" class="map"></div>
        <div>
          <label>
            Elevation
            <span id="info2">0.0</span> meters
          </label>
        </div>
      </div>
    </div>
    <script src="main.js"></script>
  </body>
</html>
package.json
{
  "name": "disable-image-smoothing",
  "dependencies": {
    "ol": "6.4.3"
  },
  "devDependencies": {
    "parcel": "1.11.0"
  },
  "scripts": {
    "start": "parcel index.html",
    "build": "parcel build --experimental-scope-hoisting --public-url . index.html"
  }
}