Cesium中创建矩形的方法!

875 阅读2分钟

先放一个最关键的API:

/**
 * Creates the smallest possible Rectangle that encloses all positions in the provided array.
 * @param cartesians - The list of Cartesian instances.
 * @param [ellipsoid = Ellipsoid.WGS84] - The ellipsoid the cartesians are on.
 * @param [result] - The object onto which to store the result, or undefined if a new instance should be created.
 * @returns The modified result parameter or a new Rectangle instance if none was provided.
 */
static fromCartesianArray(cartesians: Cartesian3[], ellipsoid?: Ellipsoid, result?: Rectangle): Rectangle;

意思是:创建包含所提供数组中所有位置的尽可能最小的矩形。

在使用viewer.entities.add(entity)添加矩形时,如果我们单纯的使用以下方式创建矩形实体

const entity = new Cesium.Entity({
    id,
    rectangle: {
      coordinates: new Cesium.CallbackProperty(() => {
        const ellipsoid = Cesium.Ellipsoid.WGS84; // 使用WGS84椭球体模型
        const arr: number[] = [];
        positions.forEach(position => {
          const cartographic = ellipsoid.cartesianToCartographic(position);
          arr.push(cartographic.longitude);
          arr.push(cartographic.latitude);
        });
        return new Cesium.Rectangle(
          arr[0],
          arr[3],
          arr[2],
          arr[1],
        )
      }, false),
      outline: true,
      outlineColor: Cesium.Color.WHITE,
      heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
      material: Cesium.Color.RED.withAlpha(0.3)
    }
});

如果不对起始结束点的坐标做交换处理,就会遇到如下的错误: DeveloperError: options.rectangle.north must be greater than or equal to options.rectangle.south,这是因为new Cesium.Rectangle()接收的四个参数,是起始点,结束点的经纬度顺序是确定的,从左上角往右下方向移动绘制是没问题的,但是往相反方向,此时起点和终点的坐标计算就错误了,终点的north比起点的south大了,导致计算错误,而Cesium.Rectangle.fromCartesianArray(positions)就完美解决了这个问题,我们只需要将笛卡尔点位集合交给他,他自动计算这两个点位所围成的最小矩形,返回Cesium.Rectangle(west?: number, south?: number, east?: number, north?: number)需要的数据位置正确的coordinates实例对象。

image.png
下面是创建矩形完整的代码:

let entity: Cesium.Entity; // 当前绘制的矩形实体对象;
/**
 * 存放绘制实体的位置集合,这里设置对象结构,
 * 是因为会绘制多个,绘制新的内容不清理已经绘制的矩形实体
 */
let positionsMap: {
  [key: string]: Cesium.Cartesian3[]
} = {};
let drawEntityId = ''; // 当前绘制的实体ID
let drawState = 'idle'; // 绘制状态,idle: 空闲,未操作, drawing: 绘制中;
/**
 * 创建地图交互对象。
 */
let drawHandler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
const viewer = new Cesium.Viewer('mapContainer');
// 添加实体对象
const addRectangleEntity = (id: string, positions: Cesium.Cartesian3[]) => {
  if (positions.length == 1) {
    positions.push(positions[0])
  };
  entity = new Cesium.Entity({
    id,
    rectangle: {
      coordinates: new Cesium.CallbackProperty(() => {
        // 就本文中非常牛逼的那个方法
        return Cesium.Rectangle.fromCartesianArray(positions);
      }, false),
      outline: true,
      outlineColor: Cesium.Color.WHITE,
      heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
      material: Cesium.Color.RED.withAlpha(0.3)
    }
  });
  return viewer.entities.add(entity)
};
// 注册左键点击事件
drawHandler.setInputAction((click: Cesium.ScreenSpaceEventHandler.PositionedEvent) => {
  // click.endPosition是二维笛卡尔屏幕坐标
  const cartesian: Cesium.Cartesian3 = viewer.camera.pickEllipsoid(click.endPosition);
  // 开始新的矩形绘制
  if (drawState === 'idle') {
    drawEntityId = '这里设置一个不应该重复的字符串';
    positionsMap[drawEntityId] = [];
    positionsMap[drawEntityId].push(cartesian);
    drawState = 'drawing';
    addRectangleEntity(drawEntityId, positionsMap[drawEntityId]);
  };
  // 结束正在绘制的矩形
  if (drawState === 'drawing') {
    positionsMap[drawEntityId].push(cartesian);
    drawState = 'idle';
  };
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
/**
 * 注册鼠标移动事件
 * 鼠标的移动会动态修改矩形的结束点位
 */
drawHandler.setInputAction((click: Cesium.ScreenSpaceEventHandler.MotionEvent) => {
  if (drawState === 'idle') return;
  const { length } = positionsMap[drawEntityId] ?? [];
  if (length === 0) return;
  const cartesian = viewer.camera.pickEllipsoid(click.endPosition);
  if (!cartesian) return;
  if (length == 1) {
    positionsMap[drawEntityId].push(cartesian)
  } else {
    positionsMap[drawEntityId][length - 1] = cartesian
  }
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);