文章出處

原文:https://github.com/AnalyticalGraphicsInc/cesium/wiki/CZML-Structure

CZML是一種用來描述動態場景的JSON架構的語言,主要用于Cesium在瀏覽器中的展示。它可以用來描述點、線、布告板、模型以及其他的圖元,同時定義他們是怎樣隨時間變化的。Cesium擁有一套富客戶端API,通過CZML采用數據驅動的方式,不用寫代碼我就可以使用通用的Cesium viewer構建出豐富的場景。Cesium與CZML的關系就如同Google Earth和KML的關系。CZML和KML都是用來描述場景的數據格式,可以通到很多其他的程序自動生成,或者手寫也可以。CZML擁有很多的特性,其中有一些區別于KML的:

  1. CZML 是基于JSON的。
  2. CZML可以準確的描述值隨時間變化的屬性。理由,一條在某一時間內是紅色的而在另一時間內是藍色的。同時客戶端可以根據時間戳進行差值。加入有一輛車,分別定義了兩個不同時間的位置,通過CZML定義的差值算法,客戶端可以準確的顯示在這兩個時間點之間車的位置。所有屬性都可以是隨時間變化的。
  3. CZML通過增量流的方式傳送到客戶端。在場景顯示之前,整個CZML文檔需要首先被下載到客戶端。在某些情況下,個別客戶端可能會加入或離開正在傳輸的流。(這段翻譯的不好)
  4. CZML高度優化,旨在解析時更緊湊也更容易,讓人工的讀寫更容易。
  5. CZML可擴展,盡管CZML的主要作用在與虛擬地球客戶端程序與場景的交流,但它可以很容易的通過擴展來滿足其他一些輔助的程序對靜態或動態數據的需求。例如,隨時間動態變化在數據就可以用在某些2D的圖表程序中。
  6. CZML是一個開放的格式。我們希望有更多的程序能使用CZML,同時期待有一天它也能成為OGC一樣的標準。
  7. 可以通過czml-writer來生成CZML,這個程序維護在Github上。

我們將CZML標準以及它的相應實現分為4個部分:

CZML Structure --  CZML文檔的整體結構

CZML Content --內容

CZML in Cesium -- Cesium中解析和顯示CZML的流程

czml-writer-Architecture –czml-writer的架構

CZML Structure

CZML是json的一個子集,也就是說一個有效的CZML文檔同時也是一個有效的JSON文檔。特別的,一個CZML文檔包含一個JSON數組,數組中個每一個對象都是一個CZML數據包(packet),一個packet對應一個場景中的對象,例如一個飛機。

注:在下面的例子中我們使用javascript方式的注釋來幫助理解CZML,但在真實場景中是不允許的。

[
// packet one
{
"id":"GroundControlStation"
"position":{"cartographicDegrees":[-75.5,40.0,0.0]},
"point":{
"color":{"rgba":[0,0,255,255]},
}
},
// packet two
{
"id":"PredatorUAV",
// ...
}
]

每個packet都有一個id屬性用來標示我們當前描述的對象。id在同一個CZML以及與它載入同一個范圍(scope)內的其他CZML文件中必須是唯一的。隨后我們將討論scope的含義。

假如沒有指定id,那么客戶端將自動生成一個唯一的id。但是這樣的話在隨后的包中我們就沒有辦法引用它了,例如我們不能再往它里面添加數據。

除了id以為,一個包通常還包含0到多個(正常情況下是1到多個)定義對象圖形特征的屬性。正如上面的例子,我們定義了一個“GroundControlStation”對象,它擁有一個固定的經度-75.0維度40.0的WGS84坐標,高度為0,隨后一個藍色的點將會繪制在他的坐標位置處。

CZML還有很多標準的屬性,包括用來添加點、布告板、模型、線以及其他圖形到場景的屬性。所有這些屬性將在CZML Content這節討論。在這里我們主要討論這些數據是怎樣組織的。例如,我們怎樣定義一個屬性,使它在兩個不同的時間擁有兩個不同的值。

