// 三、曲线 // DrawCurve /* 绘制曲线 */ class DrawCurve { constructor(arg) { this.viewer = arg.viewer; this.Cesium = arg.Cesium; this.floatingPoint = null;//标识点 this._curveline = null; //活动曲线 this._curvelineLast = null; //最后一条曲线 this._positions = []; //活动点 this._entities_point = []; //脏数据 this._entities_line = []; //脏数据 this._curvelineData = null; //用于构造曲线数据 this.DrawStartEvent = new Cesium.Event(); //开始绘制事件 this.DrawEndEvent = new Cesium.Event(); //结束绘制事件 this.ZERO_TOLERANCE = 0.0001; this.FITTING_COUNT = 100; this.t = 0.3; /* 通用参数集合 */ this._param = { id: "DrawStraightArrow", polygonColor: 'rgba(0,255,0,0.5)', //面填充颜色 outlineColor: 'rgba(255, 255, 255, 1)', //边框颜色 outlineWidth: 1, //边框宽度 } /* 创建面材质 */ this.polygonMaterial = Cesium.Color.fromCssColorString(this._param.polygonColor); /* 创建线材质 */ // this.outlineMaterial = new Cesium.PolylineDashMaterialProperty({//曲线 // dashLength: 16, // color: Cesium.Color.fromCssColorString(this._param.outlineColor) // }); this.outlineMaterial = Cesium.Color.fromCssColorString(this._param.outlineColor); } //返回最后活动曲线 get curveline() { return this._curvelineLast; } //返回线数据用于加载线 getData() { return this._curvelineData; } //加载曲线 addload(data) { var $this = this; let pnts = [] for (let p = 0; p < data.length; p++) { pnts.push($this.lonLatToMercator(data[p])) } let CurvePoints = $this.getCurvePoints(pnts) let point = []; for (let i = 0; i < CurvePoints.length; i++) { point.push($this.LatlngTocartesian($this.WebMercator2lonLat(CurvePoints[i]))) } var polyline = this.viewer.entities.add({ Type: 'DrawCurve', Position: data, id: data.id || $this.objId, polyline: { positions: point, show: true, material: $this.Cesium.Color.YELLOW, width: 3, clampToGround: true } }); return polyline; } // 修改编辑调用计算 computePosition(data) { let pnts = []; let position = []; for (let p = 0; p < data.length; p++) { position.push(this.cartesianToLatlng(data[p])); pnts.push(this.lonLatToMercator(this.cartesianToLatlng(data[p]))) } this._curvelineData = position; let CurvePoints = this.getCurvePoints(pnts) let point = []; for (let i = 0; i < CurvePoints.length; i++) { point.push(this.LatlngTocartesian(this.WebMercator2lonLat(CurvePoints[i]))) } return point; } //开始创建 startCreate(drawType) { this.drawType = drawType var $this = this; this.handler = new this.Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas); this.handler.setInputAction(function (evt) { //单机开始绘制 //屏幕坐标转地形上坐标 var cartesian = $this.getCatesian3FromPX(evt.position); if ($this._positions.length == 0) { $this._positions.push(cartesian.clone()); $this.floatingPoint = $this.createPoint(cartesian); $this.createPoint(cartesian);// 绘制点 } $this._positions.push(cartesian); }, $this.Cesium.ScreenSpaceEventType.LEFT_CLICK); this.handler.setInputAction(function (evt) { //移动时绘制线 if ($this._positions.length < 3) return; var cartesian = $this.getCatesian3FromPX(evt.endPosition); if (!$this.Cesium.defined($this._curveline)) { $this._curveline = $this.createCurveline(); } $this.floatingPoint.position.setValue(cartesian); if ($this._curveline) { $this._positions.pop(); $this._positions.push(cartesian); } }, $this.Cesium.ScreenSpaceEventType.MOUSE_MOVE); this.handler.setInputAction(function (evt) { if (!$this._curveline) return; var cartesian = $this.getCatesian3FromPX(evt.position); $this._positions.pop(); $this._positions.push(cartesian); $this.createPoint(cartesian);// 绘制点 $this._curvelineData = $this._positions.concat(); $this.viewer.entities.remove($this._curveline); //移除 $this._curveline = null; $this._positions = []; $this.floatingPoint.position.setValue(cartesian); let lnglatArr = []; for (var i = 0; i < $this._curvelineData.length; i++) { var lnglat = $this.cartesianToLatlng($this._curvelineData[i]); lnglatArr.push(lnglat) } $this._curvelineData = lnglatArr; var line = $this.addload($this._curvelineData); //加载曲线 $this._entities_line.push(line); $this._curvelineLast = line; $this.clearPoint() $this.destroy() }, $this.Cesium.ScreenSpaceEventType.RIGHT_CLICK); } //创建点 createPoint(cartesian) { var $this = this; var point = this.viewer.entities.add({ position: cartesian, point: { pixelSize: 10, color: $this.Cesium.Color.RED, heightReference: Cesium.HeightReference.CLAMP_TO_GROUND, } }); $this._entities_point.push(point); return point; } //创建曲线 createCurveline() { var $this = this; var polyline = this.viewer.entities.add({ polyline: { //使用cesium的peoperty positions: new $this.Cesium.CallbackProperty(function () { let pnts = [] for (let p = 0; p < $this._positions.length; p++) { pnts.push($this.lonLatToMercator($this.cartesianToLatlng($this._positions[p]))) } let CurvePoints = $this.getCurvePoints(pnts) let point = []; for (let i = 0; i < CurvePoints.length; i++) { point.push($this.LatlngTocartesian($this.WebMercator2lonLat(CurvePoints[i]))) } return point; }, false), show: true, material: $this.Cesium.Color.YELLOW, width: 3, clampToGround: true } }); $this._entities_line.push(polyline); return polyline; } clearPoint() { this.DrawEndEvent.raiseEvent(this._curvelineLast, this._curvelineData, this.drawType); for (var i = 0; i < this._entities_point.length; i++) { this.viewer.entities.remove(this._entities_point[i]); } this._entities_point = []; //脏数据 } //销毁 destroy() { if (this.handler) { this.handler.destroy(); this.handler = null; } } //清空实体对象 clear() { for (var i = 0; i < this._entities_point.length; i++) { this.viewer.entities.remove(this._entities_point[i]); } for (var i = 0; i < this._entities_line.length; i++) { this.viewer.entities.remove(this._entities_line[i]); } this.floatingPoint = null;//标识点 this._curveline = null; //活动曲线 this._curvelineLast = null; //最后一条曲线 this._positions = []; //活动点 this._entities_point = []; //脏数据 this._entities_line = []; //脏数据 this._curvelineData = null; //用于构造曲线数据 } getCatesian3FromPX(px) { var cartesian; var ray = this.viewer.camera.getPickRay(px); if (!ray) return null; cartesian = this.viewer.scene.globe.pick(ray, this.viewer.scene); return cartesian; } cartesianToLatlng(cartesian) { var latlng = this.viewer.scene.globe.ellipsoid.cartesianToCartographic(cartesian); var lat = this.Cesium.Math.toDegrees(latlng.latitude); var lng = this.Cesium.Math.toDegrees(latlng.longitude); return [lng, lat]; } LatlngTocartesian(latlng) { let cartesian3 = this.Cesium.Cartesian3.fromDegrees(latlng[0], latlng[1]); return cartesian3 } /** * 经纬度坐标转墨卡托坐标 */ // 墨卡托坐标系:展开地球,赤道作为x轴,向东为x轴正方,本初子午线作为y轴,向北为y轴正方向。 // 数字20037508.34是地球赤道周长的一半:地球半径6378137米,赤道周长2*PI*r = 2 * 20037508.3427892,墨卡托坐标x轴区间[-20037508.3427892,20037508.3427892] lonLatToMercator(Latlng) { var E = Latlng[0]; var N = Latlng[1]; var x = E * 20037508.34 / 180; var y = Math.log(Math.tan((90 + N) * Math.PI / 360)) / (Math.PI / 180); y = y * 20037508.34 / 180; return [x, y] } /** * 墨卡托坐标转经纬度坐标转 */ WebMercator2lonLat(mercator) { let x = mercator[0] / 20037508.34 * 180; let ly = mercator[1] / 20037508.34 * 180; let y = 180 / Math.PI * (2 * Math.atan(Math.exp(ly * Math.PI / 180)) - Math.PI / 2) return [x, y]; } /** * 插值曲线点 * @param t * @param controlPoints * @returns {null} */ getCurvePoints( controlPoints) { let leftControl = this.getLeftMostControlPoint(controlPoints, this.t) let [pnt1, pnt2, pnt3, normals, points] = [null, null, null, [leftControl], []] for (let i = 0; i < controlPoints.length - 2; i++) { [pnt1, pnt2, pnt3] = [controlPoints[i], controlPoints[i + 1], controlPoints[i + 2]] let normalPoints = this.getBisectorNormals(this.t, pnt1, pnt2, pnt3) normals = normals.concat(normalPoints) } let rightControl = this.getRightMostControlPoint(controlPoints, this.t) if (rightControl) { normals.push(rightControl) } for (let i = 0; i < controlPoints.length - 1; i++) { pnt1 = controlPoints[i] pnt2 = controlPoints[i + 1] points.push(pnt1) for (let t = 0; t < this.FITTING_COUNT; t++) { let pnt = this.getCubicValue(t / this.FITTING_COUNT, pnt1, normals[i * 2], normals[i * 2 + 1], pnt2) points.push(pnt) } points.push(pnt2) } return points } /** * 获取左边控制点 * @param controlPoints * @returns {[*,*]} */ getLeftMostControlPoint(controlPoints, t) { let [pnt1, pnt2, pnt3, controlX, controlY] = [controlPoints[0], controlPoints[1], controlPoints[2], null, null] let pnts = this.getBisectorNormals(0, pnt1, pnt2, pnt3) let normalRight = pnts[0] let normal = this.getNormal(pnt1, pnt2, pnt3) let dist = Math.sqrt(normal[0] * normal[0] + normal[1] * normal[1]) if (dist > this.ZERO_TOLERANCE) { let mid = this.Mid(pnt1, pnt2) let pX = pnt1[0] - mid[0] let pY = pnt1[1] - mid[1] let d1 = this.MathDistance(pnt1, pnt2) let n = 2.0 / d1 let nX = -n * pY let nY = n * pX let a11 = nX * nX - nY * nY let a12 = 2 * nX * nY let a22 = nY * nY - nX * nX let dX = normalRight[0] - mid[0] let dY = normalRight[1] - mid[1] controlX = mid[0] + a11 * dX + a12 * dY controlY = mid[1] + a12 * dX + a22 * dY } else { controlX = pnt1[0] + t * (pnt2[0] - pnt1[0]) controlY = pnt1[1] + t * (pnt2[1] - pnt1[1]) } return [controlX, controlY] } /** * getBisectorNormals * @param t * @param pnt1 * @param pnt2 * @param pnt3 * @returns {[*,*]} */ getBisectorNormals(t, pnt1, pnt2, pnt3) { let normal = this.getNormal(pnt1, pnt2, pnt3) let [bisectorNormalRight, bisectorNormalLeft, dt, x, y] = [null, null, null, null, null] let dist = Math.sqrt(normal[0] * normal[0] + normal[1] * normal[1]) let uX = normal[0] / dist let uY = normal[1] / dist let d1 = this.MathDistance(pnt1, pnt2) let d2 = this.MathDistance(pnt2, pnt3) if (dist > this.ZERO_TOLERANCE) { if (this.isClockWise(pnt1, pnt2, pnt3)) { dt = t * d1 x = pnt2[0] - dt * uY y = pnt2[1] + dt * uX bisectorNormalRight = [x, y] dt = t * d2 x = pnt2[0] + dt * uY y = pnt2[1] - dt * uX bisectorNormalLeft = [x, y] } else { dt = t * d1 x = pnt2[0] + dt * uY y = pnt2[1] - dt * uX bisectorNormalRight = [x, y] dt = t * d2 x = pnt2[0] - dt * uY y = pnt2[1] + dt * uX bisectorNormalLeft = [x, y] } } else { x = pnt2[0] + t * (pnt1[0] - pnt2[0]) y = pnt2[1] + t * (pnt1[1] - pnt2[1]) bisectorNormalRight = [x, y] x = pnt2[0] + t * (pnt3[0] - pnt2[0]) y = pnt2[1] + t * (pnt3[1] - pnt2[1]) bisectorNormalLeft = [x, y] } return [bisectorNormalRight, bisectorNormalLeft] } /** * 获取默认三点的内切圆 * @param pnt1 * @param pnt2 * @param pnt3 * @returns {[*,*]} */ getNormal(pnt1, pnt2, pnt3) { let dX1 = pnt1[0] - pnt2[0] let dY1 = pnt1[1] - pnt2[1] let d1 = Math.sqrt(dX1 * dX1 + dY1 * dY1) dX1 /= d1 dY1 /= d1 let dX2 = pnt3[0] - pnt2[0] let dY2 = pnt3[1] - pnt2[1] let d2 = Math.sqrt(dX2 * dX2 + dY2 * dY2) dX2 /= d2 dY2 /= d2 let uX = dX1 + dX2 let uY = dY1 + dY2 return [uX, uY] } /** * 判断是否是顺时针 * @param pnt1 * @param pnt2 * @param pnt3 * @returns {boolean} */ isClockWise(pnt1, pnt2, pnt3) { return ((pnt3[1] - pnt1[1]) * (pnt2[0] - pnt1[0]) > (pnt2[1] - pnt1[1]) * (pnt3[0] - pnt1[0])) } /** * 求取两个坐标的中间值 * @param point1 * @param point2 * @returns {[*,*]} * @constructor */ Mid(point1, point2) { return [(point1[0] + point2[0]) / 2, (point1[1] + point2[1]) / 2] } /** * 计算两个坐标之间的距离 * @ignore * @param pnt1 * @param pnt2 * @returns {number} * @constructor */ MathDistance(pnt1, pnt2) { return (Math.sqrt(Math.pow((pnt1[0] - pnt2[0]), 2) + Math.pow((pnt1[1] - pnt2[1]), 2))) } /** * 获取右边控制点 * @param controlPoints * @param t * @returns {[*,*]} */ getRightMostControlPoint(controlPoints, t) { let count = controlPoints.length let pnt1 = controlPoints[count - 3] let pnt2 = controlPoints[count - 2] let pnt3 = controlPoints[count - 1] let pnts = this.getBisectorNormals(0, pnt1, pnt2, pnt3) let normalLeft = pnts[1] let normal = this.getNormal(pnt1, pnt2, pnt3) let dist = Math.sqrt(normal[0] * normal[0] + normal[1] * normal[1]) let [controlX, controlY] = [null, null] if (dist > this.ZERO_TOLERANCE) { let mid = this.Mid(pnt2, pnt3) let pX = pnt3[0] - mid[0] let pY = pnt3[1] - mid[1] let d1 = this.MathDistance(pnt2, pnt3) let n = 2.0 / d1 let nX = -n * pY let nY = n * pX let a11 = nX * nX - nY * nY let a12 = 2 * nX * nY let a22 = nY * nY - nX * nX let dX = normalLeft[0] - mid[0] let dY = normalLeft[1] - mid[1] controlX = mid[0] + a11 * dX + a12 * dY controlY = mid[1] + a12 * dX + a22 * dY } else { controlX = pnt3[0] + t * (pnt2[0] - pnt3[0]) controlY = pnt3[1] + t * (pnt2[1] - pnt3[1]) } return [controlX, controlY] } /** * 获取立方值 * @param t * @param startPnt * @param cPnt1 * @param cPnt2 * @param endPnt * @returns {[*,*]} */ getCubicValue(t, startPnt, cPnt1, cPnt2, endPnt) { t = Math.max(Math.min(t, 1), 0) let [tp, t2] = [(1 - t), (t * t)] let t3 = t2 * t let tp2 = tp * tp let tp3 = tp2 * tp let x = (tp3 * startPnt[0]) + (3 * tp2 * t * cPnt1[0]) + (3 * tp * t2 * cPnt2[0]) + (t3 * endPnt[0]) let y = (tp3 * startPnt[1]) + (3 * tp2 * t * cPnt1[1]) + (3 * tp * t2 * cPnt2[1]) + (t3 * endPnt[1]) return [x, y] } } export default DrawCurve