目錄javascript
Cesium的Camera案例,展現了其關於漫遊器鏡頭的控制,可以調整視圖的位置。這裏改進了一下這個實例,使之可以展現一些本身關注的興趣點的狀況,並總結遇到的問題。css
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no"> <meta name="description" content="Fly to a specified location or view a geographic rectangle."> <meta name="cesium-sandcastle-labels" content="Beginner, Tutorials, Showcases"> <title>Cesium Demo</title> <script type="text/javascript" src="../Build/Cesium/Cesium.js"></script> <style> @import url(../Build/Cesium/Widgets/widgets.css); html, body { width: 100%; height: 100%; margin: 0; padding: 0; overflow: hidden; font-family: sans-serif; background: #000; } .fullSize { display: block; position: absolute; top: 0; left: 0; border: none; width: 100%; height: 100%; } #toolbar { margin: 5px; padding: 2px 5px; position: absolute; } </style> </head> <body> <div id="cesiumContainer" class="fullSize"></div> <div id="toolbar"> <select id = "camera_select", class="cesium-button"> <option value="undefined"> 相機選項 </option> <option value="undefined"> 飛行至某一點——武漢大學 </option> <option value="undefined"> 飛行至某區域——武漢市 </option> <option value="undefined"> 設置相機點——華中科技大學 </option> <option value="undefined"> 設置相機區域——上海市 </option> <option value="undefined"> 從武大飛向華科 </option> </select> </div> <script src="Camera.js"></script> </body> </html>
這段代碼在數字地球展現組件的基礎上新添加了一個視圖控制的下拉列表框,選擇相應的選項可以將當前的視圖調整到對應的位置。html
//Add your ion access token from cesium.com/ion/ Cesium.Ion.defaultAccessToken = '你在Cesium申請的key'; var tdtKey = "你在天地圖申請的key"; 'use strict'; //默認BING影像地圖 var viewer = new Cesium.Viewer('cesiumContainer', { imageryProvider: Cesium.createWorldImagery({ style: Cesium.IonWorldImageryStyle.AERIAL }), baseLayerPicker: false }); //全球影像中文註記服務 var imageryLayers = viewer.scene.imageryLayers; var tdtAnnoLayer = imageryLayers.addImageryProvider(new Cesium.WebMapTileServiceImageryProvider({ url: "http://t0.tianditu.gov.cn/cva_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=cva&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={TileMatrix}&TILEROW={TileRow}&TILECOL={TileCol}&tk=" + tdtKey, layer: "tdtAnnoLayer", style: "default", format: "image/jpeg", tileMatrixSetID: "GoogleMapsCompatible" })); var camera_select = document.getElementById("camera_select"); if (camera_select) { camera_select.onchange = function gradeChange() { switch (camera_select.selectedIndex) { case 1: viewer.camera.flyTo({ destination: Cesium.Cartesian3.fromDegrees(114.35231209, 30.53542614, 5000.0), orientation: { heading: Cesium.Math.toRadians(0.0), pitch: Cesium.Math.toRadians(-90.0), roll: Cesium.Math.toRadians(0.0) } }); break; case 2: viewer.camera.flyTo({ destination: Cesium.Rectangle.fromDegrees(113.683333, 29.966667, 115.083333, 31.366667) }); break; case 3: viewer.camera.setView({ destination: Cesium.Cartesian3.fromDegrees(114.40782845, 30.51011682, 5000.0), orientation: { heading: Cesium.Math.toRadians(0.0), pitch: Cesium.Math.toRadians(-90.0), roll: Cesium.Math.toRadians(0.0) } }); break; case 4: viewer.camera.setView({ destination: Cesium.Rectangle.fromDegrees(120.86667, 30.66667, 122.2, 31.883333) }); break; case 5: { var whdxOptions = { destination: Cesium.Cartesian3.fromDegrees(114.35231209, 30.53542614, 5000.0), duration: 5, orientation: { heading: Cesium.Math.toRadians(0.0), pitch: Cesium.Math.toRadians(-90.0), roll: Cesium.Math.toRadians(0.0) } }; var hzkjdxOptions = { destination: Cesium.Cartesian3.fromDegrees(114.40782845, 30.51011682, 5000.0), orientation: { heading: Cesium.Math.toRadians(0.0), pitch: Cesium.Math.toRadians(-90.0), roll: Cesium.Math.toRadians(0.0) }, duration: 5, //flyOverLongitude: Cesium.Math.toRadians(60.0) }; whdxOptions.complete = function () { setTimeout(function () { viewer.camera.flyTo(hzkjdxOptions); }, 1000); }; // if (adjustPitch) { // tokyoOptions.pitchAdjustHeight = 1000; // laOptions.pitchAdjustHeight = 1000; // } viewer.camera.flyTo(whdxOptions); } break; default: break; } } }
這段代碼首先添加了Cesium.Viewer默認的Bing影像地圖和天地圖的中文標註;而後根據id獲取HTML頁面的下拉列表框控件camera_select;最後根據選項調整相應的相機視圖。這裏展現了幾種調整視圖的方式。java
設置相機鏡頭逐漸從當前位置飛行到某一點是經過Cesium.Camera的flyTo()函數實現的,其具體的函數定義以下:git
該函數傳入了鍵值對配置對象,其中destination、orientation這兩項,分別表示相機鏡頭的位置和姿態。本例中相應的代碼以下:框架
viewer.camera.flyTo({ destination: Cesium.Cartesian3.fromDegrees(114.35231209, 30.53542614, 5000.0), orientation: { heading: Cesium.Math.toRadians(0.0), pitch: Cesium.Math.toRadians(-90.0), roll: Cesium.Math.toRadians(0.0) } });
這段設置相機視圖的代碼意思就是,將相機的位置移動到經緯度位置(114.35231209, 30.53542614),離地面5000米的點;航向角(heading)設置爲0度,俯仰角(pitch)設置爲-90度,滾轉角(roll)設置爲0度。實際頁面的顯示效果爲逐漸飛往某一點:ide
此時數字地球會顯示在武漢大學附近,視線看上去會垂直與地面,而且東西南北方向也基本上與常規地圖一致。實際上當不設置姿態參數orientation只設置位置參數destination也能達到一樣的效果,說明(0.0,-90.0,0.0)的三個姿態角是設置相機視圖的默認值。在Cesium的設定中,heading、pitch、roll的定義以下:函數
這說明航向角(heading)是繞Z後負方向旋轉的角度,俯仰角(pitch)是繞Y軸負方向旋轉的角度,滾轉角(roll)是繞X軸正方向旋轉的角度。那麼問題來了,這個定義裏面的X、Y、Z軸的指的是什麼呢?我這裏認爲這個函數蘊含了一種視圖變換,使得基於相機的視空間座標系成爲一種相似於一種北東地站心座標系(NED)座標系,XYZ軸指的正是這個視空間座標系的XYZ軸。在這個視空間座標系中,Z軸垂直球面向下(Down),Y軸沿緯線指東(East),X軸沿經線向北(North),而位於視空間座標系原點的相機的姿態爲由南看向北。在這種狀況下,只須要使相機繞Y軸正向旋轉90度,也就是俯仰角(pitch)設爲90,就能夠獲得視線垂直於地圖,東西南北向正常的視圖。ui
flyTo()函數另一個頗有用的功能就是根據設定的範圍顯示視圖,這在顯示特定空間的視圖時特別有用,例如加載的三維模型的範圍,一個地區的範圍等等。實現也很很簡單,只須要給位置參數destination傳入一個Cesium.Rectangle對象便可:url
viewer.camera.flyTo({ destination: Cesium.Rectangle.fromDegrees(113.683333, 29.966667, 115.083333, 31.366667) });
將武漢市的經緯度範圍傳入,實際的顯示結果以下:
flyTo()函數還能夠傳入一個配置項complete,能夠給其設定一個飛行結束後再運行的函數,經過這個配置項能夠實現兩地或多地飛行:
var whdxOptions = { destination: Cesium.Cartesian3.fromDegrees(114.35231209, 30.53542614, 5000.0), duration: 5, orientation: { heading: Cesium.Math.toRadians(0.0), pitch: Cesium.Math.toRadians(-90.0), roll: Cesium.Math.toRadians(0.0) } }; var hzkjdxOptions = { destination: Cesium.Cartesian3.fromDegrees(114.40782845, 30.51011682, 5000.0), orientation: { heading: Cesium.Math.toRadians(0.0), pitch: Cesium.Math.toRadians(-90.0), roll: Cesium.Math.toRadians(0.0) }, duration: 5, //flyOverLongitude: Cesium.Math.toRadians(60.0) }; whdxOptions.complete = function () { setTimeout(function () { viewer.camera.flyTo(hzkjdxOptions); }, 1000); }; // if (adjustPitch) { // tokyoOptions.pitchAdjustHeight = 1000; // laOptions.pitchAdjustHeight = 1000; // } viewer.camera.flyTo(whdxOptions);
這段代碼分別定義了兩個飛行配置項whdxOptions和hzkjdxOptions,而且給whdxOptions的complete項配置了一個函數,表示完成1S以後,自動進行hzkjdxOptions的飛行。運行結果以下圖所示:
設置當前視圖經過setView()函數實現的,它跟flyTo()最大的不一樣是沒有持續時間,沒有飛行過程,是當即生效的。其具體的配置選項也比較類似,都是須要設置位置以及姿態:
viewer.camera.setView({ destination: Cesium.Cartesian3.fromDegrees(114.40782845, 30.51011682, 5000.0), orientation: { heading: Cesium.Math.toRadians(0.0), pitch: Cesium.Math.toRadians(-90.0), roll: Cesium.Math.toRadians(0.0) } });
設置具體的顯示範圍,也是當即生效,這兩個部分由於與flyTo()函數比較相似,就再也不具體講解了。
viewer.camera.setView({ destination: Cesium.Rectangle.fromDegrees(120.86667, 30.66667, 122.2, 31.883333) });
Cesium.Camera還提供了當前視圖發生變化的事件changed、視圖發生移動的事件moveStart/moveEnd,它們均可以經過addEventListener()給其添加相應的響應函數。
自帶案例Camera中還提供了另一種視圖控制方式:
function setReferenceFrame() { Sandcastle.declare(setReferenceFrame); var center = Cesium.Cartesian3.fromDegrees(-75.59777, 40.03883); var transform = Cesium.Transforms.eastNorthUpToFixedFrame(center); // View in east-north-up frame var camera = viewer.camera; camera.constrainedAxis = Cesium.Cartesian3.UNIT_Z; camera.lookAtTransform(transform, new Cesium.Cartesian3(-120000.0, -120000.0, 120000.0)); // Show reference frame. Not required. referenceFramePrimitive = scene.primitives.add(new Cesium.DebugModelMatrixPrimitive({ modelMatrix: transform, length: 100000.0 })); }
這段代碼的意思是選定一個經緯度的點,能夠計算出以該點爲中心的東北天(ENU)站心座標系與地心座標系的轉換矩陣,將這個矩陣傳入給Cesium.Camera的lookAtTransform函數,從而達到設置視圖的目的。可是這樣作會致使當前世界座標系發生變化,當前漫遊器的鍵鼠交互操做再也不以地心座標系原點爲中心,而以站心座標系的原點爲中心,致使這個時候的鍵鼠交互操做難以操做。
Cesium默認是基於ITRF,也就是國際地球地心參考框架。自帶案例還提供了一種將其轉換爲ICRF參考框架的視圖設置方式。關於ICRF我也不是很瞭解,查閱網上資料只知道是一種原點在太陽系的質心的天文參考框架,留待之後須要用到的時候再研究。
[1]. 北東地/東北天兩種導航座標系與姿態轉換
[2]. Cesium中的相機—HeadingPitchRoll
[3]. Cesium類HeadingPitchRoll及heading、pitch、roll等參數詳解