[Introduction to CesiumJS] (7) Drawing polylines (dynamic real-time drawing)

foreword

If you are looking for a solution, you can directly use the cesium-draw plugin.

Left mouse button to add points, right mouse button to complete the drawing, click the popup window close button on the right to clear the drawing. Reference sandbox example: Drawing on Terrain
insert image description here

directly on the code

/*
 * @Date: 2023-07-12 18:47:18
 * @LastEditors: ReBeX  [email protected]
 * @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()
  }
}

transfer:

// 引入
import {
    
     PolylineDrawer } from '@/utils/Entity/Draw/polyline.js'

// 声明实例:无回调函数
const polylineDrawer = new PolylineDrawer();

// 声明实例:有回调函数
const polylineDrawer = new PolylineDrawer((lineList)=> {
    
    
	console.log(lineList)
);

// 开始绘制
polylineDrawer.start()

// 结束绘制并清除所有点和线
polylineDrawer.destroy()

Added: modify the style of point or line

If you need to modify the style of nodes or dynamic points, you can refer to the following Entity Point related parameters:

  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,
  }

If you need to modify the style of lines or dynamic lines, you can refer to the following Entity Polyline related parameters:

  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,
  }

Guess you like

Origin blog.csdn.net/ReBeX/article/details/131751913