時間間隔Intervals

通常情況下CZML的屬性值是一個數組,數組中的每個元素對應每一個不同的時間,屬性的值。時間間隔的定義使用interval 屬性,通過ISO8601 interval格式的字面值表示。

{
"id":"myObject",
"someProperty":[
{
"interval":"2012-04-30T12:00:00Z/13:00:00Z",
"number":5
},
{
"interval":"2012-04-30T13:00:00Z/14:00:00Z",
"number":6
}
]
}

這里我們定義了一個someProperty屬性,它有兩個時間間隔,第一個是從中午到下午一點,屬性值為5,第二個是從下午一點到下午兩點,屬性值為6,在時間由第一個間隔變化到第二個間隔的時候,屬性值會瞬間從5變到6。注意,這里的時間為UTC(協調世界時),而我們國家通常使用的是UTC+8也就是北京時間,所以要注意。

我們使用number來表示屬性是一個數字類型的屬性。值得注意的是有些屬性允許通過不同的格式來定義,例如表示位置的屬性可以通過笛卡兒坐標的X、Y、Z表示,也可以通過經緯度和高程來表示。CZML Content一節將會詳細討論。

Interval屬性是可選的,你可以定義也可以不定義,如果沒有定義,默認為整個時間。通常定義多個無限的時間間隔或者間隔之間有重疊是沒什么意思的,如果你非要這么做,那么在CZML中最后定義的時間間隔將擁有較高的優先級。

通常情況下屬性值只跨越一個時間間隔,這時候你間隔列表可以省略。

{
"id":"myObject",
"someProperty":{
"interval":"2012-04-30T12:00:00Z/14:00:00Z",
"number":5
}
}

和之前的一樣,加入interval跨越整個時間,那么你可以省略掉它。對于一些簡單的值,例如上面我們使用的數字類型,加入它對于整個時間來說都不變,那么我還可以定義的更簡單一些。

{
"id":"myObject",
"someProperty":5
}

這種簡略的表示法,適用于所有值是簡單的json數據類型(string、number、boolean)的的屬性。

復合值Composite Values

一些復雜的值復合值例如笛卡爾坐標位置或顏色,是通過json數組的形式來表示。例如坐標位置,所使用的數組有三個元素,分別對應于坐標的x、y和z。

{
"id":"myObject",
"someComplexProperty":{
"cartesian":[1.0,2.0,3.0]
}
}

合成值必須定義在間隔中,即使他的時間間隔是無限的也是一樣,不能采用簡略的寫法。假如允許值[1.0,2.0,3.0]直接作為someComplexProperty的屬性值,那么客戶端代碼就需要解析CZML并且判斷數組里面的值到底是時間間隔列表(intervals list)還是說是簡單的值。為了簡單起見,我們不允許這樣的寫法。

屬性值采樣Sampled Property Values

到目前為止我們討論的了怎樣為那些橫跨整個時間的的屬性定義單一的值,以及針對離散的時間間隔定義不同的值。一些屬性(CZML Content一節將告訴你是哪些)允許你定義時間戳采樣,客戶端通過給定的時間差值計算出屬性的值。時間的定義是采用ISO8601標準字符串。

{
// ...  
"someInterpolatableProperty":{
"cartesian":[
"2012-04-30T12:00Z",1.0,2.0,3.0,
"2012-04-30T12:01Z",4.0,5.0,6.0,
"2012-04-30T12:02Z",7.0,8.0,9.0
]
}
}
在這里我們指定在中午的時候屬性值為[1.0,2.0,3.0],一分鐘后為[4.0,5.0,6.0],再過一分鐘后變為[7.0,8.0,9.0]。假如客戶端當前的時間是12點0分30秒,也就是離開始時間過去了30秒,那當前的屬性值將通過在[1.0, 2.0, 3.0][4.0, 5.0, 6.0]線性差值得出,為[2.5, 3.5, 4.5]。

