Photo Credit: NASA 2009

Stitching together map tile fragments

Matt Gardner
NYC Planning Tech
Published in
3 min readDec 3, 2018

--

Mapbox Vector Tiles are awesome — and conceptually fascinating. They allow us to create lightning-fast maps with enormous amounts of data while providing for dynamic styling and interactivity.

But, you know what they say: with great power, comes …mo’ problems…? Um, anyways, here’s a little gotcha with mapping vector tiles:

Exhibit A: Feature Fragments — a highlighted Census Tract is fragmented along an invisible tile grid

Mapbox Vector Tiles, or MVT, fragment vector data across grid and zoom level, meaning information is only provided precisely where and when it’s needed. So, it’s not necessary to load all the data for administrative boundaries — the MVT specification provides only the fragments necessary for a given zoom level, latitude, and longitude.

The problem here, however, is with client-side interactivity, specifically in the implementation of a common UX pattern for highlighting geometries as a mouse pointer moves across it:

Exhibit B: Shattered Dreams?

Why is this happening? As our mouse pointer moves across the screen, MapboxGL continuously fires events that provide information about the hovered geometry. Intuitively, one would simply add this geometric data to the map to provide a “styled” geometry for the hovered boundary, but as Exhibit B demonstrates, this data is fragmented, as we should expect.

In comes query source features, which we run to pick up all relevant geometric data. This step allows us to gather not just geometric information on a single tile — which may be split across several tiles — but also all tiles loaded presently on the map:

Exhibit C: On the Mend!

Using browser-side JavaScript libraries for “stitching” (unioning) these fragments together, we’re able to provide a more accurate representation of a highlighted boundary.

Stitching, however, is expensive, and requires some checks. In our implementation, we run the geometric union in a cancellable “task”, a pattern for managing asynchrony. The task lightly debounces the operation to prevent the machine from being overloaded. It also prevents the union from occurring if there are way too many fragments to union. Finally, once it passes these checks, the task returns the union of fragments and overwrites the initial feature fragment with a more complete geometry. There is still the immediate feedback of a hovered feature fragment which is then backfilled once the stitching procedure finishes.

Note, of course, that this data is never reliable, even after stitching. It’s possible that there is some geometric data missing from the map view. This means that this map data should only ever be relied on for presentation concerns rather than data itself.

After coming up with this solution, we were able to bake it in to our shared mapping library and easily update most of our projects with the new feature.

How have you approached this problem? Let us know in the comments below!

--

--