前言

鼠标左键添加点、右键完成绘制,单击右侧弹窗关闭按钮清空绘制。参考沙盒示例:Drawing on Terrain
【CesiumJS入门】(7)绘制多段线(动态实时画线)-LMLPHP

直接上代码了

/*
 * @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,
  }
07-16 19:31