簡單起見,時間使用距離起始時間(epoch)的秒數來表示。與每個時間都通過ISO8601字符串表示相比這樣不是很精確,不過對于采樣間隔在一天以內,或者偏移值時整數的時候這樣的精度還是綽綽有余。

{
// ...  
"someInterpolatableProperty":{
"epoch":"2012-04-30T12:00Z",
"cartesian":[
0.0,1.0,2.0,3.0,
60.0,4.0,5.0,6.0,
120.0,7.0,8.0,9.0
]
}
}

最后,使用時間戳采樣的屬性還有一些附加的可選的子屬性,用來控制采樣的方式。

{
// ...  
"someInterpolatableProperty":{
"epoch":"2012-04-30T12:00Z",
"cartesian":[
0.0,1.0,2.0,3.0,
60.0,4.0,5.0,6.0,
120.0,7.0,8.0,9.0
],
"interpolationAlgorithm":"LAGRANGE",
"interpolationDegree":5
},
}

interpolationAlgorithm定義了采樣使用的算法,下面的表格中列出了可能的值。interpolationDegree定義了用來插值所使用的多項式的次數,1表示線性差值,2表示二次插值法,默認為1.

每個采樣值的時間沒有必要落在包含它的時間間隔內。但是這些采樣值在他們的時間間隔內不會被使用。這樣的好處就是對于多次差值有更好的精度。

下表總結了可插值屬性的一些子屬性:

名稱

Scope 范圍

JSON類型

說明

epoch

Packet

string

使用ISO8601規范來表示日期和時間

nextTime

Packet

string or number

在時間間隔內下一個采樣的時間,可以通過ISO8061方式,也可以通過與epoch秒數來定義。它決定了不同packet之間的采樣是否有停頓。

previousTime

Packet

String or number

在時間間隔內前一個采樣的時間,可以通過ISO8061方式,也可以通過與epoch秒數來定義,它決定了不同packet之間的采樣是否有停頓。

InterpolationAlgorithm

Interval

String

用于插值的算法,有LAGTANGE,HERMITE和GEODESIC。默認是LAGRANGE。如果位置不在該采樣區間,那么這個屬性值會被忽略。

interpolationDegree

Interval

Number

定義了用來插值所使用的多項式的次數,1表示線性差值,2表示二次插值法,默認為1。如果使用GEODESIC插值算法,那么這個屬性將被忽略。 

EventSource and Streaming

如果將整個CZML文件安排在一個大的JSON數組中,這使增量加載變得很困難。雖然瀏覽器允許我們訪問沒有讀取完的流數據,但是解析不完整的數據需要漫長而繁瑣的字符串操作。

為了高效,CZML使用瀏覽器的server-sent events(EventSource)API來處理流數據。在實際操作中,每一個CZML的packet包會被作為單獨的一個事件傳輸到客戶端。

event: czml

data: { 

    // packet one 

} 

event: czml

data: { 

    // packet two 

}

當瀏覽器接收到一個packet后就會發出一個事件,事件中會包含剛剛接收到了數據。這樣我們就可以通過增量的方式高效的處理CZML數據。

目前為止,我們都是使用一個packet包來描述一個對象,這個packet包含了所有這個對象的圖形屬性。我們還可以使用其他的方式,例如一個CZML文件或流可以包含多個packet,每個packet都有相同的id,分別描述同一個對象的不同方面的屬性。

事實上在大多數情況下我們使用兩個packet來描述一個對象。當對象屬性跨越多個時間間隔,或者一個時間間隔有很多個時間戳采樣時,這樣做就很有用了。通過將一個屬性定義打包進多個packet,我們可以使數據更快的傳輸到Cesium中,減少用戶等待的時間。

當客戶端接收到一個packet,它會遍歷packet中的每一個屬性。對于每個屬性,它會遍歷屬性定義的每個時間間隔。對于每個時間間隔,它會判斷這個時間間隔是否已經定義,假如這個間隔已經定義,將更新已經存在的間隔,如果沒有定義,那么就根據這個間隔創建一個新的。

