-
Notifications
You must be signed in to change notification settings - Fork 211
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
v1.0.2 draft #36
v1.0.2 draft #36
Changes from all commits
657c5d7
0293370
710036a
f13294b
7511427
279ea1e
b959772
4380643
e91624e
24ef298
f3c9254
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
# vector-tile-spec | ||
|
||
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", | ||
"SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in | ||
this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). | ||
|
||
## 1. Purpose | ||
|
||
This specification attempts to create a standard for encoding of tiled geospatial data that can be shared across clients. | ||
|
||
## 2. File format | ||
|
||
The vector tile encoding scheme encodes vector data for a tile in a space efficient manner. It is designed to be used in browsers or serverside applications for fast rendering or lookups of feature data. | ||
|
||
Vector Tiles use [Google Protocol buffers](https://developers.google.com/protocol-buffers/) as a container format. It is exclusively geared towards square pixel tiles in [Spherical Mercator projection](http://wiki.openstreetmap.org/wiki/Mercator). | ||
|
||
## 3. Internal structure | ||
|
||
A vector tile SHOULD consist of one or more named layers. | ||
|
||
A layer MUST contain a field describing its name and a version of the vector tile spec from which it was created. A layer MUST also contain one or more features. A layer MAY contain one or more sets of feature attributes. | ||
|
||
Features MAY contain an `id` and `tags` (attributes) and MUST contain either a `geometry` (either point, linestring, or polygon) or a `raster` field. A feature SHALL NOT contain both a `raster` and a `geometry` field. If a feature has a `geometry` field it MUST also have a `type` field to describe the `geometry`. | ||
|
||
### 3.1. Geometry Encoding | ||
|
||
Geometries are stored as an a single array of integers that represent a command,x,y stream (where command is a rendering command like `move_to` or `line_to`). Commands are encoded only when they change. | ||
|
||
Geometries with multiple parts (multipoint, multiline, or multipolygon) MUST be encoded one after another in the same `geometry` field and therefore are "flattened". Geometries with only a single part MUST have only a single `move_to` present. For multipoints and multilines a repeated `move_to` SHALL indicate another part of a multipart geometry. For polygons a repeated `move_to` SHALL indicate either another exterior of a new polygon part or an interior ring of the previous polygon part. The winding order MUST be used to distinguish between the two types: polygon interior rings (holes) must be oriented with the opposite winding order than their parent exterior rings and all interior rings MUST directly follow the exterior ring to which they belong. | ||
|
||
The exterior ring MUST have a positive area as calculated by applying the [surveyor's formula](https://en.wikipedia.org/wiki/Shoelace_formula) to the vertices of the polygon in tile coordinates. In screen coordinates (with the Y axis positive down) this makes the exterior ring's winding order appear clockwise. In a frame where the Y axis is positive up, this would make the winding order appear counter clockwise. | ||
|
||
Geometry collections are not supported. | ||
|
||
Geometries SHOULD be clipped, reprojected into spherical mercator, converted to screen coordinates, and MUST be [delta](http://en.wikipedia.org/wiki/Delta_encoding) and [zigzag](https://developers.google.com/protocol-buffers/docs/encoding#types) encoded. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Geometries are stored as graphics in the tiles, as they are projected, clipped and converted to screen coordinates. |
||
|
||
Geometries SHOULD be a geometric objects that has no anomalous geometric points, such as self intersection or self tangency. | ||
|
||
### 3.2. Feature Attributes | ||
|
||
Feature attributes are encoded as key:value pairs which are dictionary encoded at the layer level for compact storage of any repeated keys or values. Values use [variant](https://developers.google.com/protocol-buffers/docs/encoding#varints) type encoding supporting both unicode strings, boolean values, and various integer and floating point types. | ||
|
||
### 3.3. Example | ||
|
||
For example, a GeoJSON feature like: | ||
|
||
```json | ||
{ | ||
"type": "FeatureCollection", | ||
"features": [ | ||
{ | ||
"geometry": { | ||
"type": "Point", | ||
"coordinates": [ | ||
-8247861.1000836585, | ||
4970241.327215323 | ||
] | ||
}, | ||
"type": "Feature", | ||
"properties": { | ||
"hello": "world" | ||
} | ||
} | ||
] | ||
} | ||
``` | ||
|
||
Would be structured like: | ||
|
||
```js | ||
layers { | ||
name: "points" | ||
features { | ||
id: 1 | ||
tags: 0 | ||
tags: 0 | ||
type: Point | ||
geometry: 9 | ||
geometry: 2410 | ||
geometry: 3080 | ||
} | ||
keys: "hello" | ||
values { | ||
string_value: "world" | ||
} | ||
extent: 4096 | ||
version: 2 | ||
} | ||
``` | ||
|
||
The authoritative details on encoding are part of the code comments for the [vector tile protobuf schema document](vector_tile.proto). |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
// Protocol Version 1 | ||
|
||
package vector_tile; | ||
|
||
option optimize_for = LITE_RUNTIME; | ||
|
||
message Tile { | ||
enum GeomType { | ||
UNKNOWN = 0; | ||
POINT = 1; | ||
LINESTRING = 2; | ||
POLYGON = 3; | ||
} | ||
|
||
// Variant type encoding | ||
message Value { | ||
// Exactly one of these values may be present in a valid message | ||
optional string string_value = 1; | ||
optional float float_value = 2; | ||
optional double double_value = 3; | ||
optional int64 int_value = 4; | ||
optional uint64 uint_value = 5; | ||
optional sint64 sint_value = 6; | ||
optional bool bool_value = 7; | ||
|
||
extensions 8 to max; | ||
} | ||
|
||
message Feature { | ||
optional uint64 id = 1 [ default = 0 ]; | ||
|
||
// Tags of this feature are encoded as repeated pairs of | ||
// integers. Even indexed values (n, beginning with 0) are | ||
// themselves indexes into the layer's keys list. Odd indexed | ||
// values (n+1) are indexes into the layer's values list. | ||
// The first (n=0) tag of a feature, therefore, has a key of | ||
// layer.keys[feature.tags[0]] and a value of | ||
// layer.values[feature.tags[1]]. | ||
repeated uint32 tags = 2 [ packed = true ]; | ||
|
||
// The type of geometry stored in this feature. | ||
optional GeomType type = 3 [ default = UNKNOWN ]; | ||
|
||
// Contains a stream of commands and parameters (vertices). The | ||
// repeat count is shifted to the left by 3 bits. This means | ||
// that the command has 3 bits (0-7). The repeat count | ||
// indicates how often this command is to be repeated. Defined | ||
// commands are: | ||
// - MoveTo: 1 (2 parameters follow) | ||
// - LineTo: 2 (2 parameters follow) | ||
// - ClosePath: 7 (no parameters follow) | ||
// | ||
// Commands are encoded as uint32 varints. Vertex parameters | ||
// are encoded as deltas to the previous position and, as they | ||
// may be negative, are further "zigzag" encoded as unsigned | ||
// 32-bit ints: | ||
// | ||
// n = (n << 1) ^ (n >> 31) | ||
// | ||
// Ex.: MoveTo(3, 6), LineTo(8, 12), LineTo(20, 34), ClosePath | ||
// Encoded as: [ 9 6 12 18 10 12 24 44 15 ] | ||
// | | `> [00001 111] command type 7 (ClosePath), length 1 | ||
// | | ===== relative LineTo(+12, +22) == LineTo(20, 34) | ||
// | | ===== relative LineTo(+5, +6) == LineTo(8, 12) | ||
// | `> [00010 010] = command type 2 (LineTo), length 2 | ||
// | ==== relative MoveTo(+3, +6) | ||
// `> [00001 001] = command type 1 (MoveTo), length 1 | ||
// | ||
// The original position is (0,0). | ||
repeated uint32 geometry = 4 [ packed = true ]; | ||
|
||
optional bytes raster = 5; | ||
} | ||
|
||
message Layer { | ||
// Any compliant implementation must first read the version | ||
// number encoded in this message and choose the correct | ||
// implementation for this version number before proceeding to | ||
// decode other parts of this message. | ||
required uint32 version = 15 [ default = 1 ]; | ||
|
||
required string name = 1; | ||
|
||
// The actual features in this tile. | ||
repeated Feature features = 2; | ||
|
||
// Dictionary encoding for keys | ||
repeated string keys = 3; | ||
|
||
// Dictionary encoding for values | ||
repeated Value values = 4; | ||
|
||
// The bounding box in this tile spans from 0..4095 units | ||
optional uint32 extent = 5 [ default = 4096 ]; | ||
|
||
extensions 16 to max; | ||
} | ||
|
||
repeated Layer layers = 3; | ||
|
||
extensions 16 to 8191; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should the last in a ring point be the same as the first point, or are polygon rings implicitly closed? Section 6.1.7.1 of the "Simple" Features spec says that linear rings must be both simple and closed explicitly (first point = last point), but I don't think that gains us anything. Instead, could this spec say something like "exterior and interior rings MAY be closed. If they are not, then client software SHOULD treat them as implicitly closed (e.g: by appending the first point)."?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@zerebubuth I will address this in the next commit to the spec. Thank you for bring it up.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Whatever the spec resolution ends up being, my understanding is that Mapbox's JS decoder currently assumes that rings are implicitly closed, as it looks like it duplicates the first point when decoding (without checking if these are already the same): https://github.com/mapbox/vector-tile-js/blob/master/lib/vectortilefeature.js#L77
Is that a correct reading?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct, and implicit close will be mandatory in the next spec revision.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool thanks, that's been my expectation and preference, keeps things simpler.