Custom TileLayer

Explore in the sandboxView live

Introduction to layer extensibility

ArcGIS API 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 API 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 Stamen's toner black and white tiles. 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 Stamen 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.

var 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
    // stamen's black and white tiles
    // value provided by the application
    tint: {
      value: null,
      type: Color

  // override necessary methods here

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.

var 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 Stamen's toner 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.

// 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
  var 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
      var image =;
      var width = this.tileInfo.size[0];
      var height = this.tileInfo.size[0];

      // create a canvas with 2D rendering context
      var canvas = document.createElement("canvas");
      var 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 steman 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;

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.

// Create a new instance of the TintLayer and set its properties.
var stamenTonerLayer = new TintLayer({
  urlTemplate: "{z}/{x}/{y}.png",
  tint: "#71DE6E", // blue color
  title: "Stamen Toner"

var map = new Map({
  layers: [stamenTonerLayer]

var view = new SceneView({
  container: "viewDiv",
  map: map,
  center: [0, 30],
  zoom: 3