假設這樣一個場景,用戶在Cesium球上加載了一個GeoJson文件(DataSource),里面是全美國所有州的Geometry信息(Entity),疊加到球面后,你自然會有一種沖動,點擊某一個州,了解這個州的基本信息。場景如下圖所示:
這個點擊行為,對應的是選擇控件(SelectionIndicator),而呈現信息的載體,就是信息框控件(InfoBox)。
如上是一個簡單的邏輯關系,可見Viewer.SelectEntity屬性起到了承上啟下的作用。首先,Viewer模塊負責UI的事件交互,比如鼠標點擊或者雙擊,則會觸發對應事件,判斷當前的鼠標事件是否選中了Entity,如果選中,則更新Viewer.SelectEntity屬性。
如上,可以看到在Cesium源碼中,目前綁定了兩個事件,一個是鼠標左鍵單擊,選中該Entity,一個是鼠標左鍵雙擊,則會追蹤到該位置。當然,如果你想要增加或者修改某個事件,則可以修改screenSpaceEventHandler的內容。可以參考 screenSpaceEventHandler,比如很多人希望取消雙擊效果,則可以調用:viewer.cesiumWidget.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK);
而無論是單擊還是雙擊,內部都是通過pickEntity獲取選中的Entity,該方法內部通過ID作為唯一標識,判斷是否選中某一Entity,具體實現我們以后在詳細討論。而在單擊中,會調用Viewer.SelectEntity的Set方法,此時,當狀態發生變化時(選中或消失),都會觸發選擇控件的動畫效果:
如上就是一個Entity從鼠標選中,事件的觸發以及屬性信息的顯示的大致過程。
但Cesiu支持實時性的InfoBox狀態更新,也就是說,在每一幀,我們都可以更新選擇控件和信息框控件的狀態,而這,就要從Viewer.prototype._onTick說起。
首先,Viewer.prototype._onTick由Clock.onTick事件觸發,而時鍾本身在每一幀都會觸發onTick事件,因此Viewer也可以通過onTick來實現自身的更新。
機智的你一眼就看到了time參數,原來如此。這時,根據time時間,我們獲取對應的description,字符串類型,本身就是一個div,然后賦給infoBoxViewModel.description進而實現InfoBox控件的實時更新。
通常,每一個Entity的description都是固定內容,不需要根據時間等變換,而如果對實時性有一定要求,或者需要自定義效果,則需要匿名函數來實現回調方法。在這看不見的地方,主要是createPropertyDescriptor,Property兩者間錯綜復雜的關系…
如上是Entity中description的屬性定義,通過createPropertyDescriptor把Entity. description封裝成Property,提供了Set和Get方法,方便該屬性的調用。這里createPropertyDescriptor會根據屬性賦值來選擇具體的Property子類來滿足不同需求,稍后在介紹完Property在詳細解釋這一部分。
對於Property,則會調用getValueOrDefault方法,獲取該Entity的description,獲取每一個Entity對應的描述信息,這里,每一個Property都需要提供一個getValue的標准方法,這也是每一個Property子類必須要實現的方法:
而Property有很多子類,但在InfoBox中,主要涉及到CallbackProperty和ConstantProperty兩種類型,通過命名我們不難看出,前者是一種回調方式,而后者則是一個常量。
好了,對Property有了一個基本認識后,我們在看看createPropertyDescriptor的具體實現:
高亮處代碼,不難看出,在創建時優先考慮CallbackProperty的屬性,如果用戶沒有采用這種屬性,此時,value為String類型而不是Property,所以沒有實現value. getValue方法,則將value(String)封裝為ConstantProperty。所以,constant常量的方式非常簡單,直接賦值即可:
Entity.description = “<div> description</div>”;
可我偏要用回調的方式,如何破?我們看看GeoJsonDataSource中是如何實現的。
首先,你要實現一個匿名函數,如上,實現一個describe方法,其中,因為是匿名函數,所以可以隨時獲取到該Entity的所有屬性的Key-Value,可以根據自己的需要來創建對應的DIV,另外,該匿名函數也提供time參數,滿足對實時性的需求。
如果,萬事俱備,我們就可以開心的創建一個CallbackProperty了:
而CallbackProperty的getValue的實現如下,通過該方法,可以把Entity的description屬性,通過CallbackProperty,以RunTime的方式傳遞給InfoBox,如此,通過CallbackProperty可以實現動態顯示InfoBox信息的方式:
基本上,InfoBox的實現大致如此,可以看到,Cesium的InfoBox的還是很成熟的,盡可以的在無編碼的情況下顯示Entity的屬性,同時,也能滿足自定義的擴展,並能夠滿足實時性的要求,而且封裝的很優雅,也很簡單,真的不是隨便就能寫來的……
光說不練假把式,寫了這么多,相見ifoBox.html,,基於本文涉及到的知識點,實現了自定義InfoBox內容,並可以通過chart實現動態效果。具體代碼參考infobox.html范例