地圖渲染技術(2)瓦片矢量數據格式


 

    地圖經過切割后形成了瓦片,每一個編號對於一個瓦片,瓦片可以是柵格數據也可以是矢量數據,柵格數據就是一張正方形圖片,渲染時只需要將這張圖片作為紋理貼到指定位置就行可以,柵格瓦片的缺點就是數據量大,縮放時失真,不能自由配置顯示樣式。這些缺點在矢量瓦片中都得以解決,矢量瓦片中使用矢量數據描述地圖元素,通過渲染這些矢量數據形成地圖,數據量很小,縮放的時候不會失真,可以在不同視角展示,可以展示更豐富的高度信息,例如可以拔高建築物。

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字段的值則為整數2version字段應該設定為圖層的第一個字段。解碼器應該首先解析version字段,以確定是否能夠解析該版本的圖層。當遇到一個未知版本的矢量瓦片圖層時,解碼器可以嘗試去解析它,或者可以跳過該圖層。以上兩種情況下,解碼器都應該繼續解析后續的圖層。

圖層必須包含一個name字段。每塊矢量瓦片必須不包含兩個或兩個以上的圖層具有相同name值。在向一塊矢量瓦片添加一個新的圖層之前,編碼器必須檢查已有的name值以防止重復。

圖層中的每個要素可以包含一個或多個key-value作為它的元數據(見下文)。所有要素的key和value被分別索引為兩個列表——keysvalues——為圖層中的所有要素所共享。

圖層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個比特位表示,即0pow(2, 29) - 1

command ID、command count、和CommandInteger三者可以通過以下位運算相互轉換。

CommandInteger = (id & 0x7) | (count << 3)
id = CommandInteger & 0x7
count = CommandInteger >> 3

每個command ID表示以下指令中的一種:

指令 Id 參數 參數個數
MoveTo 1 dXdY 2
LineTo 2 dXdY 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

ParameterIntegerzigzag方式編碼得到,以使小負數和正數都被編碼為小整數。將參數值編碼為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指令執行nParameterInteger必須立即接上nParameterInteger。對於(dX, dY)參數:

  1. 定義坐標(pX, pY),其中pX = cX + dXpY = cY + dY
    • 對於點要素,這個坐標定義了一個新的點要素。
    • 對於線要素,這個坐標定義了一條新的線要素的起點。
    • 對於面要素,這個坐標定義了一個新環的起點。
  2. 將游標移至(pX, pY)
4.3.3.1. LineTo指令

表示LineTo指令執行nParameterInteger必須立即接上nParameterInteger。對於(dX, dY)參數:

  1. 定義一條以游標位置(cX, cY)為起點,(pX, pY)為終點的線段,其中pX = cX + dXpY = cY + dY
    • 對於線要素,這條線段延長了當前線要素。
    • 對於面要素,這條線段延長了當前環。
  2. 將游標移至(pX, pY)

對於任意一對(dX, dY)dXdY必須不能同時為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幾何類型用來表示單線或多線幾何。線幾何的指令序列必須包含一個或多個下列序列:

  1. 一個MoveTo指令,其command count為1
  2. 一個LineTo指令,其command count大於0

如果LINESTRING的指令序列只包含1個MoveTo指令,那么必須將其解析為單線;否則,必須將其解析為多線,其中的每個MoveTo指令開始構造一條新線幾何。

4.3.4.4. Polygon幾何類型

POLYGON幾何類型表示面或多面幾何,每個面有且只有一個外環和零個或多個內環。面幾何的指令序列包含一個或多個下列序列:

  1. 一個ExteriorRing
  2. 零個或多個InteriorRing

