先放一个最关键的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实例对象。
下面是创建矩形完整的代码:
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);