Cesium 测量距离
- 需求
- 分析
- 第一种方式:使用测距 Measure
- 第二中方式:使用 distance,自己封装
- 第三种方式:自己封装(样式不太好)
需求
实际开发中我们经常需要用到量测工具,而Cesium没有直接提供量测功能,如何在 Cesium 中实现两点间距离测量
分析
第一种方式:使用测距 Measure
在Cesium中,可以使用Measure
类进行测距操作。以下是测距的简单示例代码:
// 创建测量工具对象
var measure = new Cesium.Measure(viewer, {
lineColor: Cesium.Color.YELLOW, // 线颜色
lineOpacity: 1, // 线透明度
lineOutline: true, // 线是否显示轮廓
lineOutlineColor: Cesium.Color.BLACK, // 线轮廓颜色
labelFont: '16px sans-serif', // 标签字体
labelColor: Cesium.Color.BLACK, // 标签颜色
labelOffset: new Cesium.Cartesian2(0, 20), // 标签偏移
measureUnits: Cesium.MeasureUnits.METERS // 测量单位
});
// 开始测距
measure.startMeasure();
在按下鼠标左键开始绘制第一个点之后,测距工具将会创建一个用于显示测距结果的线段、标签等元素。测量结果将实时更新,直到鼠标左键抬起。
除了直接使用Measure
类进行测距外,还可以使用Cesium.MeasureHandler
类创建自定义的测距工具,这样可以根据具体需求进行更加详细的设置,例如支持多线段测量等。具体使用方法可以参考Cesium官方文档。
参考文献:
- https://cesium.com/docs/cesiumjs-ref-doc/Measure.html
- https://cesium.com/docs/cesiumjs-ref-doc/MeasureHandler.html
第二中方式:使用 distance,自己封装
- 预览效果
- 源码:
data(){
viewer: null,
handler: null,
positions : [],
entityCollection: [],
leftClickFlag:0,//单击Flag
centerPoint:[],//中点
lengthText:0 , // 计算距离
},
watch: {
leftClickFlag(val){
if( val!==1 && val %2 == 1){
this.addLabel(this.centerPoint, this.lengthText);
}
}
},
methods:{
//注册鼠标事件,调用该函数实现其他方法调用
registerEvents() {
this.leftClickEvent();
},
/**
* 添加标签
* @param position
* @param text
*/
addLabel(centerPoint, text) {
return this.viewer.entities.add(new Cesium.Entity({
position: centerPoint,
label: {
text: text,
font: '14px sans-serif',
style: Cesium.LabelStyle.FILL_AND_OUTLINE, //FILL FILL_AND_OUTLINE OUTLINE
fillColor: Cesium.Color.YELLOW,
showBackground: true, //指定标签后面背景的可见性
backgroundColor: new Cesium.Color(0.165, 0.165, 0.165, 0.8), // 背景颜色
backgroundPadding: new Cesium.Cartesian2(6, 6), //指定以像素为单位的水平和垂直背景填充padding
pixelOffset: new Cesium.Cartesian2(0, -25),
disableDepthTestDistance: Number.POSITIVE_INFINITY
}
}));
},
// 添加点
addPoint(position) {
return this.viewer.entities.add({
position: position,
point:{
pixelSize: 10,
color: Cesium.Color.YELLOW
}
});
},
// 添加线
addLine(position) {
const lonlat = [];
for (let index = 0; index < position.length; index++) {
const longitude = Cesium.Math.toDegrees(Cesium.Cartographic.fromCartesian(position[index]).longitude);
const latitude = Cesium.Math.toDegrees(Cesium.Cartographic.fromCartesian(position[index]).latitude);
lonlat.push(longitude,latitude);
}
console.log(lonlat)
return this.viewer.entities.add({
name: 'distance-line',
polyline: {
positions: Cesium.Cartesian3.fromDegreesArray(lonlat),
width: 2,
material: Cesium.Color.RED,
// clampToGround: true
}
});
},
//逻辑代码如下
getLengthText(firstPoint, secondPoint) {
// 计算距离
var length = Cesium.Cartesian3.distance(firstPoint, secondPoint);
if (length > 1000) {
length = (length / 1000).toFixed(2) + " 公里";
} else {
length = length.toFixed(2) + " 米";
}
return length;
},
//鼠标移动事件
mouseMoveEvent() {
var labelEntity = null; // 标签实体
this.viewer.screenSpaceEventHandler.setInputAction((moveEvent) => {
//var movePosition = this.viewer.scene.pickPosition(moveEvent.endPosition); // 鼠标移动的点
var movePosition = this.viewer.scene.globe.pick(this.viewer.camera.getPickRay(moveEvent.endPosition), this.viewer.scene);
if(!movePosition){
return false;
}
if (this.positions.length == 2) {
this.positions.pop();
this.positions.push(movePosition);
// 绘制label
if (labelEntity) {
this.viewer.entities.remove(labelEntity);
this.entityCollection.splice(this.entityCollection.indexOf(labelEntity), 1);
}
// 计算中点
this.centerPoint = Cesium.Cartesian3.midpoint(this.positions[0], this.positions[1], new Cesium.Cartesian3());
// 计算距离
this.lengthText = "距离:" + this.getLengthText(this.positions[0], this.positions[1]);
labelEntity = this.addLabel(this.centerPoint, this.lengthText);
this.entityCollection.push(labelEntity);
} else {
this.positions.push(movePosition);
// 绘制线
// this.addLine(this.positions);
}
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
},
//左键点击事件
leftClickEvent() {
//单击鼠标左键画点点击事件
this.viewer.screenSpaceEventHandler.setInputAction(e => {
this.leftClickFlag +=1;
this.viewer._element.style.cursor = 'default';
// 坐标
let position = this.viewer.scene.pickPosition(e.position);
// 存储第一个点
if (!position){
return false;
}
if (this.positions.length == 0) {
this.positions.push(position.clone())
//添加一个点
this.addPoint(position);
// 注册鼠标移动事件
this.mouseMoveEvent();
}else{
// 存储第二个点
this.positions.pop();
this.positions.push(position);
this.addPoint(position);
this.addLine(this.positions);
// 移出事件
// this.viewer.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
// this.viewer.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.MOUSE_MOVE);
// this.leftClickFlag = 0;
if(this.positions.length == 2){
this.positions.length = 0; //置空
this.leftClickFlag += 1;
}
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
},
mounted(){
this.viewer = window.viewer;
this.fetchData();
},
第三种方式:自己封装(样式不太好)
- 新建一个MeasureTool.js文件,代码如下:
import Cesium from "cesium/Cesium";
import widgets from "cesium/Widgets/widgets.css";
export default {
//测量空间直线距离
/******************************************* */
measureLineSpace(viewer, handler) {
// 取消双击事件-追踪该位置
viewer.cesiumWidget.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK);
handler = new Cesium.ScreenSpaceEventHandler(viewer.scene._imageryLayerCollection);
var positions = [];
var poly = null;
// var tooltip = document.getElementById("toolTip");
var distance = 0;
var cartesian = null;
var floatingPoint;
// tooltip.style.display = "block";
handler.setInputAction(function (movement) {
// tooltip.style.left = movement.endPosition.x + 3 + "px";
// tooltip.style.top = movement.endPosition.y - 25 + "px";
// tooltip.innerHTML = '<p>单击开始,右击结束</p>';
// cartesian = viewer.scene.pickPosition(movement.endPosition);
let ray = viewer.camera.getPickRay(movement.endPosition);
cartesian = viewer.scene.globe.pick(ray, viewer.scene);
//cartesian = viewer.scene.camera.pickEllipsoid(movement.endPosition, viewer.scene.globe.ellipsoid);
if (positions.length >= 2) {
if (!Cesium.defined(poly)) {
poly = new PolyLinePrimitive(positions);
} else {
positions.pop();
// cartesian.y += (1 + Math.random());
positions.push(cartesian);
}
distance = getSpaceDistance(positions);
// console.log("distance: " + distance);
// tooltip.innerHTML='<p>'+distance+'米</p>';
}
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
handler.setInputAction(function (movement) {
// tooltip.style.display = "none";
// cartesian = viewer.scene.camera.pickEllipsoid(movement.position, viewer.scene.globe.ellipsoid);
// cartesian = viewer.scene.pickPosition(movement.position);
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 cartographic = Cesium.Cartographic.fromCartesian(cartesian);
var textDisance = distance + "米";
// console.log(textDisance + ",lng:" + cartographic.longitude/Math.PI*180.0);
floatingPoint = viewer.entities.add({
name: '空间直线距离',
// position: Cesium.Cartesian3.fromDegrees(cartographic.longitude / Math.PI * 180, cartographic.latitude / Math.PI * 180,cartographic.height),
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) {
handler.destroy(); //关闭事件句柄
positions.pop(); //最后一个点无效
// viewer.entities.remove(floatingPoint);
// tooltip.style.display = "none";
}, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
var PolyLinePrimitive = (function () {
function _(positions) {
this.options = {
name: '直线',
polyline: {
show: true,
positions: [],
material: Cesium.Color.CHARTREUSE,
width: 10,
clampToGround: true
}
};
this.positions = positions;
this._init();
}
_.prototype._init = function () {
var _self = this;
var _update = function () {
return _self.positions;
};
//实时更新polyline.positions
this.options.polyline.positions = new Cesium.CallbackProperty(_update, false);
viewer.entities.add(this.options);
};
return _;
})();
//空间两点距离计算函数
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;
//console.log(Math.sqrt(Math.pow(distance, 2) + Math.pow(endheight, 2)));
//返回两点之间的距离
s = Math.sqrt(Math.pow(s, 2) + Math.pow(point2cartographic.height - point1cartographic.height, 2));
distance = distance + s;
}
return distance.toFixed(2);
}
},
//****************************测量空间面积************************************************//
measureAreaSpace(viewer, handler){
// 取消双击事件-追踪该位置
viewer.cesiumWidget.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK);
// 鼠标事件
handler = new Cesium.ScreenSpaceEventHandler(viewer.scene._imageryLayerCollection);
var positions = [];
var tempPoints = [];
var polygon = null;
// var tooltip = document.getElementById("toolTip");
var cartesian = null;
var floatingPoint;//浮动点
// tooltip.style.display = "block";
handler.setInputAction(function(movement){
// tooltip.style.left = movement.endPosition.x + 3 + "px";
// tooltip.style.top = movement.endPosition.y - 25 + "px";
// tooltip.innerHTML ='<p>单击开始,右击结束</p>';
// cartesian = viewer.scene.pickPosition(movement.endPosition);
let ray = viewer.camera.getPickRay(movement.endPosition);
cartesian = viewer.scene.globe.pick(ray, viewer.scene);
//cartesian = viewer.scene.camera.pickEllipsoid(movement.endPosition, viewer.scene.globe.ellipsoid);
if(positions.length >= 2){
if (!Cesium.defined(polygon)) {
polygon = new PolygonPrimitive(positions);
}else{
positions.pop();
// cartesian.y += (1 + Math.random());
positions.push(cartesian);
}
// tooltip.innerHTML='<p>'+distance+'米</p>';
}
},Cesium.ScreenSpaceEventType.MOUSE_MOVE);
handler.setInputAction(function(movement){
// tooltip.style.display = "none";
// cartesian = viewer.scene.pickPosition(movement.position);
let ray = viewer.camera.getPickRay(movement.position);
cartesian = viewer.scene.globe.pick(ray, viewer.scene);
// cartesian = viewer.scene.camera.pickEllipsoid(movement.position, viewer.scene.globe.ellipsoid);
if(positions.length == 0) {
positions.push(cartesian.clone());
}
//positions.pop();
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();
//tempPoints.pop();
// viewer.entities.remove(floatingPoint);
// tooltip.style.display = "none";
//在三维场景中添加点
// 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});
var textArea = getArea(tempPoints) + "平方公里";
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
// },
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 );
var radiansPerDegree = Math.PI / 180.0;//角度转化为弧度(rad)
var degreesPerRadian = 180.0 / Math.PI;//弧度转化为角度
//计算多边形面积
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);
}
/*角度*/
function Angle(p1, p2, p3) {
var bearing21 = Bearing(p2, p1);
var bearing23 = Bearing(p2, p3);
var angle = bearing21 - bearing23;
if (angle < 0) {
angle += 360;
}
return angle;
}
/*方向*/
function Bearing(from, to) {
var lat1 = from.lat * radiansPerDegree;
var lon1 = from.lon * radiansPerDegree;
var lat2 = to.lat * radiansPerDegree;
var lon2 = to.lon * radiansPerDegree;
var angle = -Math.atan2(Math.sin(lon1 - lon2) * Math.cos(lat2), Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(lon1 - lon2));
if (angle < 0) {
angle += Math.PI * 2.0;
}
angle = angle * degreesPerRadian;//角度
return angle;
}
var PolygonPrimitive = (function(){
function _(positions){
this.options = {
name:'多边形',
polygon : {
hierarchy : [],
// perPositionHeight : true,
material : Cesium.Color.GREEN.withAlpha(0.5),
// heightReference:20000
}
};
this.hierarchy = positions;
this._init();
}
_.prototype._init = function(){
var _self = this;
var _update = function(){
return _self.hierarchy;
};
//实时更新polygon.hierarchy
this.options.polygon.hierarchy = new Cesium.CallbackProperty(_update,false);
viewer.entities.add(this.options);
};
return _;
})();
function distance(point1,point2){
var point1cartographic = Cesium.Cartographic.fromCartesian(point1);
var point2cartographic = Cesium.Cartographic.fromCartesian(point2);
/**根据经纬度计算出距离**/
var geodesic = new Cesium.EllipsoidGeodesic();
geodesic.setEndPoints(point1cartographic, point2cartographic);
var s = geodesic.surfaceDistance;
//console.log(Math.sqrt(Math.pow(distance, 2) + Math.pow(endheight, 2)));
//返回两点之间的距离
s = Math.sqrt(Math.pow(s, 2) + Math.pow(point2cartographic.height - point1cartographic.height, 2));
return s;
}
}
}
- 引用MeasureTool.js文件,并调用测量方法:
<template>
<div id="cesiumContainer">
</div>
</template>
<script>
import MeasureTool from '../../public/js/MeasureTool'
export default {
name: 'CeLiang',
components: {},
data(){
return{
}
},
mounted() {
let viewer = new Cesium.Viewer('cesiumContainer');
//测量距离
MeasureTool.measureLineSpace(viewer, null);
//测量面积
MeasureTool.measureAreaSpace(viewer, null);
}
}
</script>
<style>
#cesiumContainer {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
}
</style>
- 效果