Cesium 自定义Primitive - 绘制矩形

本文介绍了如何在Cesium中使用CustomRectanglePrimitive和ScreenSpaceEventHandler实现一个动态绘制矩形的工具,包括创建绘制类、处理鼠标点击和移动事件,以及屏幕坐标到地球坐标的转换。

一、创作来源

        1、cesium的entity绘制矩形

        2、不使用entity的情况下,使用自定义的primitive来动态绘制矩形

        3、结合上一篇文章的矩形,执行动态矩形的更新

二、编写步骤

1、创建绘制矩形的类

        包括构造函数、绘图函数以及销毁函数

import { Viewer, ScreenSpaceEventHandler, ScreenSpaceEventType } from "cesium";
import { pickPosition } from "@/components/MilitaryPlot/utils/PlotUtils";
import CustomRectanglePrimitive from "@/components/samples/CustomRectanglePrimitive";
export default class DrawRectangleTool {
  /**
   * 构造函数
   * @param {Viewer} viewer
   */
  constructor(viewer) {
    /**
     * 地球视图
     * @type {Viewer}
     * @private
     */
    this._viewer = viewer;
    /**
     * 线对象
     * @type {CustomRectanglePrimitive}
     * @private
     */
    this._primitive = undefined;
    /**
     * 事件
     * @type {ScreenSpaceEventHandler}
     * @private
     */
    this._handler = undefined;
  }

  /**
   * 绘图
   */
  draw() {
  }
  /**
   * 销毁
   */
  destroy() {
    if (this._handler) {
      this._handler.isDestroyed() && this._handler.destroy();
      this._handler = null;
    }
  }
}

 2、事件确认

        鼠标点击事件、移动事件

  /**
   * 绘图
   */
  draw() {
    this._handler = new ScreenSpaceEventHandler(this._viewer.canvas);
    let positions = [];
    let flag = this;
    this._handler.setInputAction((event) => {
      console.log("左键单机事件");
    }, ScreenSpaceEventType.LEFT_CLICK);
    this._handler.setInputAction((event) => {
      console.log("鼠标移动事件");
    }, ScreenSpaceEventType.MOUSE_MOVE);
  }

3、点位变动逻辑

        =>初始为空的数组

        =>点击后当为空时直接push,当有两个点时,结束编辑

        =>移动:当只有一个点时直接push,当点位数大于1的时候,先移除后push

4、屏幕坐标转地球坐标

        

/**
 * 获取地图点位
 * @param {Cartesian2} screenPosition 屏幕坐标
 * @param {Viewer} viewer 地图
 * @returns {Cartesian3} 地图三维坐标
 */
export const pickPosition = (screenPosition, viewer) => {
  let ray = viewer.camera.getPickRay(screenPosition);
  return viewer.scene.globe.pick(ray, viewer.scene);
};

5、鼠标事件代码

/**
   * 绘图
   */
  draw() {
    this._handler = new ScreenSpaceEventHandler(this._viewer.canvas);
    let positions = [];
    let flag = this;
    this._handler.setInputAction((event) => {
      console.log("左键单机事件");
      let position = pickPosition(event.position, flag._viewer);
      if (position) {
        if (positions.length === 0) {
          positions.push(position);
        } else {
          positions.pop();
          positions.push(position);
        }
      }
    }, ScreenSpaceEventType.LEFT_CLICK);
    this._handler.setInputAction((event) => {
      console.log("鼠标移动事件");
      let position = pickPosition(event.endPosition, flag._viewer);
      if (position) {
        if (positions.length === 1) {
          positions.push(position);
        } else if (positions.length > 1) {
          positions.pop();
          positions.push(position);
        }
      }
    }, ScreenSpaceEventType.MOUSE_MOVE);
  }

 6、渲染矩形代码

