Mapbox Vector Tile Specification

A guide to encoding tiled vector data with the Mapbox Vector Tile Specification.

A specification is an important means of setting standards around how data is stored and encoded. Just like applications make assumptions about what information exists in a database, Mapbox tools make assumptions about how vector tiles store geographic information. The Mapbox Vector Tile Specification explicitly provides information about file formats & extensions, projections and bounds, and the internal structure of vector tiles. This page includes:

  1. explanations of version distinctions
  2. examples of geometry encoding
  3. examples of attribute encoding
  4. an explanation of the importance of winding order
  5. a list of concepts not included in the specification

Versioning

The specification is versioned based on major.minor notation. The major version will be incremented with any technical change to the specification format or way it should be interpreted. Changes to the minor version will be reserved for any clarification or correction of clerical errors associated with the specification language.

The major version in the specification name is synonymous with the version field in a Mapbox Vector Tile layer. See the 3.1. Layers section for more details.

Version Date of release Updates
2.1 January 19th, 2016 Correction to the wording in a few locations of the 2.0 specification.
2.0 December 4th, 2015 The focus of version 2.0 of the Mapbox Vector Tile specification is the clarification of the intent of the intial version of the specification and the definition of interior and exterior rings within polygons. The fields within the protobuffer are more clearly defined in this version of the specification and the steps for decoders and encoders are more explicity declared.
1.0.1 July 28, 2014 Update .proto file to match Protocol Buffer style guide, changed namespace
1.0.0 April 13, 2014 First release

Format

Vector tiles are encoded as Google Protobufs (PBF), which allow for serializing structured data. For clarity, Mapbox Vector Tiles use the .mvt file suffix. The specification details are largely structured around the rules implemented in the base .proto file found here.

They are not related at all. PBFs are a format, much like XML and can take many forms. Mapbox Vector Tiles and OSM PBFs are protobuf files, but conform to completely different specifications and are used in different ways.

Encoding geometry

To encode geographic information into a vector tile a tool must convert geographic coordinates, such as latitude and longitude, into vector tile grid coordinates. Vector tiles hold no concept of geographic information. They encode points, lines, and polygons as x/y pairs relative to the top left of the grid in a right-down manner.

This is a step-by-step example showing how a single vector tile encodes geometry in the grid. It follows the commands of the “pen” to encode two rings.

Step 0

An empty vector tile

The vector tile to the right is a 10x10 grid with 2 cell buffer. Let's encode some geometry to the grid. Let's start with the blue polygon.

Vector Tile Grid

Commands

Encoding attributes

Attributes are encoded in a series of tags that exist within a feature in the vector that have integer values that reference keys and values designating the original key:value pairs from the geometry. For large geometry, this removes redundancy for attributes that have the same keys and similar values.

Take a look at the original geojson FeatureCollection on the left and see how its individual parts are encoded into the proper tags of the vector tile protobuf.

Hover over the features and the properties of the GeoJSON.

Original geojson

{ "type": "FeatureCollection", "features": [ { "geometry": { ... }, "type": "Feature", "properties": { "hello": "world", "h": "world", "count": 1.23 } }, { "geometry": { ... }, "type": "Feature", "properties": { "hello": "again", "count": 2 } } ] }

Final vector tile

layers { version: 2 name: "points" features: { id: 1 tags: 0 tags: 0 tags: 1 tags: 0 tags: 2 tags: 1 type: Point geometry: ... } features { id: 2 tags: 0 tags: 2 tags: 2 tags: 3 type: Point geometry: ... } keys: "hello" keys: "h" keys: "count" values: { string_value: "world" } values: { double_value: 1.23 } values: { string_value: "again" } values: { int_value: 2 } extent: 4096 }

Winding order

Winding order refers to the direction a ring is drawn in a vector tile, either clockwise or counter-clockwise. Many geometries are multipolygons with “holes,” which are also represented as polygon rings. It is important to be able to infer winding order to extract source data from a vector tile and understand if the geometry is part of a multipolygon or a unique polygon.

Extracting the original data from images has been difficult on maps in the past, because of the loss of underlying metadata from the geometry that might have been used to create the image. However, with the introduction of client side rendering of vector tiles via GL technologies, the raw geometry data has become useful for a source of information outside of just rendering.

