本节我们讲一下Cesium中常用的测量工具和针对开发人员使用的调试工具。
量测工具
量测工具可以说,不管是二维GIS还是三维GIS中都必须具备的功能,只不过是在空间上是否有贴地、是否有高度上的距离差别之分。Cesium是三维GIS引擎,所以距离量测支持直线距离、水平距离、垂直距离以及地表距离,面积量测支持水平面积、地表面积以及模型表面积等。不管是哪种类型的距离测量还是面积测量,实现思路基本是一样的,都是按照如下思路实现的。
1)点击按钮开始测量,侦听鼠标LEFT_CLICK事件,记录坐标,绘制节点和折线(多边形);
2)侦听鼠标移动事件,鼠标点击后即复制一个浮动点,在MOUSE_MOVE事件中不断更新最后一个浮动点,动态更新折线(多边形)绘制;
3)侦听鼠标右击事件,RIGHT_CLICK触发时销毁测量相关事件句柄(ScreenSpaceEventHandler),删除多余的浮动点;
4)折线(多边形)的动态绘制通过CallbackProperty属性绑定positions属性实现。
如下为实现距离量测部分核心代码:
var handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
handler.setInputAction(function (movement) {
let ray = viewer.camera.getPickRay(movement.position);
cartesian = viewer.scene.globe.pick(ray, viewer.scene);
if (positions.length == 0) {
positions.push(cartesian.clone());
}
positions.push(cartesian);
//在三维场景中添加Label
var textDisance = distance + "米";
floatingPoint = viewer.entities.add({
name: "空间直线距离",
position: positions[positions.length - 1],
point: {
pixelSize: 5,
color: Cesium.Color.RED,
outlineColor: Cesium.Color.WHITE,
outlineWidth: 2,
},
label: {
text: textDisance,
font: "18px sans-serif",
fillColor: Cesium.Color.GOLD,
style: Cesium.LabelStyle.FILL_AND_OUTLINE,
outlineWidth: 2,
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
pixelOffset: new Cesium.Cartesian2(20, -20),
},
});
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
handler.setInputAction(function (movement) {
let ray = viewer.camera.getPickRay(movement.endPosition);
cartesian = viewer.scene.globe.pick(ray, viewer.scene);
if (positions.length >= 2) {
if (!Cesium.defined(poly)) {
poly = new PolyLinePrimitive(positions);
} else {
positions.pop();
positions.push(cartesian);
}
distance = getSpaceDistance(positions);
}
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
handler.setInputAction(function (movement) {
handler.destroy(); //关闭事件句柄
positions.pop(); //最后一个点无效
// viewer.entities.remove(floatingPoint);
// tooltip.style.display = "none";
}, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
//空间两点距离计算函数
function getSpaceDistance(positions) {
var distance = 0;
for (var i = 0; i < positions.length - 1; i++) {
var point1cartographic = Cesium.Cartographic.fromCartesian(
positions[i]
);
var point2cartographic = Cesium.Cartographic.fromCartesian(
positions[i + 1]
);
/**根据经纬度计算出距离**/
var geodesic = new Cesium.EllipsoidGeodesic();
geodesic.setEndPoints(point1cartographic, point2cartographic);
var s = geodesic.surfaceDistance;
//返回两点之间的距离
s = Math.sqrt(
Math.pow(s, 2) +
Math.pow(point2cartographic.height - point1cartographic.height, 2)
);
distance = distance + s;
}
return distance.toFixed(2);
}
如下为实现面积测量的部分核心代码:
handler.setInputAction(function (movement) {
let ray = viewer.camera.getPickRay(movement.position);
cartesian = viewer.scene.globe.pick(ray, viewer.scene);
if (positions.length == 0) {
positions.push(cartesian.clone());
}
positions.push(cartesian);
//在三维场景中添加点
var cartographic = Cesium.Cartographic.fromCartesian(
positions[positions.length - 1]
);
var longitudeString = Cesium.Math.toDegrees(cartographic.longitude);
var latitudeString = Cesium.Math.toDegrees(cartographic.latitude);
var heightString = cartographic.height;
tempPoints.push({
lon: longitudeString,
lat: latitudeString,
hei: heightString,
});
floatingPoint = viewer.entities.add({
name: "多边形面积",
position: positions[positions.length - 1],
point: {
pixelSize: 5,
color: Cesium.Color.RED,
outlineColor: Cesium.Color.WHITE,
outlineWidth: 2,
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
},
});
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
handler.setInputAction(function (movement) {
handler.destroy();
positions.pop();
var textArea = getArea(tempPoints) + "平方公里";
viewer.entities.add({
name: "多边形面积",
position: positions[positions.length - 1],
label: {
text: textArea,
font: "18px sans-serif",
fillColor: Cesium.Color.GOLD,
style: Cesium.LabelStyle.FILL_AND_OUTLINE,
outlineWidth: 2,
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
pixelOffset: new Cesium.Cartesian2(20, -40),
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
},
});
}, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
//计算多边形面积
function getArea(points) {
var res = 0;
//拆分三角曲面
for (var i = 0; i < points.length - 2; i++) {
var j = (i + 1) % points.length;
var k = (i + 2) % points.length;
var totalAngle = Angle(points[i], points[j], points[k]);
var dis_temp1 = distance(positions[i], positions[j]);
var dis_temp2 = distance(positions[j], positions[k]);
res += dis_temp1 * dis_temp2 * Math.abs(Math.sin(totalAngle));
console.log(res);
}
return (res / 1000000.0).toFixed(4);
}
完整代码请查看gitHub地址https://github.com/ls870061011/cesium_training/blob/main/examples/3_4_1measure.html,实现的示意图如下所示。
调试面板
Cesium中比较常用的调试面板是用于了解Cesium渲染效果以及性能调优的CesiumInspector和用于监视3D Tiles数据的监视器Cesium3DTilesInspector。
CesiumInspector
该控件是针对开发人员来说,虽然不能提供功能的实现,但对于了解渲染效果和性能调优是非常有帮助的,特别是解决一些渲染状态下的问题时非常的有价值。使用该控件非常的简单,只需如下一行代码就能实现该控件的加载。
viewer.extend(Cesium.viewerCesiumInspectorMixin);
控件里面有很多的功能,每一个都很专业,包括渲染帧数、Primitive外包围球、Primitive参考框架、线框模式等等,这里就不一一给大家介绍了,感兴趣的同学可以自己尝试一下。
Cesium3DTilesInspector
面对大场景下的大规模、大体量的3D Tiles数据,Cesium提供了一个监视3D Tiles数据的监视器,用于监视、观察3D Tiles数据的效果。加载该空间也非常的简单,只需一行简单代码即可,结果控件展示如下:
viewer.extend(Cesium.viewerCesium3DTilesInspectorMixin);
包括3D Tiles瓦片是否可拾取;显示颜色、线框、瓦片边界范围框、瓦片内容边界范围框、观察者请求体、点云渲染等;还包括一些动态屏幕误差设置、最大屏幕误差设置、样式修改等。对3D Tiles不熟悉的可阅读3D Tiles介绍及加载这篇文章。感兴趣的同学,可以亲自尝试此控件,体验一下控件的强大。