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);
}