We’ve written before about how pairing vector tiles with Mapbox GL can produce beautiful, complex maps. In recent projects, we’ve started using vector tiles to power the contextual charts and tables that we’d previously built a separate API to support, drastically lowering our deployment and maintenance costs, while minimizing the failure points in our infrastructure.

For developers who maintain a separate API on pages that load vector tiles, re-using data from those tiles in elements besides the map can simplify deployment, lower costs, and prevent duplicate work. In this post I’ll walk through how to do that.

Our use-case

Development Seed’s task was to build an interactive website to allow exploration of a geographic dataset over time, similar in style and layout to our India Lights project. We had quarterly measurements over ten years for most of America’s 32,000 ZIP Code Tabulation Areas (ZCTAs), and wanted to use a line chart below the map to show temporal change.

As you zoomed and panned throughout the map, we wanted to update which ZCTAs we showed in the graph; if you zoomed to the Los Angeles area, then the graph should show lines for 90210 and other 90___ ZCTAs, thus allowing you to spot temporal trends wherever you navigated.

India Lights retrieves its chart data using a traditional REST API. Initially we considered a similar strategy, which would have meant building an API that recognized queries like ?state=ca, ?countyFIPS=06037, or ?bounds=[[33.90,-118.51],[34.17,-118.09]].

However, our map’s vector tiles already contained all quarterly measurements for each ZCTA. Instead of building that API from scratch, couldn’t we re-use that data?

Turns out, Mapbox GL JS facilitates this by giving us a method to query data on features that are currently visible, returing GeoJSON. With this GeoJSON in hand, it was simple to transform the properties of each feature into the time-series needed for the line chart.

This strategy was effective and performant, and saved us both development and maintenance time. And there was now no server-upkeep cost to the client, since our vector tiles fit on free-tier Mapbox hosting.

Clearly, this solution doesn’t cover all use-cases. Many map-centric visualizations require charting or inspecting values that aren’t in the map’s viewport. Furthermore, having an API can make a platform more accessible and interoperable; for a larger project, even if you could get away with Mapbox GL JS queries at first, it may save time in the long run to design and build a database and API from the beginning.

Technical Q&A

How do I get my geographic data into vector tiles?

If you have ESRI shapefiles, you’ll first have to convert them into GeoJSON format; this can be done easily using [ogr2ogr](http://www.gdal.org/ogr2ogr.html), part of GDAL:

`ogr2ogr -f GeoJSON -t_srs crs:84 "${YOUR_FILENAME}.geojson" "${YOUR_FILENAME}.shp"`

With your data in GeoJSON format, Tippecanoe can create an MBTiles file that's ready to upload for Mapbox hosting:

`tippecanoe -o "${YOUR_FILENAME}.mbtiles" "${YOUR_FILENAME}.geojson"
mapbox upload "${YOUR_FILENAME}.mbtiles"`

What are these Mapbox GL JS queries, exactly?

There are two query methods on Mapbox GL JS’s [Map](https://www.mapbox.com/mapbox-gl-js/api/#map) object. These allow access to the GeoJSON features of visible vector tiles.

A common use-case is calling queryRenderedFeatures with the coordinates of a user's mouse, to retrieve GeoJSON features for the points, lines, and polygons at that location, as seen in this Mapbox example:

`map.on('mousemove', function (e) {
    var features = map.queryRenderedFeatures(e.point);
    ...
});`

If queryRenderedFeatures is given a null geometry argument, it will search the map's whole viewport. querySourceFeatures functions similarly, but will also return features that are hidden due to overlap or other styling restrictions.

What are the limits of this approach?

Including lots of properties on GeoJSON features increases vector tile size, which will slow page-load. Rendering tens of thousands of features can also cause latency in user interactions.

There are also some gotchas to the Mapbox GL JS query methods. Depending on Tippecanoe’s settings, features can be duplicated or missed: by default, all tiles have some overlap with redundant features to improve rendering, and tiles at wide zooms will include only a subset of point features in order to improve client performance. Separately, if a line or polygon spans across a tile boundary, then that feature will be returned multiple times by a query.

So make sure to stay safe and use responsibly. But if this approach is applicable, then save yourself from duplicate effort the next time you build a visualization alongside vector tiles!

What we're doing.

Latest