有个项目中,要求绘制多边形和矩形电子围栏。多边形的绘制很快做好了,但矩形花了一天多。官网的矩形示例是给定两个对角点坐标生成矩形,但这个不太对,它是水平垂直方向的。
我想要的效果是点击三次:
一开始我想用修改官网示例来完成,通过矩形的旋转属性(rotation: Cesium.Math.toRadians(45)
)来完成,但旋转的中心点是矩形的中心点,无法设置起点为旋转中心。这样的效果会让甲方迷惑,行不通。
还得是用绘制多边形的方式,自动生成相邻点的坐标。
如上图所示,p0/p1/p2是依次点击的三个点位
p1点不绘制,它是一个辅助点,用于确定c1点在这条延长线上。求取c1点的坐标,可以分为以下几个步骤:
借助turf.js
工具distance
计算两点的距离
import distance from "@turf/distance"
const length = distance(p0, p2, { units: 'miles' })
rhumbBearing
求取两点与正北方向的夹角
import rhumbBearing from "@turf/rhumb-bearing"
const bearing1 = rhumbBearing(p0, p1)
const bearing2 = rhumbBearing(p0, p2)
const angle1 = bearing2 - bearing1
根据前面求得的角度和距离,将余弦与长度相乘
const len1 = Math.cos(angle1 / 180 * Math.PI) * length
步骤2中已经求出p0p1点与正北方向的夹角bearing1,这里使用destination
求取目标点c1
const dest1 = destination(p0, len1, bearing1, { units: 'miles' })
肉眼可见是直角,接下来计算另一个点c2
点位c2与c1原理相同,只是角度计算方式不同
p1p0与p0c2垂直,所以
const angle2 = 90 - angle1
const len2 = Math.cos(angle2 / 180 * Math.PI) * length
p0c1点与p0c2点差了90度,所以
const dest2 = destination(p0, len2, 90 + bearing1, { units: 'miles' })
turf.js
是个好东西,我在项目中经常使用。前面的绘制一开始我是没用到turf
的,结果绘制出来的效果出乎意料,索性全部用turf中的工具进行了替换。
可能有些人会用到矩形绘制功能,这里把关键代码放在这里吧:
外部引入:
import { bearingToAzimuth, point } from "@turf/helpers"
import rhumbBearing from "@turf/rhumb-bearing"
import distance from "@turf/distance"
import destination from "@turf/destination"
绘制部分, points是数组,引用类型,所以只要在最开始调用一次就行了。
drawRectangle() {
console.log('draw rectangle')
this.handler = new Cesium.ScreenSpaceEventHandler(viewer.canvas)
const points = []
let shape = this.renderRect(points)
let step = 0
this.handler.setInputAction(e => {
let cartesian = viewer.scene.pickPosition(e.position);
if (!Cesium.defined(cartesian)) {
const ray = viewer.camera.getPickRay(e.position);
cartesian = viewer.scene.globe.pick(ray, viewer.scene);
}
points[step] = cartesian
step++
if (step === 3) {
this.handler.destroy();
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK)
this.handler.setInputAction(e => {
let cartesian = viewer.scene.pickPosition(e.startPosition);
if (!Cesium.defined(cartesian)) {
const ray = viewer.camera.getPickRay(e.startPosition);
cartesian = viewer.scene.globe.pick(ray, viewer.scene);
}
points[2] = cartesian
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE)
}
renderRect(points) {
const shape = this.viewer.entities.add({
polygon: {
hierarchy: new Cesium.CallbackProperty(() => {
if (points[0] && points[1] && points[2]) {
const r0 = Cesium.Cartographic.fromCartesian(points[0])
const r1 = Cesium.Cartographic.fromCartesian(points[1]) // 辅助点
const r2 = Cesium.Cartographic.fromCartesian(points[2])
const p0 = point([r0.longitude * 180 / Math.PI, r0.latitude * 180 / Math.PI])
const p1 = point([r1.longitude * 180 / Math.PI, r1.latitude * 180 / Math.PI])
const p2 = point([r2.longitude * 180 / Math.PI, r2.latitude * 180 / Math.PI])
const bearing1 = rhumbBearing(p0, p1)
const bearing2 = rhumbBearing(p0, p2)
const angle1 = bearing2 - bearing1
// 对角长度
const length = distance(p0, p2, { units: 'miles' })
const len1 = Math.cos(angle1 / 180 * Math.PI) * length
const dest1 = destination(p0, len1, bearing1, { units: 'miles' })
const angle2 = 90 - angle1
const len2 = Math.cos(angle2 / 180 * Math.PI) * length
const dest2 = destination(p0, len2, 90 + bearing1, { units: 'miles' })
const coordinates = [points[0], Cesium.Cartesian3.fromDegrees(...dest1.geometry.coordinates), points[2], Cesium.Cartesian3.fromDegrees(...dest2.geometry.coordinates)]
return new Cesium.PolygonHierarchy(coordinates)
}
}, false)
},
})
return shape
}