Each ExteriorRing and InteriorRing MUST consist of the following sequence: 每個ExteriorRingInteriorRing必須包含以下序列:

  1. 一個MoveTo指令,其command count為1
  2. 一個LineTo指令,其command count大於1
  3. 一個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在其所屬的layerkeys列表的中索引號(以0開始)。第二個整數表示value在其所屬的layervalues列表的中索引號(以0開始)。一個要素的所有key索引必須唯一,以保證要素中沒有重復的屬性項。每個要素的tag字段必須為偶數。要素中的tag字段包含的key索引號或value索引號必須不能大於或等於相應圖層中keysvalues列表中的元素數目。

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字段的值則為整數2version字段應該設定為圖層的第一個字段。解碼器應該首先解析version字段,以確定是否能夠解析該版本的圖層。當遇到一個未知版本的矢量瓦片圖層時,解碼器可以嘗試去解析它,或者可以跳過該圖層。以上兩種情況下,解碼器都應該繼續解析后續的圖層。

圖層必須包含一個name字段。每塊矢量瓦片必須不包含兩個或兩個以上的圖層具有相同name值。在向一塊矢量瓦片添加一個新的圖層之前,編碼器必須檢查已有的name值以防止重復。

圖層中的每個要素可以包含一個或多個key-value作為它的元數據(見下文)。所有要素的key和value被分別索引為兩個列表——keysvalues——為圖層中的所有要素所共享。

圖層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個比特位表示,即0pow(2, 29) - 1

command ID、command count、和CommandInteger三者可以通過以下位運算相互轉換。

CommandInteger = (id & 0x7) | (count << 3)
id = CommandInteger & 0x7
count = CommandInteger >> 3

每個command ID表示以下指令中的一種:

指令 Id 參數 參數個數
MoveTo 1 dXdY 2
LineTo 2 dXdY 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

ParameterIntegerzigzag方式編碼得到,以使小負數和正數都被編碼為小整數。將參數值編碼為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指令執行nParameterInteger必須立即接上nParameterInteger。對於(dX, dY)參數:

  1. 定義坐標(pX, pY),其中pX = cX + dXpY = cY + dY
    • 對於點要素,這個坐標定義了一個新的點要素。
    • 對於線要素,這個坐標定義了一條新的線要素的起點。
    • 對於面要素,這個坐標定義了一個新環的起點。
  2. 將游標移至(pX, pY)
4.3.3.1. LineTo指令

表示LineTo指令執行nParameterInteger必須立即接上nParameterInteger。對於(dX, dY)參數:

  1. 定義一條以游標位置(cX, cY)為起點,(pX, pY)為終點的線段,其中pX = cX + dXpY = cY + dY
    • 對於線要素,這條線段延長了當前線要素。
    • 對於面要素,這條線段延長了當前環。
  2. 將游標移至(pX, pY)

對於任意一對(dX, dY)dXdY必須不能同時為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幾何類型用來表示單線或多線幾何。線幾何的指令序列必須包含一個或多個下列序列:

  1. 一個MoveTo指令,其command count為1
  2. 一個LineTo指令,其command count大於0

如果LINESTRING的指令序列只包含1個MoveTo指令,那么必須將其解析為單線;否則,必須將其解析為多線,其中的每個MoveTo指令開始構造一條新線幾何。

4.3.4.4. Polygon幾何類型

POLYGON幾何類型表示面或多面幾何,每個面有且只有一個外環和零個或多個內環。面幾何的指令序列包含一個或多個下列序列:

  1. 一個ExteriorRing
  2. 零個或多個InteriorRing

Each ExteriorRing and InteriorRing MUST consist of the following sequence: 每個ExteriorRingInteriorRing必須包含以下序列:

  1. 一個MoveTo指令,其command count為1
  2. 一個LineTo指令,其command count大於1
  3. 一個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在其所屬的layerkeys列表的中索引號(以0開始)。第二個整數表示value在其所屬的layervalues列表的中索引號(以0開始)。一個要素的所有key索引必須唯一,以保證要素中沒有重復的屬性項。每個要素的tag字段必須為偶數。要素中的tag字段包含的key索引號或value索引號必須不能大於或等於相應圖層中keysvalues列表中的元素數目。

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
}

注意幾何要素的實際坐標取決於坐標系和瓦片的范圍。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM