Cesium 自定义Primitive - 矩形

本文介绍了如何在Vue项目中创建一个自定义的CustomRectanglePrimitive,利用cesium库处理几何图形,根据两点生成矩形,提供动态更新属性的方法,用于实时调整矩形位置。

一、创作思路

        1、创建一个自定义CustomPrimitive,包括update和destroy方法

        2、然后根据两个点,生成矩形,取两点的包围和为矩形,得到的矩形为正矩形

        3、暴露更新属性方法,updateProperty,方便后期绘制矩形

二、实现代码

      1、turf

        在vue的包中引入turf:npm install @turf/turf或者yarn add @turf/turf

      2、自定义CustomRectanglePrimitive

        创建一个CustomRectanglePrimitive类,并加入更新、销毁的代码

import {
  Color,
  GeometryInstance,
  GroundPolylineGeometry,
  GroundPolylinePrimitive,
  Material,
  PolylineMaterialAppearance,
} from "cesium";
import { getRectanglePositions } from "@/components/MilitaryPlot/utils/PlotUtils";

export default class CustomRectanglePrimitive {
  constructor(options) {
    this._props = options;
    /**
     * 渲染列表
     * @type {*[]}
     * @private
     */
    this._primitiveCollection = [];

    this._dynamicPrimitive = undefined;
  }
  updateProperty(options) {
  }
  /**
   * 更新
   * @param frameState
   */
  update(frameState) {
    this._primitiveCollection.forEach((primitive) => {
      primitive && !primitive.isDestroyed() && primitive.update(frameState);
    });
    if (this._dynamicPrimitive) {
      this._dynamicPrimitive.update(frameState);
    }
  }
  destroy() {
    this._primitiveCollection.forEach((primitive) => {
      primitive && !primitive.isDestroyed() && primitive.destroy();
    });
    this._primitiveCollection = undefined;
    if (this._dynamicPrimitive) {
      this._dynamicPrimitive.destroy();
    }
    this._dynamicPrimitive = undefined;
  }
}

      3、更新属性

        对外暴露更新属性方法,主要参数为positions以及complete

        positions:点位用于确认矩形框

        complete:表示是否完成绘制

updateProperty(options) {
    this._props = {
      ...this._props,
      ...options,
    };
    let positions = this._props.positions;
    let complete = this._props.complete;
    if (positions.length > 1) {
      let dynamicPrimitive = this._dynamicPrimitive;
      if (dynamicPrimitive) {
        dynamicPrimitive.destroy();
        dynamicPrimitive = null;
        this._dynamicPrimitive = null;
      }
      let primitive = this.initRectanglePrimitive(positions);
      if (complete) {
        this._primitiveCollection.push(primitive);
      } else {
        this._dynamicPrimitive = primitive;
      }
    }
  }

     4、生成矩形

        编写绘制矩形的代码

initRectanglePrimitive(positions) {
    let rectanglePositions = getRectanglePositions(positions);
    let instance = new GeometryInstance({
      geometry: new GroundPolylineGeometry({
        positions: rectanglePositions,
        width: 4,
      }),
    });
    return new GroundPolylinePrimitive({
      geometryInstances: instance,
      appearance: new PolylineMaterialAppearance({
        material: new Material({
          fabric: {
            type: "Color",
            uniforms: {
              color: Color.fromCssColorString("#ff0000"),
            },
          },
        }),
      }),
      asynchronous: false,
    });
  }

      5、 坐标转换

        1、将世界坐标转换值WGS84坐标

        2、根据点位生成矩形包围盒,然后转换为世界坐标

const ellipsoid = Ellipsoid.WGS84;
/**
 * 将世界坐标系转换为球面坐标系
 * @param {Cartesian3} position
 * @return {{alt: number, lon: number, lat: number}}
 */
export const Cartesian3ToWgs84 = (position) => {
  let cartographic = ellipsoid.cartesianToCartographic(position);
  let lon = CesiumMath.toDegrees(cartographic.longitude);
  let lat = CesiumMath.toDegrees(cartographic.latitude);
  let alt = cartographic.height;
  return {
    lon,
    lat,
    alt,
  };
};
/**
 * 将世界坐标系转换为球面坐标系
 * @param {Cartesian3[]} positions
 * @return {{alt: number, lon: number, lat: number}[]}
 */