當更新一個已存在的時間間隔時,假如有子屬性,那么子屬性將覆蓋原有的值。有一個例外,就是當已有的屬性和新接收到的屬性都包含時間戳采樣時,新接收到的采樣不會覆蓋已有的,而是加到已有的采樣列表中。

當新的時間間隔與已有的發生重疊時,新的間隔擁有較高優先級,原有的間隔將被截斷或者整個移除。這點必須要牢記。

在同一個packet中的時間間隔的時間必須以增序排列,不同packet之間就沒有要求。但是對于不連續的采樣還是應該考慮合理的插值順序。

考慮一下,我們有一個需要插值的屬性,時間是0到10秒,間隔為1.0秒。第一個packet包含0到3秒,第二個包含8到10秒。在客戶端還沒有接收到包含4到7秒的packet時,我們可以渲染時間為5的場景嗎??

一種方式是我們就是使用已經接收到的兩個packet來插值。這可能不太好,因為即使我們使用高次插值,在這個兩個packet的間隙中得出的值可能也是錯的。所以,我們最好還是先暫停等待中間的那個packet趕快到來。但是我們是怎么知道兩個包之間有間隙的呢??我們可以猜想,剛開始的時候間隔是1秒,后來變成了5秒,再后來又變回了1秒,所以中間應該還有一個包。這個猜想只是逗你玩的,萬一人家中間就是想少采樣幾個點呢。

 這都不是事兒,CZML提供了previousTime和nextTime子屬性,用來處理這種情況。

{ 

    // ... 

    "someInterpolatableProperty": { 

        "epoch": "2012-04-30T12:00:00Z", 

        "cartesian": [ 

            0.0, 1.0, 2.0, 3.0, 

            1.0, 4.0, 5.0, 6.0, 

            2.0, 7.0, 8.0, 9.0,

            3.0, 10.0, 11.0, 12.0

        ], 

        "previousTime": -1.0, 

        "nextTime": 4.0 

    } 

}

它的作用是告訴客戶端3.0后下一個時間是4.0,就像我們上面舉的那個例子,3的后面是8,根據nextTime我們就知道3和8之間肯定還有一段數據沒有接收到,所以在開始插值之前我們就需要先等待數據讀取完成。

沒有必要同時設置previousTime和nextTime,在不同的情況下選擇使用其中最方便的一個就可以了。只要定義其中的一個,在進行插值前Cesium就會首先對數據進行完整性檢查。

Availability屬性

除了id屬性外,CZML的packet還有一個特別的額外屬性availability。

{ 

    "id": "PredatorUAV", 

    "availability": "2012-04-30T12:00:00Z/14:00:00Z", 

    // ... 

}

它用來標示一個對象的數據在什么時候是可用的。假如一個對象在當前的動畫時間內是可用,但是客戶端現在還沒有獲取到相應的數據(可能在下一packet里面,但現在還沒有獲取到),那么Cesium就會先暫停,直到獲取到數據為止。這個屬性的值可以一個字符串表示的一個時間段,也可以是一個字符串數組表示的多個時間段。

假如availability變化了或者被發現是不正確的,那么隨后的packet將會更新它的值。例如,一個SGP4 propagator可能總是可用的,但是隨后他發出了一個異常,所以他的值需要調整。如果availability屬性沒有定義,那么默認是全部時間內都可用的。Availability的范圍被限定到一個特定的CZML流中,所以對同一個對象在兩個不同的流中可以有不同的availability。在一個流中,只有定義在最后的那個availability起作用,其他的都會被忽略。在某一時刻,如果一個對象是可用的,那么這個對象至少要有一個可用的屬性并且在此時間段內需要的屬性都要有定義(也就是獲取到了數據),不然Cesium就會等待數據直到接收到數據為止。

擴展Extending CZML

可以給CZML增加自定義屬性,但是為避免沖突,我們強烈建議你給你的自定義屬性加上你特有的前綴。


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜

    大師兄 發表在 痞客邦 留言(0) 人氣()