在剛開始的入門篇中,我們講過 Cesium 小元件的顯隱,感興趣的小夥伴可以檢視我前面寫過的文章 Cesium初始化介面介紹及相關小元件顯隱 。那問題來了,這些元件除了能控制顯隱,是否能在不修改原始碼的基礎上對其進行擴充套件重寫呢?答案是當然可以!但並不是所有的元件都能重寫,本節主要講解在不修改原始碼的基礎上,如何對以下三個常用的控制元件進行修改,以達到快速實現實際中業務需求的功能。當然,如果您對 Cesium 原始碼研究得比較深入,也可以透過修改原始碼的方式實現。
homeButton元件homeButton 功能在實際的應用場景中很常見而且功能也很實用,該元件的主要功能是返回到系統初始化時的位置。預設是整個球的位置,如下圖:
1)修改相機的預設矩形範圍
Cesium.Camera.DEFAULT_VIEW_RECTANGLE = Cesium.Rectangle.fromDegrees( 110.15, 34.54, 110.25, 34.56 ); //Rectangle(west, south, east, north)
2)在 homeButton 的 viewModel 中新增監聽事件
if (viewer.homeButton) { viewer.homeButton.viewModel.command.beforeExecute.addEventListener( function (e) { e.cancel = true; //你要飛的位置 viewer.camera.flyTo({ destination: Cesium.Cartesian3.fromDegrees(117.16, 32.71, 15000.0), }); } ); }
Geocoder元件Geocoder 是地理編碼的意思,我們常用的 POI 搜尋就是就是 Geocoder 的功勞。透過檢視 Cesium 原始碼(Source/Widgets/Geocoder/GeocoderViewModel.js 第73行),我們發現Cesium 預設採用的是 Bing 地圖服務來實現地理編碼的功能,並且是透過 geocode 方法實現的。那麼我們就可以透過覆寫 geocoder 方法的方式來實現自定義的地理編碼服務,下面我們重寫geocode方法,將 Cesium 預設的 Bing 地圖服務改為OSM地圖服務。
function OpenStreetMapNominatimGeocoder() {} OpenStreetMapNominatimGeocoder.prototype.geocode = function (input) { var url = "https://nominatim.openstreetmap.org/search"; var resource = new Cesium.Resource({ url: url, queryParameters: { format: "json", q: input, }, }); return resource.fetchJson().then(function (results) { var bboxDegrees; return results.map(function (resultObject) { bboxDegrees = resultObject.boundingbox; return { displayName: resultObject.display_name, destination: Cesium.Rectangle.fromDegrees( bboxDegrees[2], bboxDegrees[0], bboxDegrees[3], bboxDegrees[1] ), }; }); }); }; var viewer = new Cesium.Viewer("cesiumContainer", { geocoder: new OpenStreetMapNominatimGeocoder(), });
BaseLayerPicker元件Cesium為我們提供了預設的底圖、地形圖的選擇面板,透過修改baseLayerPicker的屬性ture或false來控制顯隱,透過選擇面板中的底圖或地形圖來實現對應圖層的切換與顯示。Cesum 提供的預設選擇面板如下圖所:
這些圖層都是線上的資源,如果是離線環境,或者是隻顯示客戶提供的幾個圖層資料,我們該如何實現呢。要實現這個功能,首先,我們看一下 BaseLayerPicker 的主要邏輯關係圖,如下圖。
從上圖我們可以看出,對於開發者而言,要實現不同的ImageryProvider,只需要提供不同的ProviderViewModel,比如BingMap、OSM、ArcGIS、GoogleMaps的,這樣在BaseLayerPicker的UI中,就會有多個Provider供使用者選擇,而互動則由BaseLayerPickerViewModel類負責,使用者並不需要關心內部的實現,BaseLayerPickerViewModel類已經幫我們都實現了。下面我們利用 BaseLayerPicker 的邏輯關係,實現自定義的 ImageryProvider(高德向量圖)和 TerrainPovider(ArcGIS地形),並將其顯示在選擇器面板中。下面為核心程式碼和結果截圖。
// 自定義影像圖層 var imageProviderVMs = []; let gaodeImageProvider = new Cesium.UrlTemplateImageryProvider({ url: "http://webrd0{s}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}", subdomains: ["1", "2", "3", "4"], }); var gaodeVM = new Cesium.ProviderViewModel({ name: "高德向量", iconUrl: Cesium.buildModuleUrl( "Widgets/Images/ImageryProviders/openStreetMap.png" ), tooltip: "高德向量 地圖服務", creationFunction: function () { return gaodeImageProvider; }, }); imageProviderVMs.push(gaodeVM); viewer.baseLayerPicker.viewModel.imageryProviderViewModels = imageProviderVMs;
// 自定義地形圖層 var terrainProviderVMs = []; var terrainProvider = new Cesium.ArcGISTiledElevationTerrainProvider({ url: "https://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer", token: "KED1aF_I4UzXOHy3BnhwyBHU4l5oY6rO6walkmHoYqGp4XyIWUd5YZUC1ZrLAzvV40pR6gBXQayh0eFA8m6vPg..", }); var arcgisVM = new Cesium.ProviderViewModel({ name: "ArcGIS地形", iconUrl: Cesium.buildModuleUrl( "Widgets/Images/TerrainProviders/Ellipsoid.png" ), tooltip: "ArcGIS地形服務", creationFunction: function () { return terrainProvider; }, }); terrainProviderVMs.push(arcgisVM); viewer.baseLayerPicker.viewModel.terrainProviderViewModels = terrainProviderVMs;
獲取完整程式碼,可檢視本人 GitHub 地址https://github.com/ls870061011/cesium_training/tree/main/examples 中的3_1部分。