3

I am trying to highlight features in a tileset-layer based on this tutorial: https://jsfiddle.net/oyjLzfq4/1/

The grey layer is stored as a tileset in Mapbox studio. The feature under the mouse cursor is transferred to a dynamic layer in the frontend code. This works rather well, but some polygons appear to be split by Mapbox. No such split is visible when opening the same layer in QGIS.

The screenshot below shows the problem. It might be a coincidence, but the label (seperate layer in Mapbox studio) appears to be also split along this line.

It seems that this splitting could be processing or rendering artifact. Maybe the feature is not copied completly, but just the part of the feature that is on the current rendering tile. Is there any way this can be prevented?

Mapbox GL Js polygon splitting error

I use this method to create the hover effect, because filtering the polygons is a lot slower and in this case unusably slow. See the discussion here: https://github.com/mapbox/mapbox-gl-js/issues/2492

Edit

Currently my best solution to the problem is to load the part of the feature on the current tile and let the filter on the hover-layer run at the same time. That means that part of the feature is highlighted right away and the full feature after about 3 seconds.

map.addSource("dynamic_source", {
    "type": "geojson",
    "data": {"type": "FeatureCollection", "features": []}
});

map.addLayer({
    "id": "dynamic_layer",
    "type": "fill",
    "source": "dynamic_source",
    "layout": {},
    "paint": {
        "fill-color": "#f00",
        "fill-opacity": 1
    }
});

map.on("mousemove", function(event) {
    var features = this.map.queryRenderedFeatures(event.point, {
        layers: ["MyLayer"]
    });

    // Instant hover, but only part of feature on current tile
    map.getSource("dynamic_source").setData({
        "type": "FeatureCollection",
        "features": features.map(function (f) {
            return {"type": "Feature", "geometry": f.geometry};
        })
    });

    // 3s delayed hover, but complete feature
    if (features.length) {
        map.setFilter("MyLayer", ["==", "name", features[0].properties.name]);
    } else {
        map.setFilter("MyLayer", ["==", "name", ""]);
    }
});

This forked example from the Github issue shows that this example also sometimes leads to incomplete hover higlighting at tile boundaries: https://jsfiddle.net/t2sks4pz/

incomplete hover in geojson example

3
  • That's how vector tiles work. Vector data are split at tile boundaries usually by applying some threshold mapbox.com/vector-tiles/specification.
    – user30184
    Commented Mar 20, 2017 at 10:00
  • Is there a way to make the whole canvas one tile? Otherwise I am back to using filters for hover, and those are just too slow at the moment. Commented Mar 20, 2017 at 10:25
  • It is up to who provides vector tiles. I suppose that is very uncommon. The documentation above suggest to make union on client side That being said, one could use the id field in the protobuf to store information necessary for reconstructing polygons.
    – user30184
    Commented Mar 20, 2017 at 10:45

2 Answers 2

1

The idea of vector tiles is to clip geometries to predefined grid. With points that should make no difficulties but lines and polygons which do not fit inside one tile the geometries are cut at the tile boundaries, possibly with some margin. This can make some difficulties later with labeling etc. as you have noticed. From the MapBox vector tiles specification page https://www.mapbox.com/vector-tiles/specification/#clipping

Clipping

The specification does not explain how geographic data should be clipped between vector tiles since clipping, like simplification, can be executed in many ways. Mapbox specifically clips features at a buffer around the tile (see the encoding example above). Any geometry within this buffer is assumed to carry over to another tile. This is up for consideration for a future release.

Note: encoded geometry in vector tiles can actually extend beyond the bound of the tile. This means features are not required to be clipped.

A common question, when it comes to clipping is “how do renderers know which lines to connect for clipped geometry?”. This is the very reason Mapbox adds a buffer to vector tiles and clipped geometry. When it is time to render the canvas is set to the exact tile size, which sets the edges outside of the visual frame, thus the tiles all line up. Therefore, there is no need to know which nodes are part of others for rendering purposes. That being said, one could use the id field in the protobuf to store information necessary for reconstructing polygons.

1
  • Thanks for the detailed explanation. I am still trying to make a workaround, where I get the rest of the polygon. But maybe I have to go back to filtering the tileset layer. Commented Mar 20, 2017 at 11:53
0

I know this is an old question, but wanted to mention that Mapbox now supports event listeners with layer filters. I would expect better performance if you did this:

map.on("mousemove", "MyLayer", function(event) {
  var features = this.map.queryRenderedFeatures(event.point, {
      layers: ["MyLayer"]
  });

  // Instant hover, but only part of feature on current tile
  map.getSource("dynamic_source").setData({
      "type": "FeatureCollection",
      "features": features.map(function (f) {
          return {"type": "Feature", "geometry": f.geometry};
      })
  });

  // 3s delayed hover, but complete feature
  if (features.length) {
      map.setFilter("MyLayer", ["==", "name", features[0].properties.name]);
  } else {
      map.setFilter("MyLayer", ["==", "name", ""]);
  }
});

Also, rather than setting your filter or map data on hover events, you can set a style that differentiates based on a feature state, and set that state dynamically on hover event. This should be more performant than (re)setting the entire source data. Mapbox example.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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