1 、空间量测
空间量测是在三维空间中测量距离、角度、面积等内容。实现方法为在屏幕中拾取对应点的位置,然后将屏幕坐标转换为地理坐标,再根据地球椭球参数,进行几何解算,获取地理空间距离、面积等。
1.1、 距离量测
两点之间的距离量测,核心代码:
function distanceCal(point1,point2){var cartographic1 = Cesium.Cartographic.fromCartesian(point1); var cartographic2 = Cesium.Cartographic.fromCartesian(point2); console.log(cartographic1);console.log(cartographic2);var geodesic = new Cesium.EllipsoidGeodesic();geodesic.setEndPoints(cartogrartographic2);//两点的贴地线距离(不计算高度差)var s =geodesic.surfaceDistance;return s;
}
思路是先获取两点的屏幕坐标,将其转为笛卡尔空间直角坐标,然后转成WGS-84。利用WGS-84椭球体得出两点的距离。
1.2、 面积量测
//计算多边形面积, S=[(x1*y2-y1*x2)+(x2*y3-y2*x3)+.....+(xn*y1*x1)]/2,
function getArea(points) {let s = 0;let p1 = 0;let p2 = 0;for (let i = 0; i < points.length; ++i){ p1 = points[i];let j = (i + 1) % points.length;p2 = points[j];s += p1.x * p2.y;s -= p2.x * p1.y;}return Math.abs(s/2/1000000.0).toFixed(6);
}
根据官网案例
https://sandcastle.cesium.com/?src=Drawing%20on%20Terrain.html
改造完成距离和面积测量功能,效果如下:
距离量测:
面积量测:
完整代码如下:
<div id="container"></div>
<div id="toolbar" style="display: inline;"><select id="spatial_cal" class="cesium-button"><option value="distance">距离量算</option><option value="area">面积量算</option></select><button id="calculate" class="cesium-button" onclick="cal();">计算</button><button id="clear" class="cesium-button" onclick="clearCal();">清空</button>
</div>
//创建点
function createPoint(windowPosition){var point = new Cesium.Entity({position: windowPosition, point:{pixelSize: 10, //点的大小color : Cesium.Color.WHITE,//点的颜色heightReference : Cesium.HeightReference.NONE, //高度参考,NONE表示绝对高程}
})viewer.entities.add(point);return point;
}var drawingMode = "polyline";
var activeShapePoints = []; //参与画图的点集合
var activeShape; //正在绘制的图形
var floatingPoint; //绘制图形时的跟踪点
var calPoints = [];
var calEntity;
var floatingPoints = [];
var label;
//监听下拉框的变化
var selectForm = document.getElementById("spatial_cal");
selectForm.addEventListener('change',function(){let select_val = selectForm.options[selectForm.selectedIndex].value;if(select_val == "distance"){drawingMode = "polyline";}else if(select_val == "area"){drawingMode = "polygon";}
});const handler = new Cesium.ScreenSpaceEventHandler(viewer.canvas);
//添加鼠标左键处理事件(在鼠标右键终止绘制后,将会禁用该事件)
function setBuildShapeHandler(flag){if(flag){handler.setInputAction(function (event) {const earthPosition = viewer.scene.pickPosition(event.position);// `earthPosition` will be undefined if our mouse is not over the globe.if (Cesium.defined(earthPosition)) {if (activeShapePoints.length === 0) {floatingPoint = createPoint(earthPosition);floatingPoints.push(floatingPoint);activeShapePoints.push(earthPosition);const dynamicPositions = new Cesium.CallbackProperty(function () {if (drawingMode === "polygon") {return new Cesium.PolygonHierarchy(activeShapePoints);}return activeShapePoints;}, false);activeShape = drawShape(dynamicPositions);}activeShapePoints.push(earthPosition);floatingPoint = createPoint(earthPosition);floatingPoints.push(floatingPoint);}}, Cesium.ScreenSpaceEventType.LEFT_CLICK);//添加鼠标移动处理事件handler.setInputAction(function (event) {if (Cesium.defined(floatingPoint)) {const newPosition = viewer.scene.pickPosition(event.endPosition);if (Cesium.defined(newPosition)) {floatingPoint.position.setValue(newPosition);activeShapePoints.pop();activeShapePoints.push(newPosition);}}}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);}else{handler.removeInputAction(Cesium.ScreenSpaceEventType.MOUSE_MOVE);handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);}
}
//默认开启
setBuildShapeHandler(true);//绘制图形
function drawShape(positionData)
{let shape;if(drawingMode === "polyline"){shape = viewer.entities.add({polyline: {positions: positionData,width: 5,material: Cesium.Color.RED,heightReference : Cesium.HeightReference.NONE,depthFailMaterial: Cesium.Color.RED, //被地形遮挡部分的颜色},});}else if(drawingMode === "polygon"){shape = viewer.entities.add({polygon: {hierarchy: positionData,material: new Cesium.ColorMaterialProperty(Cesium.Color.WHITE.withAlpha(0.7)),},});}return shape;
}
// Redraw the shape so it's not dynamic and remove the dynamic shape.
function terminateShape() {activeShapePoints.pop();calEntity = drawShape(activeShapePoints);calPoints = activeShapePoints;viewer.entities.remove(floatingPoint);viewer.entities.remove(activeShape);floatingPoint = undefined;activeShape = undefined;activeShapePoints = [];//绘制完一个图形后,移除鼠标左键和移动的监听事件,防止再次绘制。setBuildShapeHandler(false);
}
handler.setInputAction(function (event) {terminateShape();
}, Cesium.ScreenSpaceEventType.RIGHT_CLICK);//计算距离、面积、角度
window.cal = function(){let result = 0;if(drawingMode == "polyline"){result = distanceCal(calPoints);let text = "距离:"+result+"km";label = createLabel(calPoints[calPoints.length-1],text);}else{result = getArea(calPoints);let text = "面积:"+result+"km²";label = createLabel(calPoints[calPoints.length-1],text);}
}
//清空,注意viewer.scene.requestRenderMode为true时,不会立刻显示清空后的样子,只有当鼠标中键滚动或鼠标左键拖拽时才会渲染!!!
function clearCalculate()
{viewer.entities.remove(calEntity);viewer.entities.remove(label);for(let i=0;i<floatingPoints.length;++i){viewer.entities.remove(floatingPoints[i]);}floatingPoint = undefined;floatingPoints = [];activeShape = undefined;activeShapePoints = [];calEntity = undefined;calPoints = [];label = undefined;//清空后,再次添加鼠标左键和移动的监听事件,以便下一次测量。setBuildShapeHandler(true);
}
window.clearCal = function(){clearCalculate();
}function createLabel(position,text){const label = viewer.entities.add({position: position,label : {text : text,font : '20px sans-serif',style : Cesium.LabelStyle.FILL,backgroundColor : Cesium.Color.GRAY.withAlpha(0.3),showBackground : true,fillColor : Cesium.Color.YELLOW,heightReference : Cesium.HeightReference.CLAMP_GROUND, //高度参考,NONE表示绝对高程,horizontalOrigin : Cesium.HorizontalOrigin.LEFT,verticalOrigin : Cesium.VerticalOrigin.TOP,disableDepthTestDistance: Number.POSITIVE_INFINITY //解决了label被地形建筑遮挡的问题!}})return label;
}
//计算多边形面积, 微元法求面积,S=[(x1*y2-y1*x2)+(x2*y3-y2*x3)+.....+(xn*y1-yn*x1)]/2,
//但是结果不除以2才是真实面积,不知道为什么!
function getArea(points) {let s = 0;let p1 = 0;let p2 = 0;for (let i = 0; i < points.length; ++i){ p1 = points[i];let j = (i + 1) % points.length;p2 = points[j];s += p1.x * p2.y;s -= p2.x * p1.y;}return Math.abs(s/1000000.0).toFixed(6);
}
//计算两点之间的距离
function distanceBetweenTwoPoints(point1,point2){let cartographic1 = Cesium.Cartographic.fromCartesian(point1); let cartographic2 = Cesium.Cartographic.fromCartesian(point2); let geodesic = new Cesium.EllipsoidGeodesic();geodesic.setEndPoints(cartographic1,cartographic2);//这里得出来的是两个点的测地线距离let s =geodesic.surfaceDistance;return s;
}
//计算总长度
function distanceCal(points){let res = 0;for(let i=0;i<points.length-1;++i){res += distanceBetweenTwoPoints(points[i],points[i+1]);}return (res/1000.0).toFixed(3);
}