Esri provides several widgets that can be added to the GUI with little coding to improve the user experience. A common use for widgets is in enabling the user to change basemaps. The BasemapToggle widget is used when you have exactly two basemaps that you’d like the user to be able to switch between. The BasemapGallery widget allows the user to choose from any number of basemap options. Let’s have a look at how these and a few other widgets are implemented.
There are four key steps in implementing this widget:
The four steps are annotated in the code sample below:
const map = new Map({ basemap: "topo-vector" // STEP 1 }); const view = new MapView({ container: "viewDiv", map: map, center: [-86.049, 38.485], zoom: 3 }); const toggle = new BasemapToggle({ view: view, // STEP 2 nextBasemap: "hybrid" // STEP 3 }); view.ui.add(toggle, "top-right"); // STEP 4
This widget provides the user a set of basemap options (with thumbnail previews) to choose from. The simplest implementation, as seen in this sample [1] involves creating a BasemapGallery object, associating it with the appropriate View, and adding it to the desired position in the UI.
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 [2], 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 Stadia.StamenTerrain option to follow along with the discussion below. (This is a tiled map developed by Stamen, [3]a cartography and visualization company based in San Francisco, and delivered in partnership with Stadia [4], another geospatial company.)
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: 'https://tiles.stadiamaps.com/tiles/stamen_terrain/{level}/{col}/{row}.png', subDomains: ["a","b","c","d"], copyright: '© <a href="https://www.stadiamaps.com/" target="_blank">Stadia Maps</a> © <a href="https://www.stamen.com/" target="_blank">Stamen Design</a> © <a href="https://openmaptiles.org/" target="_blank">OpenMapTiles</a> © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors' });
The next step is to create a new Basemap object from the WebTileLayer:
const terrain = new Basemap({ baseLayers: [terrainLayer], title: "Stamen Terrain", id: "terrain", thumbnailUrl: "http://www.arcgis.com/sharing/rest/content/items/d9118dcf7f3c4789aa66834b6114ec70/info/thumbnail/terrain.png" });
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 [5] by Jim Detwiler (@jimdetwiler [6]) on CodePen [7].
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 [8](a company based in Madrid). I want to draw your attention to a couple of important points:
The Home widget is used to provide users with the ability to return to the app’s initial viewpoint. To implement the widget, you simply need to create a new Home object and set its view property to the appropriate View. You then specify where to add the widget on the UI as seen with the earlier widgets. This sample demonstrates the Home widget [9].
The LayerList widget is used to provide users with a list of the app’s operational layers and the ability to toggle the layer visibility on/off. This is another widget that is straightforward to implement, so I'll again refer you to an Esri sample of the LayerList widget [10].
We saw the Legend widget used earlier in the course. Basic implementation of this widget is simple, only requiring you to specify the View containing the layers you’d like listed in the legend. By default, the widget will display an entry for each layer in the view, though this can be overridden. Here are a couple of examples, built on the UniqueValueRenderer example from Lesson 5:
See the Pen Uncustomized Legend Demo [11] by Jim Detwiler (@jimdetwiler [6]) on CodePen [7].
See the Pen Legend Demo [12] by Jim Detwiler (@jimdetwiler [6]) on CodePen [7].
In the first example, the Legend has only its view property set. Note that each of the two layers displayed on the map are included in the legend and that the labels for each legend entry are taken from the layer source’s name.
In the second example, the layerInfos property is used to customize the legend a bit. Only one object is defined in the layerInfos array, for the cities layer, so the counties layer is not added to the legend. A more user-friendly title is also applied to the cities layer.
Esri’s Legend widget sample [13] demonstrates a similar customization of a layer’s title. One thing I want to call your attention to is in how the reference to the layer is obtained. The layer is actually the first layer in a web map and is retrieved using the expression webmap.layers.getItemAt(0).
I’m guessing you haven’t been living under a rock all your life and are familiar with the concept of a scale bar. The ScaleBar widget has two main properties that you may want to set: style and unit. The unit property can be set to "metric", "non-metric" or "dual". The style property can be set to "ruler" or "line". Here's an example that shows a map with two ScaleBar widgets:
See the Pen ScaleBar demo [14] by Jim Detwiler (@jimdetwiler [6]) on CodePen [7].
The only difference I can see between the two styles is that the ruler style has the labels appear above the line while the line style has them below the line. The Esri ScaleBar sample [15] shows that setting the unit to dual will produce a line with the metric label on top and the non-metric label on bottom.
Links
[1] https://developers.arcgis.com/javascript/latest/sample-code/widgets-basemapgallery/
[2] http://leaflet-extras.github.io/leaflet-providers/preview/index.html
[3] https://stamen.com/
[4] https://stadiamaps.com
[5] https://codepen.io/jimdetwiler/pen/YerNNg
[6] https://codepen.io/jimdetwiler
[7] https://codepen.io
[8] https://carto.com/
[9] http://developers.arcgis.com/javascript/latest/sample-code/widgets-home/index.html
[10] http://developers.arcgis.com/javascript/latest/sample-code/widgets-layerlist/index.html
[11] https://codepen.io/jimdetwiler/pen/eVGgEy
[12] https://codepen.io/jimdetwiler/pen/wyrgPG
[13] http://developers.arcgis.com/javascript/latest/sample-code/widgets-legend/index.html
[14] https://codepen.io/jimdetwiler/pen/xYXgYy
[15] https://developers.arcgis.com/javascript/latest/sample-code/sandbox/index.html?sample=widgets-scalebar