作为cesium入门示例级别的最后一篇,参考cesium-长度测量和面积测量实现测量工具封装,修改了其中的距离测量函数,计算贴地距离,并对事件内部处理做了调整。包括贴地距离测量、面积测量、结果清除。
实现思路(以距离测量为例):
1、点击按钮开始测量,侦听鼠标LEFT_CLICK事件,记录坐标,绘制节点和折线;
2、侦听鼠标移动事件,鼠标点击后即复制一个浮动点,在MOUSE_MOVE事件中不断更新最后一个浮动点,动态更新折线绘制;
3、侦听鼠标右击事件,RIGHT_CLICK触发时销毁测量相关事件句柄(ScreenSpaceEventHandler),删除多余的浮动点;
4、折线的动态绘制通过CallbackProperty属性绑定positions属性实现。
封装代码如下:
1 /* 2 * params: 3 * viewer:required,三维视图 4 * target:required,测量工具放置的div的id 5 * */ 6 7 var MeasureTool = (function() { 8 function _(option) { 9 this.viewer = option.viewer; 10 this.dom = document.getElementById(option.target); 11 this.options = option; 12 13 var me = this; 14 var btnDistance = document.createElement(\'button\'); 15 btnDistance.innerHTML = \'测量距离\'; 16 btnDistance.onclick = function() { 17 if(me.bMeasuring) 18 return; 19 20 me.bMeasuring = true; 21 me._measureLineSpace(); 22 }; 23 this.dom.appendChild(btnDistance); 24 25 var btnArea = document.createElement(\'button\'); 26 btnArea.innerHTML = \'测量面积\'; 27 btnArea.onclick = function() { 28 if(me.bMeasuring) 29 return; 30 31 me.bMeasuring = true; 32 me._measureAreaSpace(); 33 }; 34 this.dom.appendChild(btnArea); 35 36 var btnClear = document.createElement(\'button\'); 37 btnClear.innerHTML = \'清除结果\'; 38 btnClear.onclick = function() { 39 //删除事先记录的id 40 for(var jj = 0; jj < me.measureIds.length; jj++) { 41 me.viewer.entities.removeById(me.measureIds[jj]); 42 } 43 me.measureIds.length = 0; 44 }; 45 this.dom.appendChild(btnClear); 46 47 this.bMeasuring = false; 48 this.measureIds = []; 49 } 50 51 _.prototype._finishMeasure = function() { 52 this.bMeasuring = false; 53 } 54 55 //内部测量距离函数 56 _.prototype._measureLineSpace = function() {} 57 //内部测量面积函数 58 _.prototype._measureAreaSpace = function() {} 59 60 return _; 61 })();
调用方法:
1 <div style="position:absolute;width: 350px;height: 30px; top: 25px; left: 10px;"> 2 <div id="measure"> </div> 3 </div> 4 5 //创建测量工具 6 new MeasureTool({ 7 viewer: viewer, 8 target: \'measure\' 9 })
计算贴地距离时,对折线按距离或按比例切分成小线段,通过SampleTerrain函数统一获取各点的高度,分段计算空间距离叠加即为贴地距离,因该过程稍费时异步计算后再显示计算结果,测量折线贴地距离的代码如下:
1 //内部测量距离函数 2 _.prototype._measureLineSpace = function() { 3 var me = this; 4 var viewer = this.viewer; 5 // 取消双击事件-追踪该位置 6 viewer.cesiumWidget.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK); 7 8 var handler = new Cesium.ScreenSpaceEventHandler(viewer.scene._imageryLayerCollection); 9 var positions = []; 10 var poly = null; 11 var distance = 0; 12 var cartesian = null; 13 var floatingPoint; 14 var labelPt; 15 16 handler.setInputAction(function(movement) { 17 let ray = viewer.camera.getPickRay(movement.endPosition); 18 cartesian = viewer.scene.globe.pick(ray, viewer.scene); 19 if(!Cesium.defined(cartesian)) //跳出地球时异常 20 return; 21 if(positions.length >= 2) { 22 if(!Cesium.defined(poly)) { 23 poly = new PolyLinePrimitive(positions); 24 } else { 25 positions.pop(); 26 positions.push(cartesian); 27 } 28 } 29 }, Cesium.ScreenSpaceEventType.MOUSE_MOVE); 30 31 handler.setInputAction(function(movement) { 32 let ray = viewer.camera.getPickRay(movement.position); 33 cartesian = viewer.scene.globe.pick(ray, viewer.scene); 34 if(!Cesium.defined(cartesian)) //跳出地球时异常 35 return; 36 37 if(positions.length == 0) { 38 positions.push(cartesian.clone()); 39 } 40 positions.push(cartesian); 41 //记录鼠标单击时的节点位置,异步计算贴地距离 42 labelPt = positions[positions.length - 1]; 43 if(positions.length > 2) { 44 getSpaceDistance(positions); 45 } else if(positions.length == 2) { 46 //在三维场景中添加Label 47 floatingPoint = viewer.entities.add({ 48 name: \'空间距离\', 49 position: labelPt, 50 point: { 51 pixelSize: 5, 52 color: Cesium.Color.RED, 53 outlineColor: Cesium.Color.WHITE, 54 outlineWidth: 2, 55 } 56 }); 57 me.measureIds.push(floatingPoint.id); 58 } 59 60 }, Cesium.ScreenSpaceEventType.LEFT_CLICK); 61 62 handler.setInputAction(function(movement) { 63 handler.destroy(); //关闭事件句柄 64 handler = undefined; 65 positions.pop(); //最后一个点无效 66 if(positions.length == 1) 67 viewer.entities.remove(floatingPoint); 68 //记录测量工具状态 69 me._finishMeasure(); 70 71 }, Cesium.ScreenSpaceEventType.RIGHT_CLICK); 72 73 var PolyLinePrimitive = (function() { 74 function _(positions) { 75 this.options = { 76 name: \'直线\', 77 polyline: { 78 show: true, 79 positions: [], 80 material: Cesium.Color.CHARTREUSE, 81 width: 5, 82 clampToGround: true 83 } 84 }; 85 this.positions = positions; 86 this._init(); 87 } 88 89 _.prototype._init = function() { 90 var _self = this; 91 var _update = function() { 92 return _self.positions; 93 }; 94 //实时更新polyline.positions 95 this.options.polyline.positions = new Cesium.CallbackProperty(_update, false); 96 var addedEntity = viewer.entities.add(this.options); 97 me.measureIds.push(addedEntity.id); 98 }; 99 100 return _; 101 })(); 102 103 //空间两点距离计算函数 104 function getSpaceDistance(positions) { 105 //只计算最后一截,与前面累加 106 //因move和鼠标左击事件,最后两个点坐标重复 107 var i = positions.length - 3; 108 var point1cartographic = Cesium.Cartographic.fromCartesian(positions[i]); 109 var point2cartographic = Cesium.Cartographic.fromCartesian(positions[i + 1]); 110 getTerrainDistance(point1cartographic, point2cartographic); 111 } 112 113 function getTerrainDistance(point1cartographic, point2cartographic) { 114 var geodesic = new Cesium.EllipsoidGeodesic(); 115 geodesic.setEndPoints(point1cartographic, point2cartographic); 116 var s = geodesic.surfaceDistance; 117 var cartoPts = [point1cartographic]; 118 for(var jj = 1000; jj < s; jj += 1000) { //分段采样计算距离 119 var cartoPt = geodesic.interpolateUsingSurfaceDistance(jj); 120 // console.log(cartoPt); 121 cartoPts.push(cartoPt); 122 } 123 cartoPts.push(point2cartographic); 124 //返回两点之间的距离 125 var promise = Cesium.sampleTerrain(viewer.terrainProvider, 8, cartoPts); 126 Cesium.when(promise, function(updatedPositions) { 127 // positions height have been updated. 128 // updatedPositions is just a reference to positions. 129 for(var jj = 0; jj < updatedPositions.length - 1; jj++) { 130 var geoD = new Cesium.EllipsoidGeodesic(); 131 geoD.setEndPoints(updatedPositions[jj], updatedPositions[jj + 1]); 132 var innerS = geoD.surfaceDistance; 133 innerS = Math.sqrt(Math.pow(innerS, 2) + Math.pow(updatedPositions[jj + 1].height - updatedPositions[jj].height, 2)); 134 distance += innerS; 135 } 136 137 //在三维场景中添加Label 138 var textDisance = distance.toFixed(2) + "米"; 139 if(distance > 10000) 140 textDisance = (distance / 1000.0).toFixed(2) + "千米"; 141 floatingPoint = viewer.entities.add({ 142 name: \'贴地距离\', 143 position: labelPt, 144 point: { 145 pixelSize: 5, 146 color: Cesium.Color.RED, 147 outlineColor: Cesium.Color.WHITE, 148 outlineWidth: 2, 149 }, 150 label: { 151 text: textDisance, 152 font: \'18px sans-serif\', 153 fillColor: Cesium.Color.GOLD, 154 style: Cesium.LabelStyle.FILL_AND_OUTLINE, 155 outlineWidth: 2, 156 verticalOrigin: Cesium.VerticalOrigin.BOTTOM, 157 pixelOffset: new Cesium.Cartesian2(20, -20), 158 } 159 }); 160 me.measureIds.push(floatingPoint.id); 161 }); 162 } 163 164 }
测量面积实现方式与折线类似,可参考cesium-长度测量和面积测量自行实现,无需计算贴地距离。细节处还可优化。
最后实现的效果如下: