Mapping 11M Geonames Points with Tile38
By: Steven Wolfe
Tags: Golang, Geospatial, Tile38, Mapbox

Vector Tiles in Mapbox

It’s been about five months since I started working with Tile38 and, having no prior experience with anything geospatial or mapping, the learning curve has been rather steep. Nonetheless, it has been an extremely rewarding time, and I’ve learned quite a few things both in the office and by experimenting on my own.

A few weeks ago I learned about something called Vector Tiles, a standard mapping mechanism used to encapsulate geodata into a quadtree structure in order to only serve visible data. This method of mapping points not only reduces point request payload sizes but also allows for immense data compression and caching that drastically reduces the load on your tile server. Very cool! — Previously, I had assumed that whenever you needed to plot points or polygons on a map, you needed to retrieve all the data and place it on the map, even if it’s offscreen.

Tile38

Tile38 natively supports TILE queries for the data needed to produce a Mapbox Vector Tile (MVT), making it the perfect highly performant datastore that should give us everything we need to render datasets of any size.

In order to learn-by-doing and demonstrate the power of both vector tile mapping and geospatial queries with Tile38 (the brains behind the data), I decided to set up a single Tile38 instance, packed it with the entire 11M record Geonames database, and bound the two together by a simple HTTP Tile server that does the MVT translation. The result was a fairly slow, unoptimized version of the demo we have currently at https://geonames.tile38.com.

Despite using Vector Tiles alone, however, the map was still lacking the performance we were looking for. Tiles took a while to load, my 16GB system was extremely low on memory, and my computer sounded like it was about to take off the moment Mapbox began to bombard my tile server with requests.

It was time to optimize!

Optimizations

Utilizing a series of different optimization techniques, we were not only able to alleviate the load on our tile server and Tile38, but also reduce the amount of bandwidth required for clients to consume the tile payloads.

Trimming Properties

Initially, I had stored all information about the point in the properties field of the GeoJSON object in Tile38. With 11M points, this didn’t seem to scale so well, so trimming properties to nothing other than the places name helped to dramatically reduce the memory footprint of Tile38.

Point only Tile Queries and Geocoding

Since the map displays only points, there is no reason for Mapbox to know the id or name of every single point on the map. By performing POINT TILE queries (rather than OBJECT TILE queries) on Tile38 and returning just the point data (absolutely no metadata tags) for a tile request in the MVT’s response, we were able to reduce the response payload to just what was needed to display points. Point names are then retrieved and displayed using a geocoding request to the tile server when a point is clicked.

Point Reduction

At certain zoom levels, points visually overlap one another and don’t provide much value to the user. By reducing the number of points displayed when a large amount of overlap occurs, we were able to again trim the tile server’s response payloads while giving the end-user an identical visual experience.

GZIP

By design, Mapbox’s Vector Tile response already provides an epic amount of compression with the use of protobufs. In addition to protobufs, we were also able to compress the response further with the use of GZIP compression.

On Disk Tile Caching

Some larger queries (zoom level 0–3) take several seconds to process and the underlying data in our use case never changes. So, by caching the processed tile responses on disk indefinitely we were able to ensure that our tile server would never need to process a region more than once.

Cloudflare In-Browser/CDN Caching

Finally, using a CDN like Cloudflare to cache tile responses helped not only to bring the tile’s responses geographically closer to the requester but also alleviate the load on the tile server altogether, leaving it more able to process requests that are a cache-miss on the CDN.