原文链接https://github.com/mapbox/vector-tile-spec/tree/master/2.1/
矢量瓦片规范
在此文档中, 关于关键词【必须】、【禁止】、【必要的】、【应当】、【不应】、【推荐的】、【可以】与【可选的】的解释, 见RFC 2119 中的描述。
1. 用途
此文档为矢量瓦片地理数据指定了一种节省空间的编码格式。 它被设计用于浏览器或服务器端应用程序,用于快速呈现或查找特征数据。
2. 文件格式
矢量瓦片格式使用 Google Protocol Buffers 编码格式. Protocol Buffers是一种与语言无关、与平台无关的可扩展序列化机制。
2.1. 文件扩展名
矢量瓦片文件的扩展名应该为mvt
. 例如 vector.mvt
.
2.2. Multipurpose Internet Mail Extensions (MIME)
When serving Vector Tiles the MIME type SHOULD be application/vnd.mapbox-vector-tile
.
3. 投影和边界
矢量瓦片表示一个整正方形范围内投影的数据。矢量瓦片不应包含关于边界和投影的信息。这个文件格式假定解码器在解码之前就已经知道投影和边界信息。
Web Mercator 是一个投影的参考, the Google tile scheme 是一个常见的范围参考. 它们一起提供了特定地理区域、特定细节级别和路径之间的一对一关系 ,例如 https://example.com/17/65535/43602.mvt
.
矢量瓦片可以用任何投影和瓦片范围方案来表示数据。
4. 内部结构
这个规范描述了矢量瓦片的数据结构。 读者应该了解矢量瓦片的protobuf的数据文档及其定义的结构。
4.1. Layers
矢量瓦片由一组命名的层组成。每一层包含地理特征和它的元数据。层格式的设计使一层所需的数据在内存中是连续的,因此层可以添加到矢量瓦片中而不需要修改现有的数据。
一个实例瓦片应该至少包含一层。一层应该至少包含一个特征。
层必须包含 version
字段,此字段为图层所依赖矢量瓦片规范版本号的主版本号。例如,一个符合2.1版本规范的图层包含一个整数值为' 2 '的version
字段。 version
字段应该是层中的第一个字段。 解码器应该首先解析version
,以确保他们能够解码每一层。 当矢量瓦片用户遇到一个矢量瓦片层的未知版本时,它可能会尽最大努力尝试解释该层,或者跳过该层。 无论哪种情况,它都应该继续处理矢量瓦片中的后续图层 。
一个图层必须包含一个 name
字段. 一个实例瓦片禁止有两个或两个以上名字完全一样的图层.。在添加一个图层到现有的矢量瓦片之前,编码器必须检查现有的 name
字段,以防止重复。
层中的每个特征(见下文)都可以有一个或多个键值对作为元数据。 键和值是两个列表 keys
和values
的索引,这两个列表是跨层特征共享的。
图层中keys
字段中的每个元素为字符串。 keys
包含图层所用到的特征,每个键都可以通过它在这个集合中的位置来索引,索引从0开始。 keys
不应包含两个完全一样的值。
图层中values
字段中的每个元素为以下几种类型. values
表示图层中用到的所有特征的值,每个值都可以通过它在这个集合中的位置的来索引,索引从0开始。 values
不应包含两个完全一样的值。
为了支持不同的string, boolean, integer, 和浮点型类型, value
字段的protobuf编码由一组 optional
字段组成。一个值必须包含这些可选字段中的一个。
一个图层必须包含一个 extent
, extent
通过整数坐标描述贴图的宽度和高度。通过定义extent
瓦片内的几何图形可以延展出瓦片的范围,这样做通常是为了渲染一个跨越多个相邻瓦片的特征。
举例说明,如果一个瓦片的 extent
为 4096,这个瓦片的坐标单位为瓦片尺寸的1/4096。包含0的坐标在瓦片左侧或上侧的边缘。包含4096的坐标在瓦片右侧或下方的边缘。 1-4095的坐标完全在瓦片范围内,小于0或者大于4096的坐标完全在瓦片之外。 (1,10)
或 (4095,10)
完全在瓦片内,(0,10)或
(4096,10)在瓦片范围的边缘,
(-1,10)或
(4097,10)` 则完全在瓦片之外.
4.2. 特征Features
feature必须包含一个 geometry
字段。
feature必须包含一个 type
字段,如果几何类型(Geometry Types)部分所属
feature必须包含一个 tags
字段,如果存在Feature-level metadata,他们应该 存储在 tags
字段。
feature必须包含一个 id
字段。 如果feature包含 id
字段, id
的值应该在其所在的图层内保证唯一。
4.3. 几何编码Geometry Encoding
矢量瓦片的几何数据定义在一个屏幕坐标系中。瓦片的左上角为坐标系的原点(默认显示)。X轴正方向朝右,Y轴正方向朝下。几何坐标必须为整数。
每个几何图形被编码为32位无符号整数序列并存储在feature的 geometry
字段中。每个整数要么是 命令整数 CommandInteger
要么是参数整数 ParameterInteger
. 解释器把它们解析为一系列有序操作,来生成模型。
命令中的位置是相对"cursor"的,"cursor"是一个可以重定义的点。 feature执行第一个命令时 cursor 在 坐标系的(0,0)
位置。其他命令有可能移动cursor,去影响后续命令。
4.3.1. 命令整数Command Integers
CommandInteger
中command ID,用来表示要执行的命令,command count 表示命令要执行的次数。
command ID 存储在 CommandInteger
的最低3位,取值范围为[0,7],command count 存储在 CommandInteger
中剩下的29位, 取值范围[0
,pow(2, 29) - 1
].
command ID, command count, CommandInteger 三者关系可由以下位操作表示:
CommandInteger = (id & 0x7) | (count << 3)
id = CommandInteger & 0x7
count = CommandInteger >> 3
command ID用来表示以下命令:
Command | Id | Parameters | Parameter Count |
---|---|---|---|
MoveTo | 1 |
dX , dY |
2 |
LineTo | 2 |
dX , dY |
2 |
ClosePath | 7 |
No parameters | 0 |
Command Integers示例
Command | ID | Count | CommandInteger | Binary Representation [Count][Id] |
---|---|---|---|---|
MoveTo | 1 |
1 |
9 |
[00000000 00000000 00000000 00001][001] |
MoveTo | 1 |
120 |
961 |
[00000000 00000000 00000011 11000][001] |
LineTo | 2 |
1 |
10 |
[00000000 00000000 00000000 00001][010] |
LineTo | 2 |
3 |
26 |
[00000000 00000000 00000000 00011][010] |
ClosePath | 7 |
1 |
15 |
[00000000 00000000 00000000 00001][111] |
4.3.2. 参数整数Parameter Integers
命令用到的参数在 ParameterInteger
后面。 ParameterIntegers
的数量等于命令的参数个数乘以 CommandInteger
的command count。 比如, CommandInteger
有一个 MoveTo
命令,command count 为 3,那么在后面根6个 ParameterIntegers
。
ParameterInteger
采用 zigzag 编码,所以小的正数或者负数都会被编码成小整数。 parameter value转ParameterInteger
公式为:
ParameterInteger = (value << 1) ^ (value >> 31)
Parameter values不支持 大于 pow(2,31) - 1
或者小于 -1 * (pow(2,31) - 1)
的值。
解码 ParameterInteger
的公式:
value = ((ParameterInteger >> 1) ^ (-(ParameterInteger & 1)))
4.3.3. 命令类型Command Types
对所有命令的描述中,初始坐标记作 (cX, cY)
,即 cursor 的坐标。
4.3.3.1. MoveTo 命令
MoveTo
命令的 command count 为 n
, 那么它后边必须跟随 n
对ParameterInteger
。每对为 (dX, dY)
:
- 定义坐标
(pX, pY)
, 其中pX = cX + dX
pY = cY + dY
.- 在 POINT 几何图形中中, 这个坐标定义一个新点.
- 在 LINESTRING 几何图形中,这个坐标定义一条线的起始点。
- 在POLYGON 几何图形中, 这个坐标定定义线性环的起始点。
- 移动 cursor 到
(pX, pY)
。
4.3.3.2. LineTo 命令
LineTo
命令的 command count 为 n
, 那么它后边必须跟随 n
对ParameterInteger
。每对为 (dX, dY)
:
- 定义一个以 cursor
(cX, cY)
为起点 以(pX, pY)
为终点 的线段,其中pX = cX + dX
pY = cY + dY
.- 在 LINESTRING 几何图形中,这条线段对当前的线段进行延申。
- W在POLYGON 几何图形中, 这条线段对当前的线性环进行延申。
- 移动 cursor 到
(pX, pY)
。
任何 (dX, dY)
中 dX
和 dY
禁止同时为0
.
4.3.3.3. ClosePath 命令
ClosePath
命令command count 必须为 1 且不带 ParameterInteger
。此命令创建一个从cursor (cX, cY)
开始到起始点结束的线段,来完成POLYGON 几何图形中当前的线性环的绘制。
此命令不改变 cursor位置。
4.3.4. Geometry Types
在feature中 type
字段来描述 geometry
的类型 ,其值为取自枚举 GeomType
。以下几何类型被支持:
- UNKNOWN
- POINT
- LINESTRING
- POLYGON
不支持几何图形集合。
4.3.4.1. Unknown Geometry Type
该规范有意留下一个未知的几何类型作为选项。 这种几何类型对编码器可以选择实现的实验几何类型进行编码。 这种几何类型可以作为实验性的类型被某些编码器所执行。解码器可能会忽略这种几何类型的任何特征。
4.3.4.2. Point Geometry Type
POINT
类型,表示一个或多个点的几何图形。此类型的命令序列必须是一个command count大于0的 MoveTo
的命令。
如果 POINT
类型的 MoveTo
命令的command count 等于 1,那么该几何图形一定是一个单点,否则,几何图形必须被解释为多点几何图形,其中每一对 ParameterInteger
为一个单点。
4.3.4.3. Linestring Geometry Type
LINESTRING
类型表示 单个线条的 linestring 或多线条组合的 multilinestring 几何图形。此类型的命令序列必须由以下一个或者多个重复的序列组成 :
- 一个command count 等于 1的
MoveTo
命令 - 一个command count 大于0的
LineTo
命令
如果 LINESTRING
的命令序列只包含一个 MoveTo
命令,那么该图形一定被解释为单条线段的几何图形 ;否则,该几何图形一定被解释为多条线段组合的图形,每一个 MoveTo
命令表示新线条的起始点。
4.3.4.4. Polygon Geometry Type
POLYGON
类型用来表示 单个多边形或这个组合多边形的几何图形, 每个多边形仅由一个外环组成,而外环又包含零个或多个内环。 此类型的命令序列必须由以下一个或者多个重复的序列组成 :
- 一个
ExteriorRing
- 0个或多个
InteriorRing
s
每一个ExteriorRing
和 InteriorRing
必须由以下序列组成:
- 一个command count 等于 1
MoveTo
命令 - 一个command count 大于 1
LineTo
命令 - 一个
ClosePath
命令
exterior ring 被定义为线性环,通过 计算公式 计算其面积为正数,在瓦片坐标系中(Y 轴正向朝下, X 轴正向朝右) 外环为顺时针顺序。
interior ring 被定义为线性环,通过 计算公式 计算其面积为负数,在瓦片坐标系中(Y 轴正向朝下, X 轴正向朝右) 外环为逆时针顺序。
如果一个 POLYGON
类型的命令序列中只有一个外环那么它一定被解释为一个多边形,否则它一定是一个多边形组合的图形,其每一个内环标示着一个新多边形的起点。如果一个多边形有内环,它们必须跟在它们所属的多边形的外环之后。
线性环必须是没有异常几何点的几何物体,如自交或自切。 在调用线性环的ClosePath
命令之前,cursor的位置不应与线性环的第一个点重复,因为这会创建一个长度为0的线段。 线性环的面积不应为零,因为这意味着环上有异常的几何点。
多边形几何形状不应有任何内环相交,内环必须由外环包围。
4.3.5. 几何图形编码示例
4.3.5.1. Point示例
表示一个点:
- (25,17)
仅需要一条命令:
- MoveTo(+25, +17)
Encoded as: [ 9 50 34 ]
| | `> Decoded: ((34 >> 1) ^ (-(34 & 1))) = +17
| `> Decoded: ((50 >> 1) ^ (-(50 & 1))) = +25
| ===== relative MoveTo(+25, +17) == create point (25,17)
`> [00001 001] = command id 1 (MoveTo), command count 1
4.3.5.2. Multi Point示例
表示两个点分别位于:
- (5,7)
- (3,2)
这需要两条命令:
- MoveTo(+5,+7)
- MoveTo(-2,-5)
Encoded as: [ 17 10 14 3 9 ]
| | | | `> Decoded: ((9 >> 1) ^ (-(9 & 1))) = -5
| | | `> Decoded: ((3 >> 1) ^ (-(3 & 1))) = -2
| | | === relative MoveTo(-2, -5) == create point (3,2)
| | `> Decoded: ((34 >> 1) ^ (-(34 & 1))) = +7
| `> Decoded: ((50 >> 1) ^ (-(50 & 1))) = +5
| ===== relative MoveTo(+5, +7) == create point (5,7)
`> [00010 001] = command id 1 (MoveTo), command count 2
4.3.5.3. Linestring示例
表示由以下点组成的Linestring:
- (2,2)
- (2,10)
- (10,10)
这需要三条命令:
- MoveTo(+2,+2)
- LineTo(+0,+8)
- LineTo(+8,+0)
Encoded as: [ 9 4 4 18 0 16 16 0 ]
| | ==== relative LineTo(+8, +0) == Line to Point (10, 10)
| | ==== relative LineTo(+0, +8) == Line to Point (2, 10)
| `> [00010 010] = command id 2 (LineTo), command count 2
| === relative MoveTo(+2, +2)
`> [00001 001] = command id 1 (MoveTo), command count 1
4.3.5.4. Multi Linestring示例
表示两个Linestring:
- Line 1:
- (2,2)
- (2,10)
- (10,10)
- Line 2:
- (1,1)
- (3,5)
需要以下命令:
- MoveTo(+2,+2)
- LineTo(+0,+8)
- LineTo(+8,+0)
- MoveTo(-9,-9)
- LineTo(+2,+4)
Encoded as: [ 9 4 4 18 0 16 16 0 9 17 17 10 4 8 ]
| | | | === relative LineTo(+2, +4) == Line to Point (3,5)
| | | `> [00001 010] = command id 2 (LineTo), command count 1
| | | ===== relative MoveTo(-9, -9) == Start new line at (1,1)
| | `> [00001 001] = command id 1 (MoveTo), command count 1
| | ==== relative LineTo(+8, +0) == Line to Point (10, 10)
| | ==== relative LineTo(+0, +8) == Line to Point (2, 10)
| `> [00010 010] = command id 2 (LineTo), command count 2
| === relative MoveTo(+2, +2)
`> [00001 001] = command id 1 (MoveTo), command count 1
4.3.5.5. Polygon示例
表示由以下点组成的多边形:
- (3,6)
- (8,12)
- (20,34)
- (3,6) Path Closing as Last Point
需要以下命令:
- MoveTo(3, 6)
- LineTo(5, 6)
- LineTo(12, 22)
- ClosePath
Encoded as: [ 9 6 12 18 10 12 24 44 15 ]
| | `> [00001 111] command id 7 (ClosePath), command count 1
| | ===== relative LineTo(+12, +22) == Line to Point (20, 34)
| | ===== relative LineTo(+5, +6) == Line to Point (8, 12)
| `> [00010 010] = command id 2 (LineTo), command count 2
| ==== relative MoveTo(+3, +6)
`> [00001 001] = command id 1 (MoveTo), command count 1
4.3.5.6. Multi Polygon示例
下边的示例 由两个多边形组成 ,其中一个有一个洞。多边形的顶点如下所示。多边形的顺序在这个例子中是非常重要的,因为它表示内环和新多边形之间的区别。
- Polygon 1:
- Exterior Ring:
- (0,0)
- (10,0)
- (10,10)
- (0,10)
- (0,0) Path Closing as Last Point
- Exterior Ring:
- Polygon 2:
- Exterior Ring:
- (11,11)
- (20,11)
- (20,20)
- (11,20)
- (11,11) Path Closing as Last Point
- Interior Ring:
- (13,13)
- (13,17)
- (17,17)
- (17,13)
- (13,13) Path Closing as Last Point
- Exterior Ring:
此多边形需要以下命令:
- MoveTo(+0,+0)
- LineTo(+10,+0)
- LineTo(+0,+10)
- LineTo(-10,+0) // Cursor at 0,10 after this command
- ClosePath // End of Polygon 1
- MoveTo(+11,+1) // NOTE THAT THIS IS RELATIVE TO LAST LINETO!
- LineTo(+9,+0)
- LineTo(+0,+9)
- LineTo(-9,+0) // Cursor at 11,20 after this command
- ClosePath // 这是一个新的多边形,因为它的面积为正数
- MoveTo(+2,-7) // NOTE THAT THIS IS RELATIVE TO LAST LINETO!
- LineTo(+0,+4)
- LineTo(+4,+0)
- LineTo(+0,-4) // Cursor at 17,13
- ClosePath // 这是一个内环,因为它的面积为负数
Encoded as: [ 9 0 0 26 20 0 0 20 19 0 15 9 22 2 26 18 0 0 18 17 0 15 9 4 13 26 0 8 8 0 0 7 15 ]
| | | | | | | | `> [00001 111] (ClosePath)
| | | | | | | `> [00011 010] = (LineTo), command count 3
| | | | | | `> [00001 001] = command id 1 (MoveTo), command count 1
| | | | | `> [00001 111] (ClosePath)
| | | | `> [00011 010] = (LineTo), command count 3
| | | `> [00001 001] = command id 1 (MoveTo), command count 1
| | `> [00001 111] (ClosePath)
| `> [00011 010] = (LineTo), command count 3
`> [00001 001] = command id 1 (MoveTo), command count 1
4.4. Feature 属性
特征属性位于feature的 tag
字段,为成对的整数。每一对中的第一个整数为layer
中keys
的索引(从0开始),用来表示它属于此特征, 每一对中的第二个整数为layer
中values
的索引(从0开始),用来表示它属于此特征。每个键索引在该特性中必须是唯一的,这样该特性中的其他属性对就不会有相同的键索引。 一个特性必须有偶数个 tag
字段。特性 tag
自动中的键和值分别不能大于等于 keys
values
集合元素的数量。
4.5. 示例
例如, 一个 GeoJSON 特征:
{
"type": "FeatureCollection",
"features": [
{
"geometry": {
"type": "Point",
"coordinates": [
-8247861.1000836585,
4970241.327215323
]
},
"type": "Feature",
"properties": {
"hello": "world",
"h": "world",
"count": 1.23
}
},
{
"geometry": {
"type": "Point",
"coordinates": [
-8247861.1000836585,
4970241.327215323
]
},
"type": "Feature",
"properties": {
"hello": "again",
"count": 2
}
}
]
}
可以被构造为这样:
layers {
version: 2
name: "points"
features: {
id: 1
tags: 0
tags: 0
tags: 1
tags: 0
tags: 2
tags: 1
type: Point
geometry: 9
geometry: 2410
geometry: 3080
}
features {
id: 2
tags: 0
tags: 2
tags: 2
tags: 3
type: Point
geometry: 9
geometry: 2410
geometry: 3080
}
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
}
记住,几何形状的精确值会根据贴图的投影和范围而不同。