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

const key = 'Get your own API key at https://www.maptiler.com/cloud/';
const 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>';

const 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,
  }),
});

const 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: '',
  }),
});

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

const control = document.getElementById('opacity');
const 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);

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

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

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

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

const showElevations = function (evt) {
  if (evt.dragging) {
    return;
  }
  map1.forEachLayerAtPixel(
    evt.pixel,
    function (layer, pixel) {
      const 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) {
      const 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>
    <meta charset="UTF-8">
    <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>
    <!-- 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,Number.isInteger"></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.6.1"
  },
  "devDependencies": {
    "parcel": "^2.0.0-beta.1"
  },
  "scripts": {
    "start": "parcel index.html",
    "build": "parcel build --public-url . index.html"
  }
}