前言
鼠标左键添加点、右键完成绘制,单击右侧弹窗关闭按钮清空绘制。参考沙盒示例:Drawing on Terrain
直接上代码了
/*
* @Date: 2023-07-12 18:47:18
* @LastEditors: ReBeX 420659880@qq.com
* @LastEditTime: 2023-07-16 16:26:19
* @FilePath: \cesium-tyro-blog\src\utils\Entity\Draw\polyline.js
* @Description: 绘制多段线
*/
import { viewer } from '@/utils/createCesium.js' // 引入地图对象
import * as Cesium from 'cesium'
export class PolylineDrawer {
activeLine // 动态线
activePoint // 动态点
constructor(callback) {
if (!PolylineDrawer.instance) { // 首次使用构造器实例
this.callback = callback
// 新建DataSource用来管理entities
this.nodeCollection = new Cesium.CustomDataSource("nodeEntityCollection");
this.lineCollection = new Cesium.CustomDataSource("lineEntityCollection");
viewer.dataSources.add(this.nodeCollection);
viewer.dataSources.add(this.lineCollection);
this.addHandler = this.createScreenSpaceEventHandler(); // 新增点位的交互句柄
this.finHandler = this.createScreenSpaceEventHandler(); // 完成点选的交互句柄
this.moveHandler = this.createScreenSpaceEventHandler(); // 完成点选的交互句柄
viewer.cesiumWidget.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK); // 关闭左键双击事件
PolylineDrawer.instance = this // 将this挂载到PolylineDrawer这个类的instance属性上
}
return PolylineDrawer.instance // 返回单例
}
// 返回交互句柄
createScreenSpaceEventHandler() {
return new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
}
// 开始绘制
start() {
this.activePoint = this.createCursorPoint({ x: 0, y: 0, z: 0 }); // 默认显示动态点
this.activePoint.position.setValue(undefined); // 隐藏指针点
let pointList = []; // 初始化当前的线坐标数组
// 绘制打点时的事件
this.addHandler.setInputAction(event => {
// 获取地形表面经纬度和高度
const ray = viewer.camera.getPickRay(event.position || event.endPosition);
const cartesian = viewer.scene.globe.pick(ray, viewer.scene);
// // 获取椭球体表面的经纬度
// const cartesian = viewer.camera.pickEllipsoid(event.position || event.endPosition, viewer.scene.globe.ellipsoid);
if (Cesium.defined(cartesian)) {
this.nodeCollection.entities.add(this.createNodePoint(cartesian)); // 添加节点
// 绘制动态线:首次点击后触发
if (pointList.length === 0) {
pointList.push(cartesian) // 加入一个动态点
const dynamicPositions = new Cesium.CallbackProperty(() => pointList.slice(-2), false);
this.activeLine = this.createActiveLine(dynamicPositions); // 添加动态线
}
// 绘制线:点击2次后触发
if (pointList.length === 1) {
const dynamicPositions = new Cesium.CallbackProperty(() => pointList.slice(0, -1), false);
this.lineCollection.entities.add(this.createNormalLine(dynamicPositions)) // 绘制线
}
pointList.push(cartesian);
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
// 鼠标移动时的事件
this.moveHandler.setInputAction(event => {
if (Cesium.defined(this.activePoint)) {
// 获取地形表面经纬度和高度
const ray = viewer.camera.getPickRay(event.endPosition || event.position);
const cartesian = viewer.scene.globe.pick(ray, viewer.scene);
// // 获取椭球体表面的经纬度
// const cartesian = viewer.camera.pickEllipsoid(event.position || event.endPosition, viewer.scene.globe.ellipsoid);
if (Cesium.defined(cartesian)) {
this.activePoint.position.setValue(cartesian);
if (pointList.length > 0) {
pointList.pop();
pointList.push(cartesian);
}
} else {
this.activePoint.position.setValue(undefined); // 指针超出地球外了就隐藏指针点
}
}
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
// 完成绘制时的事件
this.finHandler.setInputAction(event => {
if (pointList.length < 2) { // 一个节点都没添加
alert('请至少选2个点')
} else if (pointList.length < 3) { // 如果点击了一次,就会马上创建点和线,那么就需要清除掉最末的entity,否则会污染数据集
alert('请至少选2个点')
this.nodeCollection.entities.remove(this.nodeCollection.entities.values[this.nodeCollection.entities.values.length - 1]);
this.lineCollection.entities.remove(this.lineCollection.entities.values[this.lineCollection.entities.values.length - 1]);
}
this.stop()
this.start()
}, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
}
// 结束绘制
stop() {
this.addHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK)
this.moveHandler.removeInputAction(Cesium.ScreenSpaceEventType.MOUSE_MOVE)
this.finHandler.removeInputAction(Cesium.ScreenSpaceEventType.RIGHT_CLICK)
viewer.entities.remove(this.activeLine); // 移除动态线
viewer.entities.remove(this.activePoint); // 移除动态点
this.callback && this.callback(this.lineCollection) // 如果需要,就把线集合给回调函数
}
// 绘制:动态点
createCursorPoint(cartesian, show) {
const point = viewer.entities.add({
position: cartesian,
point: {
pixelSize: 5, // 像素大小,默认: 1
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND, // 表示相对于地形的位置
color: Cesium.Color.SKYBLUE, // 默认: 白
disableDepthTestDistance: Number.POSITIVE_INFINITY,
},
});
return point;
}
// 绘制:节点
createNodePoint(cartesian) {
return new Cesium.Entity({
position: cartesian,
point: {
pixelSize: 3, // 像素大小,默认: 1
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND, // 表示相对于地形的位置
color: Cesium.Color.BLUE, // 默认: 白
disableDepthTestDistance: Number.POSITIVE_INFINITY,
}
})
}
// 绘制:动态线
createActiveLine(list) {
const shape = viewer.entities.add({
polyline: {
positions: list,
clampToGround: true,
width: 2,
material: new Cesium.PolylineDashMaterialProperty({
color: Cesium.Color.RED,
dashLength: 10,
dashPattern: 255,
}),
},
});
return shape;
}
// 绘制:线
createNormalLine(list) {
return new Cesium.Entity({
polyline: {
positions: list,
clampToGround: true,
width: 2,
},
})
}
// 销毁:清空绘制与监听
destroy() {
this.stop()
this.nodeCollection.entities.removeAll()
this.lineCollection.entities.removeAll()
}
}
调用:
// 引入
import { PolylineDrawer } from '@/utils/Entity/Draw/polyline.js'
// 声明实例:无回调函数
const polylineDrawer = new PolylineDrawer();
// 声明实例:有回调函数
const polylineDrawer = new PolylineDrawer((lineList)=> {
console.log(lineList)
);
// 开始绘制
polylineDrawer.start()
// 结束绘制并清除所有点和线
polylineDrawer.destroy()
补充:修改点或线的样式
如果你需要修改节点或动态点的样式,你可以参考如下Entity Point相关参数:
const PointOptions = {
show: true,
pixelSize: 10, // 像素大小,默认: 1
heightReference: Cesium.HeightReference.NONE, // 表示相对于地形的位置
color: Cesium.Color.SKYBLUE, // 默认: 白
outlineColor: Cesium.Color.BLACK, // 边框颜色,默认: 黑
outlineWidth: 3, // 边框宽度,默认: 0
scaleByDistance: new Cesium.NearFarScalar(1.0e3, 10.0, 2.0e3, 1.0), // 随着相机的距离改变大小
translucencyByDistance: new Cesium.NearFarScalar(1.0e3,1.0,2.0e3,0.1), // 随着相机的距离改变透明度
distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0,2.0e3), // 在指定距离区间内可见
// 获取或设置与相机的距离,在深度处禁用深度测试
// 设置为零时,将始终应用深度测试。设置为Number.POSITIVE_INFINITY时,永远不会应用深度测试。
disableDepthTestDistance: Number.POSITIVE_INFINITY,
}
如果你需要修改线或动态线的样式,你可以参考如下Entity Polyline相关参数:
const LineOptions = {
show: true,
// 定义线条的 Cartesian3 位置的数组
positions: Cesium.Cartesian3.fromDegreesArray([-75, 35, -125, 35]),
width: 5,
// 如果arcType不是ArcType.NONE,则指定每个纬度和经度之间的角距离
granularity: Cesium.Math.RADIANS_PER_DEGREE,
material: Cesium.Color.RED,
// 线低于地形时用于绘制折线的材质
depthFailMaterial: Cesium.Color.WHITE,
// 折线段必须遵循的线型
arcType: Cesium.ArcType.GEODESIC,
clampToGround: true, // 是否贴地
shadows: Cesium.ShadowMode.DISABLED, // 折线是投射还是接收光源的阴影
distanceDisplayCondition: new Cesium.DistanceDisplayCondition(
1.0e3,
2.0e3
),
// 在地面上时将对地形,3D tiles还是对两者进行分类 type:ClassificationType default:ClassificationType.BOTH
// TERRAIN 将仅对地形进行分类;CESIUM_3D_TILE 将仅对3D Tiles进行分类;BOTH 将同时对Terrain和3D Tiles进行分类。
classificationType: Cesium.ClassificationType.BOTH,
// 指定用于订购地面几何形状的z索引。仅在多边形为常数且未指定高度或拉伸高度的情况下才有效 type:ConstantProperty
zIndex: 0,
}