export const Cartesian3ListToWgs84 = (positions) => {
  return positions.map((position) => Cartesian3ToWgs84(position));
};
export const getRectanglePositions = (positions) => {
  let position84List = Cartesian3ListToWgs84(positions);
  let featureList = featureCollection(
    position84List.map((item) => point([item.lon, item.lat]))
  );
  let enveloped = envelope(featureList);
  return enveloped.geometry.coordinates[0].map((item) => {
    return Cartesian3.fromDegrees(item[0], item[1]);
  });
};

      6、测试代码

        测试代码,每个1秒,更新控制点位,连续更新10次

let primitive = new CustomRectanglePrimitive();
      viewer.scene.primitives.add(primitive);
      let lon = 106;
      let count = 0;
      let lat = 26;
      let centerP = Cartesian3.fromDegrees(lon, lat);
      let interval = setInterval(() => {
        count++;
        let lonTemp = lon + count * 0.00002;
        let latTemp = lat + count * 0.00001;
        let nextP = Cartesian3.fromDegrees(lonTemp, latTemp);
        primitive.updateProperty({
          positions: [centerP, nextP],
          complete: count === 11,
        });
        if (count > 10) {
          primitive.updateProperty({
            positions: [centerP, nextP],
            complete: true,
          });
          clearInterval(interval);
        } else {
          primitive.updateProperty({
            positions: [centerP, nextP],
            complete: false,
          });
        }
      }, 1000);

三、展示效果

四、全部代码

import {
  Color,
  GeometryInstance,
  GroundPolylineGeometry,
  GroundPolylinePrimitive,
  Material,
  PolylineMaterialAppearance,
} from "cesium";
import { getRectanglePositions } from "@/components/MilitaryPlot/utils/PlotUtils";

export default class CustomRectanglePrimitive {
  constructor(options) {
    this._props = options;
    /**
     * 渲染列表
     * @type {*[]}
     * @private
     */
    this._primitiveCollection = [];

    this._dynamicPrimitive = undefined;
  }
  updateProperty(options) {
    this._props = {
      ...this._props,
      ...options,
    };
    let positions = this._props.positions;
    let complete = this._props.complete;
    if (positions.length > 1) {
      let dynamicPrimitive = this._dynamicPrimitive;
      if (dynamicPrimitive) {
        dynamicPrimitive.destroy();
        dynamicPrimitive = null;
        this._dynamicPrimitive = null;
      }
      let primitive = this.initRectanglePrimitive(positions);
      if (complete) {
        this._primitiveCollection.push(primitive);
      } else {
        this._dynamicPrimitive = primitive;
      }
    }
  }
  initRectanglePrimitive(positions) {
    let rectanglePositions = getRectanglePositions(positions);
    let instance = new GeometryInstance({
      geometry: new GroundPolylineGeometry({
        positions: rectanglePositions,
        width: 4,
      }),
    });
    return new GroundPolylinePrimitive({
      geometryInstances: instance,
      appearance: new PolylineMaterialAppearance({
        material: new Material({
          fabric: {
            type: "Color",
            uniforms: {
              color: Color.fromCssColorString("#ff0000"),
            },
          },
        }),
      }),
      asynchronous: false,
    });
  }
  /**
   * 更新
   * @param frameState
   */
  update(frameState) {
    this._primitiveCollection.forEach((primitive) => {
      primitive && !primitive.isDestroyed() && primitive.update(frameState);
    });
    if (this._dynamicPrimitive) {
      this._dynamicPrimitive.update(frameState);
    }
  }
  destroy() {
    this._primitiveCollection.forEach((primitive) => {
      primitive && !primitive.isDestroyed() && primitive.destroy();
    });
    this._primitiveCollection = undefined;
    if (this._dynamicPrimitive) {
      this._dynamicPrimitive.destroy();
    }
    this._dynamicPrimitive = undefined;
  }
}

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
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

生活真难

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

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

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

打赏作者

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

抵扣说明:

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

余额充值