Custom TileLayer

Introduction to layer extensibility

The ArcGIS Maps SDK for JavaScript provides a number of predefined layers. These layers retrieve images or data from servers and display them in the view.

In addition to this sample, the following samples demonstrate the basic fundamentals of creating custom layers.

Creating custom layers is useful when you need to:

  • display data from a source not exclusively supported in the ArcGIS Maps SDK for JavaScript
  • preprocess data before it is displayed in the view (this could be because the service returns binary data, which needs to be processed to generate an image)
  • create custom visualizations not explicitly supported in the API
  • display synthesized data such as an exaggerated elevation

Writing a custom layer enables you to support the drawing of new data formats. Before you get started on creating custom layers, it is helpful to have some familiarity with the following topics:

Create a custom tile layer

This sample demonstrates how to create a custom TileLayer from OpenTopoMap's style. Tile layers are composed of images, such as satellite imagery, which are square tiles stitched together in columns and rows, giving the layer the appearance that it is one continuous image. These layers have several levels of detail (LOD) that permit users to zoom to any region of the map and load more tiles that depict features in higher resolution at larger map scales.

To create a custom tile layer, you must call the createSubclass() method on the BaseTileLayer class. We'll name the custom layer TintLayer.

Since this layer needs to know where to access predefined tiles, we will create a urlTemplate property. The application will provide the urlTemplate value for the layer, and the layer will fetch tiles from the generated URL. The difference blending operation is then applied to the OpenTopoMap tiles prior to displaying each image. We will create a tint property on this layer so that the application can specify a color, which will be used in the blending operation.

Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const TintLayer = BaseTileLayer.createSubclass({
  // set up the properties specific to this layer
  properties: {
    // url to the tiles, provided by the application
    urlTemplate: null,
    // tint color that will be used to change
    // OpenTopoMap's tiles
    // value provided by the application
    tint: {
      value: null,
      type: Color
    }
  }

  // override necessary methods here
});

Users will be able to change the tint color at runtime by picking a color from the Calcite color picker component. The reactiveUtils.watch method watches the tint layer's tint property and will respond to a change by calling the refresh() method.

Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
9
10
11
12
13
// watch the tint property and refresh the layer if the tint values
// change at runtime.
const TintLayer = BaseTileLayer.createSubclass({
  ...
  initialize() {
    reactiveUtils.watch(
      ()=> this.tint,
      ()=>{
      this.refresh();
    });
  },
  ...
});

You extend BaseTileLayer two different ways:

  • Request tiles as they are predefined from a data source
  • Images or data need to be pre-processed prior to display in the view

Request tiles as they are predefined from a data source

To request images as they are predefined from a data source, overwrite the getTileUrl() method so it returns the URL for the requested tile at a given level, row, and column.

Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const MyCustomTileLayer = BaseTileLayer.createSubclass({
  // properties of the custom tile layer
  properties: {
    urlTemplate: null
  },

  // override getTileUrl()
  // generate the tile url for a given level, row and column
  getTileUrl: function(level, row, col) {
    return this.urlTemplate
      .replace("{z}", level)
      .replace("{x}", col)
      .replace("{y}", row);
  }
});

Images or data need preprocessing prior to display in the view

If data or tiles need to be preprocessed prior to display, then override the fetchTile() method. This sample uses this approach by fetching OpenTopoMap's tiles and applying the difference blending operation with a given color to the canvas. Once the image and color are blended, the final result will display in the view.

Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// This method fetches tiles for the specified level and size.
// Override this method to process the data returned from the server.
fetchTile: function (level, row, col, options) {

  // call getTileUrl() method to construct the URL to tiles
  // for a given level, row and col provided by the LayerView
  const url = this.getTileUrl(level, row, col);

  // request for tiles based on the generated url
  // the signal option ensures that obsolete requests are aborted
  return esriRequest(url, {
    responseType: "image",
    signal: options && options.signal
  })
    .then(function (response) {
      // when esri request resolves successfully
      // get the image from the response
      const image = response.data;
      const width = this.tileInfo.size[0];
      const height = this.tileInfo.size[0];

      // create a canvas with 2D rendering context
      const canvas = document.createElement("canvas");
      const context = canvas.getContext("2d");
      canvas.width = width;
      canvas.height = height;

      // Apply the tint color provided by
      // by the application to the canvas
      if (this.tint) {
        // Get a CSS color string in rgba form
        // representing the tint Color instance.
        context.fillStyle = this.tint.toCss();
        context.fillRect(0, 0, width, height);

        // Applies "difference" blending operation between canvas
        // and OpenTopoMap tiles. Difference blending operation subtracts
        // the bottom layer (canvas) from the top layer (tiles) or the
        // other way round to always get a positive value.
        context.globalCompositeOperation = "difference";
      }

      // Draw the blended image onto the canvas.
      context.drawImage(image, 0, 0, width, height);

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

Using the custom tile layer in JavaScript application

Once the custom layer is created, you can add it to the layers of the Map and add the map to a MapView or SceneView instance.

Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Create a new instance of the TintLayer and set its properties.
const openTopoMapTileLayer = new TintLayer({
  urlTemplate: "https://tile.opentopomap.org/{z}/{x}/{y}.png",
  tint: "#132178", // blue color
  title: "OpenTopoMap"
});

const map = new Map({
  layers: [openTopoMapTileLayer]
});

const view = new SceneView({
  container: "viewDiv",
  map: map,
  center: [0, 30],
  zoom: 3
});

Your browser is no longer supported. Please upgrade your browser for the best experience. See our browser deprecation post for more details.