本文爲你們介紹幾個Cesium的Demo,經過這幾個Demo可以對如何使用Cesium有進一步的瞭解,並能充分理解Cesium的強大之處和新功能。其餘的無需多言,若是還不太瞭解什麼是Cesium,能夠參見個人另外兩篇關於Cesium的博客,下面直接進入正題。css
在Cesium中能夠很方便的監聽前臺HTML控件,相似C#等語言中的MVVM。html
前臺控件效果以下:前端
代碼以下:json
<div id="toolbar"> <div>SRTM</div> <input min="0" max="100" step="1" data-bind="value: srtm, valueUpdate: 'input'" type="range"> <input size="5" data-bind="value: srtm" type="text"> <div>SLOPE</div> <input min="0" max="100" step="1" data-bind="value: slope, valueUpdate: 'input'" type="range"> <input size="5" data-bind="value: slope" type="text"> <div>Type</div> <select data-bind="options: types, optionsText: 'name', value: selectedType, optionsCaption: 'Choose a Type...'"></select> </div>
首先建立一個div,js監測此div中的控件,重要的是id。canvas
在此div中建立input,一個或多個input對應js中的一個變量,固然此多個input之間也是相互綁定的關係。如:app
<input min="0" max="100" step="1" data-bind="value: srtm, valueUpdate: 'input'" type="range"> <input size="5" data-bind="value: srtm" type="text">
此兩者均對應js端的srtm變量,第一個是range類型,表明一個slide控件,第二個是一個文本框,兩者相互聯動,只選擇其中一個控件也是能夠的。重要的是data-bind屬性中value後的變量名稱需與js中對應。ide
固然也能夠綁定一個下拉列表框:函數
<select data-bind="options: types, optionsText: 'name', value: selectedType, optionsCaption: 'Choose a Type...'"></select>
這裏就對應了js中的兩個變量:types和selectedType。前者表明全部的可選列表及其值,後者表明選擇的結果。this
首先建立一個viewModel對象,裏面包含上述建立的各個變量,以下:url
var viewModel = { srtm: 10, slope: 5, types: [{ name: 'type1', values: '100' }, { name: 'type2', values: '200' } ], selectedType: undefined };
然後對此變量進行監控並綁定到前臺的相應控件:
Cesium.knockout.track(viewModel); // 跟蹤此Model var toolbar = document.getElementById('toolbar'); // 獲取前端監控div Cesium.knockout.applyBindings(viewModel, toolbar); // 綁定監控
這樣就能夠監聽控件的變化事件:
Cesium.knockout.getObservable(viewModel, 'srtm').subscribe(function(value) { ... });
能夠對此值進行處理好比發送到後臺或者請求相應的瓦片圖層等等。不過下拉列表框的狀況稍微複雜點:
Cesium.knockout.getObservable(viewModel, 'selectedType').subscribe(function(options) { var values = options.values; ... });
其實也就是多了一步,在定義types的時候除了name變量咱們還定義了values變量,此處就須要經過options.values來取出此值,其餘不變。
這是Cesium 1.4.0版新添加的功能,因此必定要更新到此版本。只須要正確加載地形瓦片,Cesium能夠自動算出高程設色瓦片、坡度設色瓦片以及等高線。其實也不難理解,地形瓦片中包含了空三等信息,根據這些信息天然可以計算出高度圖、坡度圖以及等高線,先來看效果:
加載地形瓦片圖層無需多言,前面已經有過介紹:
viewer.terrainProvider = new Cesium.CesiumTerrainProvider({ url : 'https://assets.agi.com/stk-terrain/v1/tilesets/world/tiles', requestWaterMask : true, requestVertexNormals : true });
而後就能夠開始計算高程設色瓦片和坡度設色瓦片以及等高線,固然此塊涉及到的東西太多,我只能憑藉我粗淺的理解簡單介紹,若有錯誤,望批評指正:
首先來看一下生成等高線:
var contourUniforms = {}; material = Cesium.Material.fromType('ElevationContour'); contourUniforms = material.uniforms; contourUniforms.width = 1; contourUniforms.spacing = 500; contourUniforms.color = Cesium.Color.RED;
很簡單的幾行代碼,其中Cesium.Material.fromType函數定義以下:
Material.fromType = function(type, uniforms) { if (!defined(Material._materialCache.getMaterial(type))) { throw new DeveloperError('material with type \'' + type + '\' does not exist.'); } var material = new Material({ fabric : { type : type } }); if (defined(uniforms)) { for (var name in uniforms) { if (uniforms.hasOwnProperty(name)) { material.uniforms[name] = uniforms[name]; } } } return material; };
此函數返回一個Material對象,根據ElevationContour能夠知道這是一個等高線類型的材質。uniforms是glsl着色器語言中的變量,用於控制對象顏色、位置等等。因此此處能夠簡單理解爲獲得ElevationContour類型的unifrom值並將此值做用於場景。Cesium根據此uniform生成相應類型的等高線。
理解了這一點,高程設色和坡度設色也就明白了。
高程設色以下:
var shadingUniforms = {}; material = Cesium.Material.fromType('ElevationRamp'); shadingUniforms = material.uniforms; shadingUniforms.minHeight = -414.0; shadingUniforms.maxHeight = 8777;
坡度設色以下:
var shadingUniforms = {}; material = Cesium.Material.fromType('SlopeRamp'); shadingUniforms = material.uniforms;
兩者都須要爲shadingUniforms變量添加一個色表:
shadingUniforms.image = getColorRamp(selectedShading); var elevationRamp = [0.0, 0.045, 0.1, 0.15, 0.37, 0.54, 1.0]; var slopeRamp = [0.0, 0.29, 0.5, Math.sqrt(2)/2, 0.87, 0.91, 1.0]; function getColorRamp(selectedShading) { var ramp = document.createElement('canvas'); ramp.width = 100; ramp.height = 1; var ctx = ramp.getContext('2d'); var values = selectedShading === 'elevation' ? elevationRamp : slopeRamp; var grd = ctx.createLinearGradient(0, 0, 100, 0); grd.addColorStop(values[0], '#000000'); //black grd.addColorStop(values[1], '#2747E0'); //blue grd.addColorStop(values[2], '#D33B7D'); //pink grd.addColorStop(values[3], '#D33038'); //red grd.addColorStop(values[4], '#FF9742'); //orange grd.addColorStop(values[5], '#ffd700'); //yellow grd.addColorStop(values[6], '#ffffff'); //white ctx.fillStyle = grd; ctx.fillRect(0, 0, 100, 1); return ramp; }
對高程和坡度歸一化後的值設置顏色。這樣就能夠正常顯示高程設色和坡度設色。
不是簡單的兩個圖層疊加,而是真實的分割整個地圖,左右顯示兩個不一樣的瓦片圖層。效果以下:
首先添加兩個圖層,第一個建立Viewer的時候設置基礎圖層,第二個採用layers.addImageryProvider的方式添加(固然也能夠兩個都採用此種方式添加),具體添加圖層的方式參考前面的博客。
layer1 = layers.addImageryProvider(...); layer2 = layers.addImageryProvider(...);
只須要設置layer1或則layer2的splitDirection屬性便可:
layer2.splitDirection = Cesium.ImagerySplitDirection.LEFT;
固然還須要設置圖層分割的位置:
viewer.scene.imagerySplitPosition = 0.5;
能夠改變此值來改變左右圖層的分割位置,0.5表示在中間。若是須要動態調整分割位置則須要加一個分割器,監聽位置變化事件。總體代碼以下:
前臺:
<!--css--> #slider { position: absolute; left: 50%; top: 0px; background-color: #D3D3D3; width: 5px; height: 100%; z-index: 9999; } #slider:hover { cursor: ew-resize; } <!--html--> <div id="cesiumContainer"> <div id="slider"></div> </div>
後臺:
var viewer = new Cesium.Viewer('cesiumContainer', { baseLayerPicker: false, imageryProvider: new Cesium.ArcGisMapServerImageryProvider({ url : 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer' }) }); var layers = viewer.imageryLayers; var balckMarble = layers.addImageryProvider(Cesium.createTileMapServiceImageryProvider({ url : 'https://cesiumjs.org/blackmarble', credit : 'Black Marble imagery courtesy NASA Earth Observatory', flipXY : true })); balckMarble.splitDirection = Cesium.ImagerySplitDirection.LEFT; var slider = document.getElementById('slider'); viewer.scene.imagerySplitPosition = (slider.offsetLeft) / slider.parentElement.offsetWidth; var handler = new Cesium.ScreenSpaceEventHandler(slider); var moveActive = false; function move(movement) { if(!moveActive) { return; } var relativeOffset = movement.endPosition.x ; var splitPosition = (slider.offsetLeft + relativeOffset) / slider.parentElement.offsetWidth; slider.style.left = 100.0 * splitPosition + '%'; viewer.scene.imagerySplitPosition = splitPosition; } handler.setInputAction(function() { moveActive = true; }, Cesium.ScreenSpaceEventType.LEFT_DOWN); handler.setInputAction(function() { moveActive = true; }, Cesium.ScreenSpaceEventType.PINCH_START); handler.setInputAction(move, Cesium.ScreenSpaceEventType.MOUSE_MOVE); handler.setInputAction(move, Cesium.ScreenSpaceEventType.PINCH_MOVE); handler.setInputAction(function() { moveActive = false; }, Cesium.ScreenSpaceEventType.LEFT_UP); handler.setInputAction(function() { moveActive = false; }, Cesium.ScreenSpaceEventType.PINCH_END);
Cesium自帶了geocoder控件,能夠檢索並定位到某個地址,原理很簡單,就是後臺解析此地址,根據解析結果將地圖切換到該位置。Cesium默認採用的是微軟Bing地址解析引擎,若是咱們想要換成其餘的如OSM或者咱們本身的,只須要對此控件簡單改造便可。示例代碼以下:
/** * This class is an example of a custom geocoder. It provides geocoding through the OpenStreetMap Nominatim service. * @alias OpenStreetMapNominatimGeocoder * @constructor */ function OpenStreetMapNominatimGeocoder() { } /** * The function called to geocode using this geocoder service. * * @param {String} input The query to be sent to the geocoder service * @returns {Promise<GeocoderResult[]>} */ OpenStreetMapNominatimGeocoder.prototype.geocode = function (input) { var endpoint = 'https://nominatim.openstreetmap.org/search?'; var query = 'format=json&q=' + input; var requestString = endpoint + query; return Cesium.loadJson(requestString) //請求url獲取json數據 .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() });
首先建立了一個OpenStreetMapNominatimGeocoder類,併爲其添加了geocode方法,在此方法中根據輸入拼接請求url,解析結果取出經緯度、名稱等內容。這樣就實現了咱們本身的地名解析器,其實這就是C#等語言中的父類和繼承的關係。
本文介紹了幾個Cesium的案例,都是一些比較有意思和好玩的功能,後續若是蒐集到其餘好玩的使用案例,一樣也會總結放出。