地圖經過切割后形成了瓦片,每一個編號對於一個瓦片,瓦片可以是柵格數據也可以是矢量數據,柵格數據就是一張正方形圖片,渲染時只需要將這張圖片作為紋理貼到指定位置就行可以,柵格瓦片的缺點就是數據量大,縮放時失真,不能自由配置顯示樣式。這些缺點在矢量瓦片中都得以解決,矢量瓦片中使用矢量數據描述地圖元素,通過渲染這些矢量數據形成地圖,數據量很小,縮放的時候不會失真,可以在不同視角展示,可以展示更豐富的高度信息,例如可以拔高建築物。
1,瓦片數據格式設計原則
瓦片數據在地圖渲染的流程中包含:下載,解析,生成渲染Mesh數據,在所有,一般對瓦片數據格式的要求是,體積小,解析快,可以配合不同的樣式生成不同的效果。
2,瓦片中的Feature
2.1 Feature
Feature是幾何圖形數據和屬性的合集。例如點Feature 包含 x ,y 坐標,和name等屬性。下面是Feature類型對應描述的地圖元素。
Feature | 常見地圖元素 |
點 | POI |
線 | 路網,邊界,水系骨架 |
面 | 區域,海洋,綠地,建築物 |
多點 | 點雲,熱力圖 |
多線 | 多線相同屬性的線 |
多面 | 多個相同屬性的面 |
3D模型 | 地圖上的3D元素,例如建築物模型 |
2.2 數據模型
圖形數據和特性的屬性構成了數據模型,例如建築物的數據模型包含一個多邊形代表俯視輪廓,屬性中包含名稱,高度,類型(商業還是住宅等)。不同地圖使用的數據模型不同,包含的信息豐富程度也不相同,但是基本的信息都很雷同,下面是常見的一些數據模型:
地圖元素 | 模型信息 |
POI | 點數據,文本,重要度(用於POI碰撞),商標,類型 |
公路 | 線數據,名稱,道路等級,方向性,類型 |
鐵路 | 線數據,名稱,類型 |
水面 | 名稱 |
綠地 | 名稱 |
建築物 | 名稱,高度 |
邊界線 | 類型 |
2.3 數據與樣式
在渲染實現中,通常是先不區分實際地圖元素,而是實現對每種類型Feature的渲染,然后通過不同的配置實現不同的現實效果。例如,公路,鐵路和邊界線都是線元素,只要實現線的渲染,在線的渲染實現中,線的顏色,寬度,實虛線都可以配置,這樣我們通過不同的配置就可以實現各種公路,鐵路,和邊界線的渲染。這些配置就是樣式。數據模型中屬性需要能關聯的一個特性的樣式,這樣我們就能通過一組樣式配置來配置整個地圖了。不同的樣式組合就渲染出不同的地圖風格,例如百度地圖中的普通地圖和旅游地圖。
Mapbox MVT 數據格式規格
矢量瓦片標准
本文檔中的“必須”、“必須不”、“必備”、"應該"、“不應該”、“建議”、“可以”、“可選”的含義參照RFC 2119。
1. 目標
本文檔規定了一種節省存儲空間的矢量瓦片數據編碼格式。這種格式應用於客戶端或服務端高效渲染或查詢要素信息。
2. 文件格式
矢量瓦片文件采用Google Protocol Buffers進行編碼。Google Protocol Buffers是一種兼容多語言、多平台、易擴展的數據序列化格式。
2.1. 文件后綴
矢量瓦片文件的后綴應該為mvt
。例如,vector.mvt
。
2.2 MIME類型
矢量瓦片的MIME類型應該設置為application/vnd.mapbox-vector-tile
。
3. 投影和范圍
矢量瓦片表示的是投影在正方形區塊上的數據。矢量瓦片不應該包含范圍和投影信息。解碼方被假定知道矢量瓦片的范圍和投影信息。
Web Mercator是默認的投影方式,Google tile scheme是默認的瓦片編號方式。兩者一起完成了與任意范圍、任意精度的地理區域的一一對應,例如https://example.com/17/65535/43602.mvt
。
矢量瓦片可以用來表示任意投影方式、任意瓦片編號方案的數據。
4. 內部結構
這部分內容描述矢量瓦片的數據結構。讀者需要先了解矢量瓦片protobuf編碼方案文件中的結構定義。
4.1. 圖層
矢量瓦片由一組命名的圖層構成。每個圖層包含幾何要素和元數據信息。設計的圖層格式能夠保證圖層數據能夠在內存中按順序排列,由此在圖層組末尾添加一個新的圖層就不用更改已有的數據。
每塊矢量瓦片應該至少包含一個圖層。每個圖層應該至少包含一個要素。
圖層必須包含一個version
字段表示此圖層所遵守的《矢量瓦片標准》的主版本號。例如,某個圖層遵守2.1版本的標准,那么它的version
字段的值則為整數2
。version
字段應該設定為圖層的第一個字段。解碼器應該首先解析version
字段,以確定是否能夠解析該版本的圖層。當遇到一個未知版本的矢量瓦片圖層時,解碼器可以嘗試去解析它,或者可以跳過該圖層。以上兩種情況下,解碼器都應該繼續解析后續的圖層。
圖層必須包含一個name
字段。每塊矢量瓦片必須不包含兩個或兩個以上的圖層具有相同name
值。在向一塊矢量瓦片添加一個新的圖層之前,編碼器必須檢查已有的name
值以防止重復。
圖層中的每個要素可以包含一個或多個key-value作為它的元數據(見下文)。所有要素的key和value被分別索引為兩個列表——keys
和values
——為圖層中的所有要素所共享。
圖層keys
字段的每個元素都是字符串。keys
字段包含了圖層中所有要素的key,並且每個key可以通過它在keys
列表中的索引號引用,第一個key的索引號是0 。keys
列表必須不包含兩個或兩個以上key是一樣的。
圖層values
字段的每個元素是多種類型的值的編碼(見下文)。values
字段包含了圖層中所有要素的value,並且每個value可以通過它在values
列表中的索引號引用,第一個value的索引號是0 。values
列表必須不包含兩個或兩個以上value是一樣的。
為了支持字符串型、布爾型、整型、浮點型多種類型的值,對value
字段的編碼包含了一組optional
字段。每個value必須包含其中的一個字段。
圖層必須包含一個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. 要素
每個要素必須包含一個geometry
字段。
每個要素必須包含一個type
字段,該字段將在幾何類型章節描述(4.3.4)。
每個要素可以包含一個tags
字段。如果存在屬於要素級別的元數據,應該存儲到tags
字段中。
每個要素可以包含一個id
字段。如果一個要素包含一個id
字段,那么id
字段的值應該相對於圖層中的其他要素是唯一的。
4.3. 幾何圖形編碼
矢量瓦片中的幾何數據被定義為屏幕坐標系。瓦片的左上角(顯示默認如此)是坐標系的原點。X軸向右為正,Y軸向下為正。幾何圖形中的坐標必須為整數。
幾何圖形被編碼為要素的geometry
字段的一個32位無符號型整數序列。每個整數是CommandInteger
或者ParameterInteger
。解碼器解析這些整數序列作為生成幾何圖形的一系列有序操作。
指令涉及到的位置是相對於“游標”的,即一個可重定義的點。對於要素中的第一條指令,游標在坐標系中的位置是(0,0)
。有些指定能夠移動游標,因而會影響到接下來執行的指令。
4.3.1. 指令數
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表示以下指令中的一種:
指令 | Id | 參數 | 參數個數 |
---|---|---|---|
MoveTo | 1 |
dX , dY |
2 |
LineTo | 2 |
dX , dY |
2 |
ClosePath | 7 |
無參數 | 0 |
指令數示例
指令 | ID | Count | CommandInteger | 二進制表示[Count][Id] |
---|---|---|---|---|
MoveTo | 1 |
1 |
9 |
[00000000 00000000 0000000 00001][001] |
MoveTo | 1 |
120 |
961 |
[00000000 00000000 0000011 11000][001] |
LineTo | 2 |
1 |
10 |
[00000000 00000000 0000000 00001][010] |
LineTo | 2 |
3 |
26 |
[00000000 00000000 0000000 00011][010] |
ClosePath | 7 |
1 |
15 |
[00000000 00000000 0000000 00001][111] |
4.3.2. 參數數
指令的所有參數緊跟在ParameterInteger
之后。跟在CommandInteger
之后的ParameterIntegers
個數等於指令所需要參數的個數乘以指令執行的次數。例如,一條指示MoveTo
指令執行3次的CommandInteger
之后會跟隨6個ParameterIntegers
。
ParameterInteger
由zigzag方式編碼得到,以使小負數和正數都被編碼為小整數。將參數值編碼為ParameterInteger
按以下公式轉換:
ParameterInteger = (value << 1) ^ (value >> 31)
參數值不支持大於pow(2,31) - 1
或-1 * (pow(2,31) - 1)
的數值。
以下的公式用來將ParameterInteger
解碼為實際值:
value = ((ParameterInteger >> 1) ^ (-(ParameterInteger & 1)))
4.3.3. 指令類型
以下關於指令的描述中,游標的初始位置定義為坐標(cX, cY)
,其中cX
指代游標在X軸上的位置,cY
指代游標在Y軸上的位置。
4.3.3.1. MoveTo指令
表示MoveTo
指令執行n
的ParameterInteger
必須立即接上n
對ParameterInteger
。對於(dX, dY)
參數:
- 定義坐標
(pX, pY)
,其中pX = cX + dX
和pY = cY + dY
。- 對於點要素,這個坐標定義了一個新的點要素。
- 對於線要素,這個坐標定義了一條新的線要素的起點。
- 對於面要素,這個坐標定義了一個新環的起點。
- 將游標移至
(pX, pY)
。
4.3.3.1. LineTo指令
表示LineTo
指令執行n
的ParameterInteger
必須立即接上n
對ParameterInteger
。對於(dX, dY)
參數:
- 定義一條以游標位置
(cX, cY)
為起點,(pX, pY)
為終點的線段,其中pX = cX + dX
和pY = cY + dY
。- 對於線要素,這條線段延長了當前線要素。
- 對於面要素,這條線段延長了當前環。
- 將游標移至
(pX, pY)
。
對於任意一對(dX, dY)
,dX
和dY
必須不能同時為0
.
4.3.3.3. ClosePath指令
每條ClosePath
指令必須只能執行一次並且無附帶參數。這條指令通過構造一條以游標(cX, cY)
為起點、當前環的起點為終點的線段,閉合面要素的當前環。
這條指定不改變游標的位置。
4.3.4. 幾何類型
要素geometry
字段的type
的取值必須是GeomType
枚舉值之一。支持的幾何類型如下:
- UNKNOWN
- POINT
- LINESTRING
- POLYGON
不支持GeometryCollection
類型。
4.3.4.1. Unknown幾何類型
本標准有意設置一個Unknown幾何類型。這種幾何類型可以用來編碼試驗性的幾何類型。解碼器可以選擇忽略這種幾何類型的要素。
4.3.4.2. Point幾何類型
POINT
幾何類型用來表示單點或多點幾何。每個點幾何的指令序列必須包含一個MoveTo
指令,並且該指令的command count大於0。
如果POINT
幾何的MoveTo
的command count為1,那么必須將其解析為單點;否則必須解析為多點,指令后面的每對ParameterInteger
表示一個單點。
4.3.4.3. Linestring幾何類型
LINESTRING
幾何類型用來表示單線或多線幾何。線幾何的指令序列必須包含一個或多個下列序列:
- 一個
MoveTo
指令,其command count為1 - 一個
LineTo
指令,其command count大於0
如果LINESTRING
的指令序列只包含1個MoveTo
指令,那么必須將其解析為單線;否則,必須將其解析為多線,其中的每個MoveTo
指令開始構造一條新線幾何。
4.3.4.4. Polygon幾何類型
POLYGON
幾何類型表示面或多面幾何,每個面有且只有一個外環和零個或多個內環。面幾何的指令序列包含一個或多個下列序列:
- 一個
ExteriorRing
- 零個或多個
InteriorRing
Each ExteriorRing
and InteriorRing
MUST consist of the following sequence: 每個ExteriorRing
和InteriorRing
必須包含以下序列:
- 一個
MoveTo
指令,其command count為1 - 一個
LineTo
指令,其command count大於1 - 一個
ClosePath
指令
一個外環被定義為一個線性的環,當應用surveyor's formula,以多邊形的節點在瓦片坐標系下的坐標計算面積時,其面積為正。在瓦片坐標系下(X向右為正,Y向下為正),外環節點以順時針旋轉。
一個內環被定義為一個線性的環,當應用surveyor's formula,以多邊形的節點在瓦片坐標系下的坐標計算面積時,其面積為負。在瓦片坐標系下(X向右為正,Y向下為正),內環節點以逆時針旋轉。
如果POLYGON
的指令序列只包含一個外環,那么必須將其解析為單面;否則,必須解析為多面幾何,其中每個外環表示一個新面的開始。如果面幾何包換內環,那么必須將其編碼到所屬的外環之后。
線性環必須不包含異常點,例如自相交或自相切。在ClosePath
之前的坐標不應該與線性環的起始點坐標相同,因為會產生零長度的線段。線性環經過surveyor's formula計算的面積不應該為0,因為這意味着環包含有異常點。
面幾何必須不能有內環相交,並且內環必須被包圍在內環之中。
4.3.5. 幾何要素編碼示例
4.3.5.1. 點要素示例
假設示例點的坐標為:
- (25, 17)
表示它只需要一條指令:
- MoveTo(+25, +17)
編碼 : [ 9 50 34 ]
| | `> 解碼: ((34 >> 1) ^ (-(34 & 1))) = +17
| `> 解碼: ((50 >> 1) ^ (-(50 & 1))) = +25
| ===== 相對地 MoveTo(+25, +17) == 創建點 (25,17)
`> [00001 001] = command id 1 (MoveTo), command count 1
4.3.5.2. 多點要素示例
假設多點要素的坐標為:
- (5,7)
- (3,2)
編碼需要兩條指令:
- MoveTo(+5,+7)
- MoveTo(-2,-5)
編碼 : [ 17 10 14 3 9 ]
| | | | `> 解碼: ((9 >> 1) ^ (-(9 & 1))) = -5
| | | `> 解碼: ((3 >> 1) ^ (-(3 & 1))) = -2
| | | === 相對地 MoveTo(-2, -5) == 創建點 (3,2)
| | `> 解碼: ((34 >> 1) ^ (-(34 & 1))) = +7
| `> 解碼: ((50 >> 1) ^ (-(50 & 1))) = +5
| ===== relative MoveTo(+25, +17) == 創建點 (25,17)
`> [00010 001] = command id 1 (MoveTo), command count 2
4.3.5.3. 線要素示例
假設示例線要素的坐標為:
- (2,2)
- (2,10)
- (10,10)
編碼需要3條指令:
- MoveTo(+2,+2)
- LineTo(+0,+8)
- LineTo(+8,+0)
編碼 : [ 9 4 4 18 0 16 16 0 ]
| | ==== 相對地 LineTo(+8, +0) == 連接到點 (10, 10)
| | ==== 相對地 LineTo(+0, +8) == 連接到點 (2, 10)
| `> [00010 010] = command id 2 (LineTo), command count 2
| === 相對地 MoveTo(+2, +2)
`> [00001 001] = command id 1 (MoveTo), command count 1
4.3.5.4. Example Multi Linestring
4.3.5.4. 多線要素示例
假設示例要素的坐標為:
- 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)
編碼 : [ 9 4 4 18 0 16 16 0 9 17 17 10 4 8 ]
| | | | === 相對地 LineTo(+2, +4) == 連接到點 (3,5)
| | | `> [00001 010] = command id 2 (LineTo), command count 1
| | | ===== 相對地 MoveTo(-9, -9) == 新建一條線從 (1,1)
| | `> [00001 001] = command id 1 (MoveTo), command count 1
| | ==== 相對地 LineTo(+8, +0) == 連接到點 (10, 10)
| | ==== 相對地 LineTo(+0, +8) == 連接到點 (2, 10)
| `> [00010 010] = command id 2 (LineTo), command count 2
| === 相對地 MoveTo(+2, +2)
`> [00001 001] = command id 1 (MoveTo), command count 1
4.3.5.5. 面要素示例
假設示例面要素的坐標為:
- (3,6)
- (8,12)
- (20,34)
- (3,6) 閉合
編碼需要以下指令:
- MoveTo(3, 6)
- LineTo(5, 6)
- LineTo(12, 22)
- ClosePath
編碼 : [ 9 6 12 18 10 12 24 44 15 ]
| | `> [00001 111] command id 7 (ClosePath), command count 1
| | ===== 相對地 LineTo(+12, +22) == 連接到點 (20, 34)
| | ===== 相對地 LineTo(+5, +6) == 連接到點 (8, 12)
| `> [00010 010] = command id 2 (LineTo), command count 2
| ==== 相對地 MoveTo(+3, +6)
`> [00001 001] = command id 1 (MoveTo), command count 1
4.3.5.6. 多面要素示例
示例要素包含兩個多邊形,其中一個多邊形有一個洞。多邊形中的點如下。注意,多邊形中的點環繞順序非常重要,應為這個順序被用來區別外環和內環。
- Polygon 1:
- 外環:
- (0,0)
- (10,0)
- (10,10)
- (0,10)
- (0,0) 閉合
- 外環:
- Polygon 2:
- 外環:
- (11,11)
- (20,11)
- (20,20)
- (11,20)
- (11,11) 閉合
- 內環:
- (13,13)
- (13,17)
- (17,17)
- (17,13)
- (13,13) 閉合
- 外環:
編碼需要以下一系列指令:
- MoveTo(+0,+0)
- LineTo(+10,+0)
- LineTo(+0,+10)
- LineTo(-10,+0) // 執行這條指令后,游標的位置在(0, 10)
- ClosePath // Polygon 1結束
- MoveTo(+11,+1) // 這條指令相對於上面最后一條LineTo指令!
- LineTo(+9,+0)
- LineTo(+0,+9)
- LineTo(-9,+0) // 執行這條指令后,游標的位置在(11, 20)
- ClosePath // 這是一個新面要素,因為面積為正
- MoveTo(+2,-7) // 這條指令相對於上面最后一條LineTo指令!
- LineTo(+0,+4)
- LineTo(+4,+0)
- LineTo(+0,-4) // 執行這條指令后,游標的位置在(17, 13)
- ClosePath // 這是一個內環,因為面積為負
4.4. 要素屬性
要素屬性被編碼為tag
字段中的一對對整數。在每對tag
中,第一個整數表示key在其所屬的layer
的keys
列表的中索引號(以0開始)。第二個整數表示value在其所屬的layer
的values
列表的中索引號(以0開始)。一個要素的所有key索引必須唯一,以保證要素中沒有重復的屬性項。每個要素的tag
字段必須為偶數。要素中的tag
字段包含的key索引號或value索引號必須不能大於或等於相應圖層中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
}
注意幾何要素的實際坐標取決於坐標系和瓦片的范圍。
MapBox MVT數據格式:https://github.com/jingsam/vector-tile-spec/blob/master/2.1/README_zh.md
矢量瓦片標准
本文檔中的“必須”、“必須不”、“必備”、"應該"、“不應該”、“建議”、“可以”、“可選”的含義參照RFC 2119。
1. 目標
本文檔規定了一種節省存儲空間的矢量瓦片數據編碼格式。這種格式應用於客戶端或服務端高效渲染或查詢要素信息。
2. 文件格式
矢量瓦片文件采用Google Protocol Buffers進行編碼。Google Protocol Buffers是一種兼容多語言、多平台、易擴展的數據序列化格式。
2.1. 文件后綴
矢量瓦片文件的后綴應該為mvt
。例如,vector.mvt
。
2.2 MIME類型
矢量瓦片的MIME類型應該設置為application/vnd.mapbox-vector-tile
。
3. 投影和范圍
矢量瓦片表示的是投影在正方形區塊上的數據。矢量瓦片不應該包含范圍和投影信息。解碼方被假定知道矢量瓦片的范圍和投影信息。
Web Mercator是默認的投影方式,Google tile scheme是默認的瓦片編號方式。兩者一起完成了與任意范圍、任意精度的地理區域的一一對應,例如https://example.com/17/65535/43602.mvt
。
矢量瓦片可以用來表示任意投影方式、任意瓦片編號方案的數據。
4. 內部結構
這部分內容描述矢量瓦片的數據結構。讀者需要先了解矢量瓦片protobuf編碼方案文件中的結構定義。
4.1. 圖層
矢量瓦片由一組命名的圖層構成。每個圖層包含幾何要素和元數據信息。設計的圖層格式能夠保證圖層數據能夠在內存中按順序排列,由此在圖層組末尾添加一個新的圖層就不用更改已有的數據。
每塊矢量瓦片應該至少包含一個圖層。每個圖層應該至少包含一個要素。
圖層必須包含一個version
字段表示此圖層所遵守的《矢量瓦片標准》的主版本號。例如,某個圖層遵守2.1版本的標准,那么它的version
字段的值則為整數2
。version
字段應該設定為圖層的第一個字段。解碼器應該首先解析version
字段,以確定是否能夠解析該版本的圖層。當遇到一個未知版本的矢量瓦片圖層時,解碼器可以嘗試去解析它,或者可以跳過該圖層。以上兩種情況下,解碼器都應該繼續解析后續的圖層。
圖層必須包含一個name
字段。每塊矢量瓦片必須不包含兩個或兩個以上的圖層具有相同name
值。在向一塊矢量瓦片添加一個新的圖層之前,編碼器必須檢查已有的name
值以防止重復。
圖層中的每個要素可以包含一個或多個key-value作為它的元數據(見下文)。所有要素的key和value被分別索引為兩個列表——keys
和values
——為圖層中的所有要素所共享。
圖層keys
字段的每個元素都是字符串。keys
字段包含了圖層中所有要素的key,並且每個key可以通過它在keys
列表中的索引號引用,第一個key的索引號是0 。keys
列表必須不包含兩個或兩個以上key是一樣的。
圖層values
字段的每個元素是多種類型的值的編碼(見下文)。values
字段包含了圖層中所有要素的value,並且每個value可以通過它在values
列表中的索引號引用,第一個value的索引號是0 。values
列表必須不包含兩個或兩個以上value是一樣的。
為了支持字符串型、布爾型、整型、浮點型多種類型的值,對value
字段的編碼包含了一組optional
字段。每個value必須包含其中的一個字段。
圖層必須包含一個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. 要素
每個要素必須包含一個geometry
字段。
每個要素必須包含一個type
字段,該字段將在幾何類型章節描述(4.3.4)。
每個要素可以包含一個tags
字段。如果存在屬於要素級別的元數據,應該存儲到tags
字段中。
每個要素可以包含一個id
字段。如果一個要素包含一個id
字段,那么id
字段的值應該相對於圖層中的其他要素是唯一的。
4.3. 幾何圖形編碼
矢量瓦片中的幾何數據被定義為屏幕坐標系。瓦片的左上角(顯示默認如此)是坐標系的原點。X軸向右為正,Y軸向下為正。幾何圖形中的坐標必須為整數。
幾何圖形被編碼為要素的geometry
字段的一個32位無符號型整數序列。每個整數是CommandInteger
或者ParameterInteger
。解碼器解析這些整數序列作為生成幾何圖形的一系列有序操作。
指令涉及到的位置是相對於“游標”的,即一個可重定義的點。對於要素中的第一條指令,游標在坐標系中的位置是(0,0)
。有些指定能夠移動游標,因而會影響到接下來執行的指令。
4.3.1. 指令數
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表示以下指令中的一種:
指令 | Id | 參數 | 參數個數 |
---|---|---|---|
MoveTo | 1 |
dX , dY |
2 |
LineTo | 2 |
dX , dY |
2 |
ClosePath | 7 |
無參數 | 0 |
指令數示例
指令 | ID | Count | CommandInteger | 二進制表示[Count][Id] |
---|---|---|---|---|
MoveTo | 1 |
1 |
9 |
[00000000 00000000 0000000 00001][001] |
MoveTo | 1 |
120 |
961 |
[00000000 00000000 0000011 11000][001] |
LineTo | 2 |
1 |
10 |
[00000000 00000000 0000000 00001][010] |
LineTo | 2 |
3 |
26 |
[00000000 00000000 0000000 00011][010] |
ClosePath | 7 |
1 |
15 |
[00000000 00000000 0000000 00001][111] |
4.3.2. 參數數
指令的所有參數緊跟在ParameterInteger
之后。跟在CommandInteger
之后的ParameterIntegers
個數等於指令所需要參數的個數乘以指令執行的次數。例如,一條指示MoveTo
指令執行3次的CommandInteger
之后會跟隨6個ParameterIntegers
。
ParameterInteger
由zigzag方式編碼得到,以使小負數和正數都被編碼為小整數。將參數值編碼為ParameterInteger
按以下公式轉換:
ParameterInteger = (value << 1) ^ (value >> 31)
參數值不支持大於pow(2,31) - 1
或-1 * (pow(2,31) - 1)
的數值。
以下的公式用來將ParameterInteger
解碼為實際值:
value = ((ParameterInteger >> 1) ^ (-(ParameterInteger & 1)))
4.3.3. 指令類型
以下關於指令的描述中,游標的初始位置定義為坐標(cX, cY)
,其中cX
指代游標在X軸上的位置,cY
指代游標在Y軸上的位置。
4.3.3.1. MoveTo指令
表示MoveTo
指令執行n
的ParameterInteger
必須立即接上n
對ParameterInteger
。對於(dX, dY)
參數:
- 定義坐標
(pX, pY)
,其中pX = cX + dX
和pY = cY + dY
。- 對於點要素,這個坐標定義了一個新的點要素。
- 對於線要素,這個坐標定義了一條新的線要素的起點。
- 對於面要素,這個坐標定義了一個新環的起點。
- 將游標移至
(pX, pY)
。
4.3.3.1. LineTo指令
表示LineTo
指令執行n
的ParameterInteger
必須立即接上n
對ParameterInteger
。對於(dX, dY)
參數:
- 定義一條以游標位置
(cX, cY)
為起點,(pX, pY)
為終點的線段,其中pX = cX + dX
和pY = cY + dY
。- 對於線要素,這條線段延長了當前線要素。
- 對於面要素,這條線段延長了當前環。
- 將游標移至
(pX, pY)
。
對於任意一對(dX, dY)
,dX
和dY
必須不能同時為0
.
4.3.3.3. ClosePath指令
每條ClosePath
指令必須只能執行一次並且無附帶參數。這條指令通過構造一條以游標(cX, cY)
為起點、當前環的起點為終點的線段,閉合面要素的當前環。
這條指定不改變游標的位置。
4.3.4. 幾何類型
要素geometry
字段的type
的取值必須是GeomType
枚舉值之一。支持的幾何類型如下:
- UNKNOWN
- POINT
- LINESTRING
- POLYGON
不支持GeometryCollection
類型。
4.3.4.1. Unknown幾何類型
本標准有意設置一個Unknown幾何類型。這種幾何類型可以用來編碼試驗性的幾何類型。解碼器可以選擇忽略這種幾何類型的要素。
4.3.4.2. Point幾何類型
POINT
幾何類型用來表示單點或多點幾何。每個點幾何的指令序列必須包含一個MoveTo
指令,並且該指令的command count大於0。
如果POINT
幾何的MoveTo
的command count為1,那么必須將其解析為單點;否則必須解析為多點,指令后面的每對ParameterInteger
表示一個單點。
4.3.4.3. Linestring幾何類型
LINESTRING
幾何類型用來表示單線或多線幾何。線幾何的指令序列必須包含一個或多個下列序列:
- 一個
MoveTo
指令,其command count為1 - 一個
LineTo
指令,其command count大於0
如果LINESTRING
的指令序列只包含1個MoveTo
指令,那么必須將其解析為單線;否則,必須將其解析為多線,其中的每個MoveTo
指令開始構造一條新線幾何。
4.3.4.4. Polygon幾何類型
POLYGON
幾何類型表示面或多面幾何,每個面有且只有一個外環和零個或多個內環。面幾何的指令序列包含一個或多個下列序列:
- 一個
ExteriorRing
- 零個或多個
InteriorRing
Each ExteriorRing
and InteriorRing
MUST consist of the following sequence: 每個ExteriorRing
和InteriorRing
必須包含以下序列:
- 一個
MoveTo
指令,其command count為1 - 一個
LineTo
指令,其command count大於1 - 一個
ClosePath
指令
一個外環被定義為一個線性的環,當應用surveyor's formula,以多邊形的節點在瓦片坐標系下的坐標計算面積時,其面積為正。在瓦片坐標系下(X向右為正,Y向下為正),外環節點以順時針旋轉。
一個內環被定義為一個線性的環,當應用surveyor's formula,以多邊形的節點在瓦片坐標系下的坐標計算面積時,其面積為負。在瓦片坐標系下(X向右為正,Y向下為正),內環節點以逆時針旋轉。
如果POLYGON
的指令序列只包含一個外環,那么必須將其解析為單面;否則,必須解析為多面幾何,其中每個外環表示一個新面的開始。如果面幾何包換內環,那么必須將其編碼到所屬的外環之后。
線性環必須不包含異常點,例如自相交或自相切。在ClosePath
之前的坐標不應該與線性環的起始點坐標相同,因為會產生零長度的線段。線性環經過surveyor's formula計算的面積不應該為0,因為這意味着環包含有異常點。
面幾何必須不能有內環相交,並且內環必須被包圍在內環之中。
4.3.5. 幾何要素編碼示例
4.3.5.1. 點要素示例
假設示例點的坐標為:
- (25, 17)
表示它只需要一條指令:
- MoveTo(+25, +17)
編碼 : [ 9 50 34 ]
| | `> 解碼: ((34 >> 1) ^ (-(34 & 1))) = +17
| `> 解碼: ((50 >> 1) ^ (-(50 & 1))) = +25
| ===== 相對地 MoveTo(+25, +17) == 創建點 (25,17)
`> [00001 001] = command id 1 (MoveTo), command count 1
4.3.5.2. 多點要素示例
假設多點要素的坐標為:
- (5,7)
- (3,2)
編碼需要兩條指令:
- MoveTo(+5,+7)
- MoveTo(-2,-5)
編碼 : [ 17 10 14 3 9 ]
| | | | `> 解碼: ((9 >> 1) ^ (-(9 & 1))) = -5
| | | `> 解碼: ((3 >> 1) ^ (-(3 & 1))) = -2
| | | === 相對地 MoveTo(-2, -5) == 創建點 (3,2)
| | `> 解碼: ((34 >> 1) ^ (-(34 & 1))) = +7
| `> 解碼: ((50 >> 1) ^ (-(50 & 1))) = +5
| ===== relative MoveTo(+25, +17) == 創建點 (25,17)
`> [00010 001] = command id 1 (MoveTo), command count 2
4.3.5.3. 線要素示例
假設示例線要素的坐標為:
- (2,2)
- (2,10)
- (10,10)
編碼需要3條指令:
- MoveTo(+2,+2)
- LineTo(+0,+8)
- LineTo(+8,+0)
編碼 : [ 9 4 4 18 0 16 16 0 ]
| | ==== 相對地 LineTo(+8, +0) == 連接到點 (10, 10)
| | ==== 相對地 LineTo(+0, +8) == 連接到點 (2, 10)
| `> [00010 010] = command id 2 (LineTo), command count 2
| === 相對地 MoveTo(+2, +2)
`> [00001 001] = command id 1 (MoveTo), command count 1
4.3.5.4. Example Multi Linestring
4.3.5.4. 多線要素示例
假設示例要素的坐標為:
- 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)
編碼 : [ 9 4 4 18 0 16 16 0 9 17 17 10 4 8 ]
| | | | === 相對地 LineTo(+2, +4) == 連接到點 (3,5)
| | | `> [00001 010] = command id 2 (LineTo), command count 1
| | | ===== 相對地 MoveTo(-9, -9) == 新建一條線從 (1,1)
| | `> [00001 001] = command id 1 (MoveTo), command count 1
| | ==== 相對地 LineTo(+8, +0) == 連接到點 (10, 10)
| | ==== 相對地 LineTo(+0, +8) == 連接到點 (2, 10)
| `> [00010 010] = command id 2 (LineTo), command count 2
| === 相對地 MoveTo(+2, +2)
`> [00001 001] = command id 1 (MoveTo), command count 1
4.3.5.5. 面要素示例
假設示例面要素的坐標為:
- (3,6)
- (8,12)
- (20,34)
- (3,6) 閉合
編碼需要以下指令:
- MoveTo(3, 6)
- LineTo(5, 6)
- LineTo(12, 22)
- ClosePath
編碼 : [ 9 6 12 18 10 12 24 44 15 ]
| | `> [00001 111] command id 7 (ClosePath), command count 1
| | ===== 相對地 LineTo(+12, +22) == 連接到點 (20, 34)
| | ===== 相對地 LineTo(+5, +6) == 連接到點 (8, 12)
| `> [00010 010] = command id 2 (LineTo), command count 2
| ==== 相對地 MoveTo(+3, +6)
`> [00001 001] = command id 1 (MoveTo), command count 1
4.3.5.6. 多面要素示例
示例要素包含兩個多邊形,其中一個多邊形有一個洞。多邊形中的點如下。注意,多邊形中的點環繞順序非常重要,應為這個順序被用來區別外環和內環。
- Polygon 1:
- 外環:
- (0,0)
- (10,0)
- (10,10)
- (0,10)
- (0,0) 閉合
- 外環:
- Polygon 2:
- 外環:
- (11,11)
- (20,11)
- (20,20)
- (11,20)
- (11,11) 閉合
- 內環:
- (13,13)
- (13,17)
- (17,17)
- (17,13)
- (13,13) 閉合
- 外環:
編碼需要以下一系列指令:
- MoveTo(+0,+0)
- LineTo(+10,+0)
- LineTo(+0,+10)
- LineTo(-10,+0) // 執行這條指令后,游標的位置在(0, 10)
- ClosePath // Polygon 1結束
- MoveTo(+11,+1) // 這條指令相對於上面最后一條LineTo指令!
- LineTo(+9,+0)
- LineTo(+0,+9)
- LineTo(-9,+0) // 執行這條指令后,游標的位置在(11, 20)
- ClosePath // 這是一個新面要素,因為面積為正
- MoveTo(+2,-7) // 這條指令相對於上面最后一條LineTo指令!
- LineTo(+0,+4)
- LineTo(+4,+0)
- LineTo(+0,-4) // 執行這條指令后,游標的位置在(17, 13)
- ClosePath // 這是一個內環,因為面積為負
4.4. 要素屬性
要素屬性被編碼為tag
字段中的一對對整數。在每對tag
中,第一個整數表示key在其所屬的layer
的keys
列表的中索引號(以0開始)。第二個整數表示value在其所屬的layer
的values
列表的中索引號(以0開始)。一個要素的所有key索引必須唯一,以保證要素中沒有重復的屬性項。每個要素的tag
字段必須為偶數。要素中的tag
字段包含的key索引號或value索引號必須不能大於或等於相應圖層中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
}
注意幾何要素的實際坐標取決於坐標系和瓦片的范圍。