1

Goal: In this link, I found an Esri vector tile (with url https://basemaps.arcgis.com/arcgis/rest/services/World_Basemap_v2/VectorTileServer/tile/{z}/{y}/{x}.pbf), and I want to load it into leaflet as a reference layer.

Inspired by this post, I managed to:

  1. First load this Esri tile into leaflet with L.vectorGrid.protobuf() with no styling at all. It is showing really haphazard styling with default blue color:

image

  1. Then, I log out this Esri protobuf layer in console and check what layers it has:

image.

  1. Then to ONLY show "Road", I create a style, where const style = {Road: {weight: 1, color: "#FF0000", opacity: 1}, ... all other layers : []}, and I load this Esri layer again with this style:
const vectorTileOptions = {vectorTileLayerStyles:style}
L.vectorGrid.protobuf(url, vectorTileOptions).addTo(map);
  1. For result, some roads do go red, and some less important geometries do go invisible. But there are still cycles (esri labels maybe?) and coastal lines that are showing in default blue (below):

image

So how exactly to render this Esri vector tile in leaflet where it only shows Roads?

1 Answer 1

1

The issue is that there are more layers than you probably hide with vectorTileLayerStyle. Some of them are only shown on certain zoom levels.

The most up-to-date code of Leaflet.VectorGrid has an option filter that takes a function to filter out unwanted layers (see line 96). Unfortunately, the most recent version on npm and unpkg.com are older than the code on GitHub.

Anyways, as a workaround, you could consider to watch the internal object _dataLayerNames of the L.VectorGrid instance and update the vectorTileLayerStyles whenever a new layer is added.

In order to accomplish this in the following example, I am using a Proxy object for the internal object and replace the internal object with the proxy object:

const map = L.map('map')
    .setView([46.801111, 8.226667], 7);

const basemap = L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
    maxZoom: 19,
    // add a link to OpenStreetMap (omitted here for shorter line width)
    attribution: '© OpenStreetMap contributors'
});
basemap.addTo(map);
// URL in this example shortened for better readability on StackOverflow
const url = 'https://basemaps.arcgis.com/.../VectorTileServer/tile/{z}/{y}/{x}.pbf';

const tileOptions = {
  rendererFactory: L.canvas.tile,
  vectorTileLayerStyles: {
    Road: {
      weight: 2,
      color: '#ff0000',
      fill: false,
    }
  },
  // see https://www.arcgis.com/home/item.html?id=30d6b8271e1849cd9c3042060001f425
  attribution: `Esri, TomTom, Garmin, FAO, 
                NOAA, USGS, and the GIS User Community`,
};

const layer = L.vectorGrid.protobuf(url, tileOptions);
function isLayerHidden(layerName) {
    return layerName != 'Road';
}

// workaround, we watch the internal _dataLayerNames object for changes
// and if there are changes, we set any layer style that is unwanted
// to an empty array
layer._dataLayerNames = new Proxy(layer._dataLayerNames, {

  set: (target, key, value) => {

    if (isLayerHidden(key)) {
      layer.options.vectorTileLayerStyles[key] = [];
    }

    target[key] = value;

    return true;

  },
  
});

map.addLayer(layer);

Caveat: This works because the _dataLayerNames object is updated before the styling is handled. That said, using internal (private) objects is usually not best practise. If the maintainers decide to change the internal structure and go ahead and releasing new versions, the code with the workaround may not work anymore.

Also, I did not do any performance testing with this workaround using a proxy object. However, I have not observed issues when testing the code in a jsfiddle.net sandbox.


Alternatively, if you are okay with hosting your own copy of Leaflet.VectorGrid, the easiest way is probably to clone the GitHub repository, use the option filter and create your own distribution files.


Another approach to get all layers could be parsing the Style file to retrieve all layers, then building the vectorTileLayerStyle option and finally using L.vectorGrid.protobuf. (I haven't had a chance to test it myself yet.)

3
  • Hi Thomas. First of all, thank you so much. I gave it a try and it did worked. Secondly, you are correct, I logged out the _dataLayerNames attributes of this layer at different zoom level, layer names are changing. When I met this problem, I was referring to the doc, which assumes we already know the layer names. But in this case, it is rendered dynamically (and the getDataLayerNames() in the doc gives me empty array). Will mark this as the solution.
    – Hang
    Commented May 30 at 4:34
  • You are welcome. I added another idea how to retrieve the layers.
    – Thomas
    Commented May 30 at 12:04
  • 1
    Thanks Thomas, actually I have tried parsing that style file before sending this post. Noise layers still exists, and I think some layers in this vector tile do not have corresponding style in the style url I found (which is also what you provided), like this: "Arts and Entertainment place". But anyway, the proxy method is working well so far.
    – Hang
    Commented May 30 at 13:00

Not the answer you're looking for? Browse other questions tagged or ask your own question.