/**
   * 绘图
   */
  draw() {
    this._handler = new ScreenSpaceEventHandler(this._viewer.canvas);
    let positions = [];
    let flag = this;
    this._handler.setInputAction((event) => {
      console.log("左键单机事件");
      let position = pickPosition(event.position, flag._viewer);
      if (position) {
        if (positions.length === 0) {
          positions.push(position);
          flag._primitive = new CustomRectanglePrimitive({});
          this._viewer.scene.primitives.add(this._primitive);
        } else {
          positions.pop();
          positions.push(position);
          flag._primitive.updateProperty({
            positions: [...positions],
            complete: false,
          });
          this._primitive = undefined;
          positions = [];
        }
      }
    }, ScreenSpaceEventType.LEFT_CLICK);
    this._handler.setInputAction((event) => {
      console.log("鼠标移动事件");
      let position = pickPosition(event.endPosition, flag._viewer);
      if (position) {
        if (positions.length === 1) {
          positions.push(position);
        } else if (positions.length > 1) {
          positions.pop();
          positions.push(position);
        }
        if (flag._primitive) {
          flag._primitive.updateProperty({
            positions: [...positions],
            complete: false,
          });
        }
      }
    }, ScreenSpaceEventType.MOUSE_MOVE);
  }

7、测试

let tool = new DrawRectangleTool(viewer);
tool.draw();

三、实现效果

四、代码

import { Viewer, ScreenSpaceEventHandler, ScreenSpaceEventType } from "cesium";
import { pickPosition } from "@/components/MilitaryPlot/utils/PlotUtils";
import CustomRectanglePrimitive from "@/components/samples/CustomRectanglePrimitive";
export default class DrawRectangleTool {
  /**
   * 构造函数
   * @param {Viewer} viewer
   */
  constructor(viewer) {
    /**
     * 地球视图
     * @type {Viewer}
     * @private
     */
    this._viewer = viewer;
    /**
     * 线对象
     * @type {CustomRectanglePrimitive}
     * @private
     */
    this._primitive = undefined;
    /**
     * 事件
     * @type {ScreenSpaceEventHandler}
     * @private
     */
    this._handler = undefined;
  }

  /**
   * 绘图
   */
  draw() {
    this._handler = new ScreenSpaceEventHandler(this._viewer.canvas);
    let positions = [];
    let flag = this;
    this._handler.setInputAction((event) => {
      console.log("左键单机事件");
      let position = pickPosition(event.position, flag._viewer);
      if (position) {
        if (positions.length === 0) {
          positions.push(position);
          flag._primitive = new CustomRectanglePrimitive({});
          this._viewer.scene.primitives.add(this._primitive);
        } else {
          positions.pop();
          positions.push(position);
          flag._primitive.updateProperty({
            positions: [...positions],
            complete: false,
          });
          this._primitive = undefined;
          positions = [];
        }
      }
    }, ScreenSpaceEventType.LEFT_CLICK);
    this._handler.setInputAction((event) => {
      console.log("鼠标移动事件");
      let position = pickPosition(event.endPosition, flag._viewer);
      if (position) {
        if (positions.length === 1) {
          positions.push(position);
        } else if (positions.length > 1) {
          positions.pop();
          positions.push(position);
        }
        if (flag._primitive) {
          flag._primitive.updateProperty({
            positions: [...positions],
            complete: false,
          });
        }
      }
    }, ScreenSpaceEventType.MOUSE_MOVE);
  }
  /**
   * 销毁
   */
  destroy() {
    if (this._handler) {
      this._handler.isDestroyed() && this._handler.destroy();
      this._handler = null;
    }
  }
}

