tilezen / raw_tiles

Raw tiles
MIT License
12 stars 4 forks source link

Raw Tiles

Raw tiles are raw extracts of a particular database table at a given zoom. These are stored in a gzipped msgpack format.

For "standard" geographic tables, items within the file are stored as an array of three elements:

  1. The feature's ID, which should be an integral number.
  2. The feature's geometry, encoded as Well-Known Binary.
  3. The feature's properties, encoded as a native msgpack map.

Two auxilliary tables are also used; planet_osm_ways and planet_osm_rels. The former stores the relationship between ways, which can be either linestring or polygon features, and the nodes which comprise them. This is used to figure out which gates are part of of highway features. The latter stores generalised "relation" relationships between elements, and is used to look up which routes go along a particular road, or figure out the structure of railway stations. The items stored within these tables are:

These are then stored somewhere, for example S3, to be used by other layers in the tile rendering pipeline.

Why not just use a database?

The Tilezen system originally used a PostgreSQL database with PostGIS to provide spatial indexes. This system performs very well and is very flexible. This system worked well for Tilezen, and many other tile providers, for many years.

However, we found a couple of issues would repeatedly cause headaches; scaling and logic in the database.

Scaling

Each tile rendered, whether batch in tilequeue or on-demand in tileserver, queries the database. This means that when there's a lot of load, due to many data changes or on-demand interest in non-cached areas, the PostgreSQL servers can become a bottleneck. Individual PostgreSQL servers scale well, and scaling out to multiple servers is possible. However, setting up each new replica server and synchronising it with the master takes a long time (around 24h in our experience), which makes it unsuitable for handling real-time load changes.

This doesn't mean that we shouldn't use PostgreSQL, just that we should find some way to take it off the "hot path".

Logic in the database

To support efficient queries for features at a given zoom level, the Tilezen database contains indexes on the feature geometry and minimum zoom level that the feature is visible at. This works well, but means that the definition of which zoom level a feature is visible at must be available to the database.

Most features have a simple relationship between their tags, geometry and the min_zoom they are assigned. However, some features have complex queries to determine their min_zoom. Others have extremely complex queries.

This creates a headache when deploying new versions of the style, as some deployments require a migration to update the min_zoom values in the database to their new calculated values. These migrations are especially bad, as they are hand-written and therefore prone to missing things which needed to be updated. They are also hard to reverse if something goes wrong.

Finally, having logic specific to a particular version of a single style in the database makes it difficult to share that database between many different styles, or many versions of a single style. In turn, this makes "Long Term Support" releases difficult without duplicating infrastructure.

Raw(r) system design

To solve the problems above, we want to:

To do this, we combine the Fundamental Theorem of Software Engineering,

We can solve any problem by introducing an extra level of indirection

With something that we might call the "Fundamental Theorem of Web Mapping",

We can solve any problem by making tiles

And end up with an intermediate data store, consisting of tiles of "raw" data. These tiles would be able to render any style, or version of a style, because they contain all the data and haven't been transformed or filtered. Due to the limited geographic footprint of the tile, they inherently act as a coarse index over the data. However, to avoid querying the database, all the raw(r) tiles must be available.

Low zoom levels present a problem, since rendered vector tiles at low zoom levels typically contain a tiny fraction of the total amount of data in their geographic extent. Although raw(r) tiles are not intended for client use, and we can assume a high-bandwidth datacentre network, raw(r) tiles are still not much use if the 0/0/0 tile contains the entire, multi-gigabyte dataset. Therefore, we choose to limit the "raw" data to a particular zoom level. We then need some style-aware way to construct the lower zoom levels from those.

There needs to be a balance between the number of raw(r) tiles being generated and kept up to date, which is 4^z, and the size of each individual tile, which falls off as 4^-z. We're hoping that a good compromise between these two will be zoom 10.

The overall system looks like:

  1. A database of geographic data, possibly with updates.
  2. A process which reads the database and produces raw(r) tiles, possibly in response to updates.
  3. A store of raw(r) tiles which is quick to access and scales easily.
  4. A batch or on-demand process which reads raw(r) tiles and calculates a min_zoom for each feature based on a style, producing one or more vector tiles. The vector tiles are either stored in a metatile or returned to the client. For features where min_zoom is smaller than z10, the batch process also emits a lower-zoom "cooked" raw(r) tile fragments.
  5. A process which aggregates "cooked" raw(r) tile fragments into whole "cooked" raw(r) tiles at zoom 5. This recurses again to make the top of the pyramid; the zoom 0 "cooked" raw(r) tile.
  6. A store (cache) of vector metatiles which is quick to access and scales easily.
  7. A front-end process which unpacks metatiles and responds with the tile that the client requested.

Raw(r) tiles benefits

Raw(r) tiles drawbacks

Why msgpack?

Msgpack makes small files which are quick enough to parse. They don't enforce a schema, which is helpful during development, but might cause issues if the file definition changes later on.

There are other serialisation formats which could be used, but at this point have some disadvantages which makes msgpack look like a better option:

Architecture

RAWR tiles system architecture diagram

The diagram above represents one way in which RAWR tiles can be used in a "global rendering" system, and is how the current Nextzen tile service renders tiles.

This starts with (on the left) data being loaded from OpenStreetMap, Natural Earth and Tilezen curated data (we also load data from osmdata.openstreetmap.de, but this is derived from OSM and omitted from the diagram for brevity). The data is loaded into a PostGIS database, and replicas of that are made to scale out read load.

The tilequeue software is then used to create RAWR tiles from the database, orchestrated using AWS Batch and stored in an AWS S3 bucket. The database replicas are also used to render (via Batch) the low zoom (zoom < 10) tiles directly into the meta tiles bucket. The RAWR tiles are rendered (again, via Batch) from the RAWR tiles directly to meta tiles, without needing to contact the database. Meta tiles are served to the outside world via tapalcatl-py, which runs on AWS Lambda and unpacks the meta tiles to the individual tiles.