In order for renderers to appropriately distinguish which polygons are holes and which are unique geometries, the specification requires all polygons to be valid (OGC validity). Any polygon interior ring must be oriented with the winding order opposite that of their parent exterior ring and all interior rings must directly follow the exterior ring to which they belong. Exterior rings must be oriented clockwise and interior rings must be oriented counter-clockwise (when viewed in screen coordinates).

The following example geometries show how encoding a ring’s winding order can affect the rendered result. Each example assumes all rings are part of the same multipolygon.

Note: the Y axis is positive-downward in vector tile coordinates!

Description
Winding order
Rendered

A single ring, in clockwise order is rendered as a single, solid polygon.

Ring 1: Clockwise

Two rings with the same winding order will render as two unique polygons overlapping.

Ring 1: Clockwise
Ring 2: Clockwise

Two rings, the first (exterior) ring is in clockwise order, while the second is counter-clockwise. This results in a "hole" in the final render.

Ring 1: Clockwise
Ring 2: Counter-Clockwise

Three rings in a multipolygon that alternate winding order.

Ring 1: Clockwise
Ring 2: Counter-Clockwise
Ring 3: Clockwise

Implementations

Implementations of the Mapbox Vector Tile Specification are far and wide. Many of them fall into one of the following categories:

  • Parsers & generators: libraries that read and/or encode vector tiles, some also have command line utilities
  • Clients: web-based tools that render vector tiles that conform to the specification
  • Applications: browser-based tools for creating and visualizing vector tiles
  • Servers: support rendering and serving up vector tiles (note: the specification doesn’t go into how to do this explicitly)

A great list of tools implementing Mapbox Vector Tiles can be found at github.com/mapbox/awesome-vector-tiles.

What the spec doesn't cover

This specification is very explicit in the way a vector tile should pack data. However, there are some related concepts that this specification does not cover.

How to use vector tiles as a dataset

This specification IS NOT intended to explain how to use vector tiles as a dataset. This is something that has been considered for the future, but it will likely be a separate specification. This specification does not cover how to store, request, or share vector tiles. Consider this specification similar to how the PNG spec explains how to pack data.

How to encode attributes that aren’t strings or numbers

Attributes in geometric data, such as the properties object in GeoJSON can include more than just strings and numbers. They can be arrays or objects. The specification doesn’t cover how to encode these values and is up to the encoder to decide. Tools like Mapnik will turn arrays and objects into strings, which requires the decoder to parse them accordingly. For example, if you had a GeoJSON property called categories and it was an array:

"categories": ["one", "two", "three"]

It would be converted into a string and stored in the protobuf like this:

keys: "categories"
values: {
  string_value: "[\"one\",\"two\",3]"
}

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.

Simplification

The conversion from geographic coordinates (latitude and longitude) to vector tile coordinates (x, y) is an important step, but can be implemented in many different ways prior to vector tile encoding. It is not included in this specification, but there are some important GOTCHAs worth noting.

Simplification can cause invalid polygons according to the OGC standards by oversimplifying polygon rings to the point where their edges overlap. See below how simplifying one line changes the rendering of a polygon by pushing the interior ring outside of the exterior ring.

1 / 4 Polygon with a "hole" 2 / 4 Exterior (blue) and interior (red) rings 3 / 4 Simplified exterior ring 4 / 4 Invalid geometry

When spatial coordinates are converted to tile coordinates, they are rounded to integers. Simplifying (rounding) the coordinates can reverse the winding order. Consider a triangle polygon that is simplified to the vector tile grid. The rounded point can cross over the polygon and "flip" it, rendering its winding order reversed.

1 / 4 Polygon pre-simplification 2 / 4 Simplify a point to the grid 3 / 4 Simplify the second point to the grid 4 / 4 Simplifying the final point flips the triangle and reverses the winding order

Open Specification

Interested in diving into the complete specification? Take a look at the repository on Github. If you have any questions or notice anything incorrect with this page or the specification, you can submit an issue and we’ll work through it.

If you are interested in contributing please refer to the CONTRIBUTING.md file on GitHub.