GEOG 863:
Web Application Development for Geospatial Professionals

8.1.3 Custom Basemaps


8.1.3 Custom Basemaps

Where the implementation of these widgets can become more complicated is if you want to offer non-Esri basemap options. First, have a look at this app, which provides a preview of many (mostly open-source) basemaps.

The app lets you preview a basemap by selecting it from the list of mini-maps on the right. Be aware that you can change the map extent to something other than Europe, if desired. After selecting a basemap, you’ll see the JS code that would be used to implement it in Leaflet (an open-source JS API). The Leaflet syntax is not quite the same as Esri’s, but we’ll be able to work out the differences.

Choose the stamen.terrain option to follow along with the discussion below. (Stamen is a cartography and visualization company based in San Francisco.)

Using this basemap in Leaflet involves creating a TileLayer object by specifying the URL of the server that hosts the map tiles, along with some optional parameters such as attribution info and subdomains. (Subdomains are often set up on the tile server to speed up the delivery of tiles to clients.) Note that the server URL contains the letters s, x, y and z in braces. These are placeholders for the subdomain, x coordinate, y coordinate and zoom level, respectively. The TileLayer class is programmed to insert the appropriate values for these placeholders to retrieve the necessary tiles.

In an Esri context, we instead create a WebTileLayer and assign the server URL to the urlTemplate property. Esri’s placeholders are a bit different: subDomain, level, col and row. The subdomains property that was set using a string of characters in Leaflet is the subDomains property set using an array of strings in Esri. Finally, the attribution property in Leaflet is instead the copyright property in Esri. If we wanted to create an Esri WebTileLayer based on the Stamen Terrain basemap, the code would look like this:

const terrainLayer = new WebTileLayer({
  urlTemplate: 'http:// stamen-tiles-{subDomain}{level}/{col}/{row}.png',
  subDomains: ["a","b","c","d"],
  copyright: 'Map tiles by <a href="">Stamen Design</a>, <a
href="">CC BY 3.0</a> &mdash; Map data &copy; <a 

The next step is to create a new Basemap object from the WebTileLayer:

const terrain = new Basemap({
  baseLayers: [terrainLayer],
  title: "Stamen Terrain",
  id: "terrain",

The thumbnailUrl property provides control over the preview image that appears for the basemap option when displayed by one of the basemap widgets. You’re welcome to create this thumbnail yourself based on some desired extent. (If on Windows, you can print the screen to the Windows clipboard and use an image editing app like Paint to crop and re-size. The image should be sized to 200x133 pixels.) If you don’t want to go to that trouble, you might have luck searching for the basemap in ArcGIS Online and copying the URL of the thumbnail that you find there. That is what I did in this case.

Have a look at the source code for the example below:

See the Pen Custom BasemapGallery by Jim Detwiler (@jimdetwiler) on CodePen.

In this app, I’ve implemented the BasemapGallery widget with two non-Esri basemaps: Stamen Terrain, discussed above, and the Positron basemap developed by Carto (a company based in Madrid). I want to draw your attention to a couple of important points:

  • after the creation of the two Basemap objects, a LocalBasemapsSource object is created and used to set the BasemapGallery’s source property,
  • after the source property is set, the activeBasemap property is set to one of the basemaps.