Tuesday, October 24, 2017

Leaflet-Mapbox-GL HOWTO: Highlighting an area

My series on Mapbox GL API combined with Leaflet using leaflet-mapbox-gl.js continues.

We have been working within a function called selectRegionByName() which would switch between choropleth view and borders-only view by way of filters. Last week we added waterways and river gauges to it as well, again using filters.

This week's icing on the cake: we want to highlight the selected region as well. The black borders are showing and the choropleth is suppressed, but can we give this area a good thick orange border to make it pop?


The Easy Way: Filtering a Dedicated Layer


No surprise here: the easiest mechanism would be to add a new layer to your Style, and filter it to match the region. If we stack the highlight layer above the black outline layer and give the stroke an opacity of 1, this would effectively hide the black border of this region in favor of the orange border.

  • Go into Mapbox Studio and add a new layer called regionhighlight.
    Use the hydrology regions as the Tileset.
    Select a line type rendering, give it a good thick stroke and make it orange.
  • Set a filter on the layer to match `region == Nonexistent` so that it's not in fact matching anything at all.
  • In client-side code, follow what we did the last few postings to add a new filter for regionhighlight matching the selected region name.
The resulting additions to selectRegionByName() would look a lot like this:

// highlight overlay on: filter to match this one region
MBOVERLAY._glMap.setFilter("highlightregion", ["==", 'name', name]);

// highlight overlay off: filter to match no region ever
MBOVERLAY._glMap.setFilter("highlightregion", ["==", 'name', "Nonexistent"]);

The Harder Way: Creating a New Layer Client-Side


Back when I implemented the highlighting, I hadn't quite wrapped my head around filtering and hadn't come up with the easy method above. Instead, I used a technique described in Mapbox's documentation which uses a Tileset as a data source and adds virtual layers to the client-side map.

First off, define a Source connected to your Tileset. You will need the Tileset's Mapbox URL and the name of it as it appears in your Tilesets page.
// define a new Source
// url is the Mapbox URL of the regions Tileset
MBOVERLAY._glMap.addSource('regions', {
    "type": "vector",
    "url": "mapbox://yourusername.abcdef1234",
});

// define a line layer using the above Source
// source is the Source name you defined above
// source-layer is the Mapbox-mangled name of that Tileset
MBOVERLAY._glMap.addLayer({
    "id": "highlightregion",
    "source": "regions",
    "source-layer": "HydroRegions-abcd1234",
    "type": "line",
    "paint": {
        "line-width": 4,
        "line-color": "orange",
    },
    "filter": ["==", 'name', "Nonexistent"],
});
Now, to activate that virtual layer, you would apply a filter:
// highlight overlay on
// filter to match this one region
MBOVERLAY._glMap.setFilter("highlightregion", ["==", 'name', name]);

// highlight overlay off
// filter to match no region ever
MBOVERLAY._glMap.setFilter("highlightregion", ["==", 'name', "Nonexistent"]);
This technique is a lot more complicated, and since you need to use filtering anyway in order to operate it, the first technique I described (a dedicated highlighting layer) gets the job done with less work. But if you are using someone else's Style and editing it is not an option, this technique would allow you to accomplish much the same effect.


Stroke vs Fill

One last note about highlighting: stroke versus fill. Mapbox layers are either polygons (a fill, with no stroke) or else lines (a boundary but no fill). If you want to highlight with both, you will need to define two layers, then set the filtering on both of them.

This goes for both doing highlights within the Mapbox Studio Style, or using the addLayer() technique.


No comments:

Post a Comment