Custom LERC Layer

Loading...

Note: Support for 3D on mobile devices may vary, view the system requirements for more information.

This sample demonstrates how to create a custom tile layer that displays elevation data requested from the ArcGIS world elevation service. Elevation changes are displayed in different shades of yellow. The brighter and stronger the color, the higher the elevation. Any elevation values higher than 6000 meters are colored with a complete yellow color.

Creating a custom tile layer

To create a custom Tilelayer you must call createSubclass() on BaseTileLayer. We'll name the custom layer LercLayer with properties for setting the minimum elevation, maximum elevation, and the url template for requesting each elevation tile.

var LercLayer = BaseTileLayer.createSubclass({
  properties: {
    urlTemplate: null,
    minElevation: 0,
    maxElevation: 4000
  },

  //override required methods
});

The fetchTile() method of LercLayer must be overriden so we can process raw elevation values and assign different shades of yellow to each pixel based on the elevation value. Once the url for a tile is returned, we can request the elevation data for that tile using esriRequest.

Since the world elevation service uses Esri LERC format, we must decode the data using the LercDecode.decode() method. This method returns a simple array of elevations. Once we have elevation values for each pixel, we can draw elevation on the canvas by assigning RGBA values to the imageData.

// fetch tiles visible in the view
fetchTile: function (level, row, col) {
  var url = this.getTileUrl(level, row, col);

  // requested encoded elevation information
  return esriRequest(url, {
    responseType: "array-buffer"
  })
  .then(function (response) {
    // create a canvas to draw the processed image
    var canvas = document.createElement("canvas");
    var context = canvas.getContext("2d");
    var width = this.tileInfo.size[0];
    var height = this.tileInfo.size[1];

    canvas.width = width;
    canvas.height = height;

    // uncompress raw elevation values in lerc format
    // into a pre-allocated array of elevation values.
    var lerc = LercDecode.decode(response.data, { noDataValue: 0 });

    // Array of elevation values width*height.
    var pixels = lerc.pixels[0];

    // stats for min, max and no data
    // values for uncompressed elevation
    var stats = lerc.statistics[0];

    // set the min and max elevation values set
    // by the layer
    var min = this.minElevation;
    var max = this.maxElevation;
    var noDataValue = stats.noDataValue;

    // Create a new blank image data object with the specified
    // dimensions.
    var imageData = context.createImageData(width, height);

    // get Uint8ClampedArray representing a one-dimensional array
    // containing the data in the RGBA order, with integer values
    // between 0 and 255 (included).
    var data = imageData.data;

    var factor = 256 / (max - min);
    var value = 0;
    var j;

    // Loop through elevation array from lerc to generate an
    // image that will be displayed.
    // `pixels` is a flat array of color values and alpha
    // [r, g, b, a, r, g, b, a, ...]
    // We need to iterate through elevation and assign color
    // and alpha values respectively.
    for (var i = 0; i < width * height; i++) {
      // map tile size is 256x256. Elevation values have a
      // tile size of 257 so we skip the last elevation
      // whenever "i" is incremented by 256 to jump to the next row.
      j = i + Math.floor(i / width);
      // read the elevation value at the given index from the elevation
      // array and multiply it by the factor. This will define
      // the shade of blue color for the pixel.
      pv = (pixels[j] - min) * factor;

      // create RGBA value for the pixel
      data[i * 4] = value;
      data[i * 4 + 1] = value;
      data[i * 4 + 2] = 0;
      data[i * 4 + 3] = pixels[i] === noDataValue ? 0 : value;
    }

    // The elevation change image and ready for display
    context.putImageData(imageData, 0, 0);

    return canvas;
  }.bind(this));
}

Sample search results

TitleSample
Loading...