Cesium 中,`Primitive` 是一种底层图形 API,用于高效地绘制大量图形元素,比如点、线、面等。如果你想要**自定义创建点**(例如设置颜色、大小、位置等),可以使用 `PointPrimitive` 或 `PointPrimitiveCollection`,它们是 `Primitive` 的一部分,专门用于点的高效绘制。 下面我们将详细介绍如何使用 `Primitive` 创建和自定义点,并提供完整的代码示例。 --- ## ✅ 方法一:使用 `PointPrimitiveCollection` 创建点(推荐) `PointPrimitiveCollection` 是 Cesium 中用于高效绘制大量点的集合类,适合创建自定义的点集合。 ### 示例代码: ```javascript const viewer = new Cesium.Viewer('cesiumContainer'); // 创建一个 PointPrimitiveCollection const pointCollection = viewer.scene.primitives.add( new Cesium.PointPrimitiveCollection() ); // 添加多个点 pointCollection.add({ position: Cesium.Cartesian3.fromDegrees(-115.0, 32.0, 0), pixelSize: 10, color: Cesium.Color.RED }); pointCollection.add({ position: Cesium.Cartesian3.fromDegrees(-115.0, 35.0, 0), pixelSize: 15, color: Cesium.Color.BLUE }); pointCollection.add({ position: Cesium.Cartesian3.fromDegrees(-115.0, 37.0, 0), pixelSize: 20, color: Cesium.Color.GREEN }); ``` ### 说明: - `position`:点的经纬度位置,使用 `Cartesian3` 表示。 - `pixelSize`:点的大小(以像素为单位)。 - `color`:点的颜色,可以使用 `Cesium.Color` 设置 RGBA 值。 - `add()` 方法可以多次调用,添加多个点。 --- ## ✅ 方法二:使用 `PointPrimitive` 单独创建点(适合单个点) 如果你只需要创建一个点,可以使用 `PointPrimitive` 类。 ### 示例代码: ```javascript const viewer = new Cesium.Viewer('cesiumContainer'); const point = viewer.scene.primitives.add( new Cesium.PointPrimitive({ position: Cesium.Cartesian3.fromDegrees(-115.0, 32.0, 0), pixelSize: 10, color: Cesium.Color.YELLOW }) ); // 动态修改点的属性 setTimeout(() => { point.pixelSize = 20; point.color = Cesium.Color.RED; }, 2000); ``` ### 说明: - `PointPrimitive` 是一个单独的点对象。 - 支持动态修改属性(如 `pixelSize` 和 `color`)。 --- ## ✅ 方法三:使用 `Geometry` + `Primitive` 创建精确点(如带形状的点) 如果你需要创建**带形状的点**(比如矩形、圆形面),可以使用 `GeometryInstance` 和 `Primitive` 结合 `PointGeometry` 或 `CircleGeometry`。 ### 示例代码:使用 `CircleGeometry` 创建圆形面点 ```javascript const viewer = new Cesium.Viewer('cesiumContainer'); const position = Cesium.Cartesian3.fromDegrees(-115.0, 32.0, 0); const geometry = Cesium.CircleGeometry({ center: position, radius: 1000.0, // 半径(米) vertexFormat: Cesium.VertexFormat.POSITION_AND_ST }); const instance = new Cesium.GeometryInstance({ geometry: geometry, attributes: { color: Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.RED.withAlpha(0.5)) } }); viewer.scene.primitives.add( new Cesium.Primitive({ geometryInstances: [instance], appearance: new Cesium.PerInstanceColorAppearance() }) ); ``` ### 说明: - `CircleGeometry` 创建一个圆形几何体。 - `GeometryInstance` 用于封装几何体和属性(如颜色)。 - `Primitive` 将几何体渲染到场景中。 - 这种方式适合创建**精确的几何图形点**,但性能略低于 `PointPrimitiveCollection`。 --- ## ✅ 方法四:结合 `CustomShader` 自定义点外观(高级) 你也可以结合 `PointPrimitiveCollection` 和 `CustomShader` 来自定义点的外观(如形状、渐变、动画等)。 ### 示例代码: ```javascript const viewer = new Cesium.Viewer('cesiumContainer'); const pointCollection = viewer.scene.primitives.add( new Cesium.PointPrimitiveCollection() ); pointCollection.add({ position: Cesium.Cartesian3.fromDegrees(-115.0, 32.0, 0), pixelSize: 20, color: Cesium.Color.WHITE }); pointCollection.customShader = new Cesium.CustomShader({ fragmentShaderText: ` in vec4 v_color; out vec4 fragColor; void main() { vec2 coord = gl_PointCoord - vec2(0.5); float dist = length(coord); if (dist > 0.5) { discard; // 只显示圆形区域 } fragColor = v_color; } ` }); ``` --- ## ✅ 总结 | 方法 | 类型 | 适用场景 | 性能 | 可控性 | |------|------|----------|------|--------| | `PointPrimitiveCollection` | Primitive | 大量点 | 高 | ✅ | | `PointPrimitive` | Primitive | 单个点 | 高 | ✅ | | `GeometryInstance` + `CircleGeometry` | Geometry | 精确图形 | 中等 | ✅✅ | | `CustomShader` + `PointPrimitive` | Shader | 自定义外观 | 高 | ✅✅✅ | --- ##
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

生活真难

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值