import { createTooltip } from "../../../common/common.js"; import { isRuntimeApp, isRuntimeWeb, createOperationMainDom, showTooltipMessage } from "../../../common/RuntimeEnvironment.js"; // 闭合曲面 class DrawClosedCurve { constructor(arg) { this.viewer = arg.viewer; this.Cesium = arg.Cesium; this.floatingPoint = null; //标识点 this._ClosedCurve = null; //活动闭合曲面 this._ClosedCurveLast = null; //最后一个闭合曲面 this._positions = []; //活动点 this._entities_point = []; //脏数据 this._entities_ClosedCurve = []; //脏数据 this._ClosedCurveData = null; //用于构造闭合曲面数据 this.ZERO_TOLERANCE = 0.0001; this.FITTING_COUNT = 100; this.t = 0.3 this.objId = Number((new Date()).getTime() + "" + Number(Math.random() * 1000).toFixed(0)); this.DrawStartEvent = new Cesium.Event(); //开始绘制事件 this.DrawEndEvent = new Cesium.Event(); //结束绘制事件 this._tooltip = createTooltip(this.viewer.container); /* 通用参数集合 */ 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 ClosedCurve() { return this._ClosedCurveLast; } //返回闭合曲面数据用于加载闭合曲面 getData() { return this._ClosedCurveData; } // 修改编辑调用计算 computePosition(data) { let $this = this; if (data.length < 2) return let pnts = [] for (let p = 0; p < data.length; p++) { pnts.push($this.cartesianToLatlng(data[p])) } this._ClosedCurveData = Array.from(pnts); pnts.push(pnts[0], pnts[1]) let [normals, pList] = [ [], [] ] for (let i = 0; i < pnts.length - 2; i++) { let normalPoints = $this.getBisectorNormals($this.t, pnts[i], pnts[i + 1], pnts[i + 2]) normals = normals.concat(normalPoints) } let count = normals.length normals = [normals[count - 1]].concat(normals.slice(0, count - 1)) for (let i = 0; i < pnts.length - 2; i++) { let pnt1 = pnts[i] let pnt2 = pnts[i + 1] pList.push($this.LatlngTocartesian(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) pList.push($this.LatlngTocartesian(pnt)) } pList.push($this.LatlngTocartesian(pnt2)) } let PolygonHierarchy = new $this.Cesium.PolygonHierarchy(pList); return { PolygonHierarchy, pList } } //加载闭合曲面 addload(data) { let $this = this if (data.length < 2) return let pnts = Array.from(data); pnts.push(pnts[0], pnts[1]) let [normals, pList] = [ [], [] ] for (let i = 0; i < pnts.length - 2; i++) { let normalPoints = $this.getBisectorNormals($this.t, pnts[i], pnts[i + 1], pnts[i + 2]) normals = normals.concat(normalPoints) } let count = normals.length normals = [normals[count - 1]].concat(normals.slice(0, count - 1)) for (let i = 0; i < pnts.length - 2; i++) { let pnt1 = pnts[i] let pnt2 = pnts[i + 1] pList.push($this.LatlngTocartesian(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) pList.push($this.LatlngTocartesian(pnt)) } pList.push($this.LatlngTocartesian(pnt2)) } console.log(data, pnts) var arrowEntity = $this.viewer.entities.add({ Type: 'DrawClosedCurve', Position: data, id: data.id || $this.objId, polygon: { hierarchy: new $this.Cesium.PolygonHierarchy(pList), show: true, fill: true, clampToGround: true, material: $this.polygonMaterial }, polyline: { positions: pList, show: true, material: new Cesium.PolylineDashMaterialProperty({ color: Cesium.Color.YELLOW, }), width: 3, clampToGround: true } }); return arrowEntity; } //开始创建 startCreate(drawType) { if (isRuntimeApp()) { showTooltipMessage("点击开始绘制"); } var $this = this; this.drawType = drawType; this.handler = new this.Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas); //单击开始绘制 this.handler.setInputAction(function(evt) { if (isRuntimeApp()) { //屏幕坐标转地形上坐标 var cartesian = $this.getCatesian3FromPX(evt.position); if (!cartesian) { return; } $this.createPoint(cartesian); // 绘制点 $this._positions.push(cartesian); if ($this._positions.length > 2) { showTooltipMessage("点击添加点,点击完成按钮,结束绘制"); if ($this._positions.length === 3) { if (!$this.Cesium.defined($this._ClosedCurve)) { $this._ClosedCurve = $this.createClosedCurve(); //创建按钮 createOperationMainDom(); //隐藏回退按钮 document.getElementById("btnDrawBackout").style.display = 'none'; //完成绘制 document.getElementById("btnDrawComplete").onclick = () => { $this._ClosedCurveData = $this._positions.concat(); $this.viewer.entities.remove($this._ClosedCurve); //移除 $this._ClosedCurve = null; $this._positions = []; var lnglatArr = []; for (var i = 0; i < $this._ClosedCurveData.length; i++) { var lnglat = $this.cartesianToLatlng($this._ClosedCurveData[i]); lnglatArr.push(lnglat) } $this._ClosedCurveData = lnglatArr; var ClosedCurve = $this.addload(lnglatArr); //加载 $this._entities_ClosedCurve.push(ClosedCurve); $this._ClosedCurveLast = ClosedCurve; $this.clearPoint(); $this.destroy(); let buttonDiv = document.getElementById("drawButtonDiv"); if (buttonDiv) { //从页面移除 document.body.removeChild(buttonDiv); } } } } } else { showTooltipMessage("点击添加点"); } } else { console.log('监听鼠标事件', '单击') /* 锁定点击事件 以免和双击事件冲突 */ clearTimeout($this._timer); $this._timer = setTimeout(function() { //屏幕坐标转地形上坐标 var cartesian = $this.getCatesian3FromPX(evt.position); if ($this._positions.length == 0) { $this.floatingPoint = $this.createPoint(cartesian); } $this._positions.push(cartesian); $this.createPoint(cartesian); }, 200); } }, $this.Cesium.ScreenSpaceEventType.LEFT_CLICK); //移动时绘制面 this.handler.setInputAction(function(evt) { /* 如果运行环境是App 则禁止使用鼠标移动事件 */ if (isRuntimeApp()) return; if ($this._positions.length == 0) { $this._tooltip.showAt(evt.endPosition, "点击开始绘制"); } else { $this._tooltip.showAt(evt.endPosition, "点击添加点"); } if ($this._positions.length < 2) return; $this._tooltip.showAt(evt.endPosition, "点击添加点,双击结束绘制"); var cartesian = $this.getCatesian3FromPX(evt.endPosition); if ($this._positions.length == 2) { $this._positions.push(cartesian); } $this._positions.pop(); $this._positions.push(cartesian); if (!$this.Cesium.defined($this._ClosedCurve)) { $this._ClosedCurve = $this.createClosedCurve(); } }, $this.Cesium.ScreenSpaceEventType.MOUSE_MOVE); //右击停止采集改为双击结束 this.handler.setInputAction(function(evt) { // if (!$this._ClosedCurve) return; // var cartesian = $this.getCatesian3FromPX(evt.position); // $this._positions.pop(); // $this._positions.push(cartesian); // $this._ClosedCurveData = $this._positions.concat(); // $this.viewer.entities.remove($this._ClosedCurve); //移除 // $this._ClosedCurve = null; // $this._positions = []; // $this.floatingPoint.position.setValue(cartesian); // var lnglatArr = []; // for (var i = 0; i < $this._ClosedCurveData.length; i++) { // var lnglat = $this.cartesianToLatlng($this._ClosedCurveData[i]); // lnglatArr.push(lnglat) // } // $this._ClosedCurveData = lnglatArr; // var ClosedCurve = $this.addload(lnglatArr); //加载 // $this._entities_ClosedCurve.push(ClosedCurve); // $this._ClosedCurveLast = ClosedCurve; // $this.clearPoint(); // $this.destroy() }, $this.Cesium.ScreenSpaceEventType.RIGHT_CLICK); //双击结束 this.handler.setInputAction(function(evt) { /* 如果运行环境是App 则禁止使用鼠标双击事件 */ if (isRuntimeApp()) return; console.log('监听鼠标事件', '双击') /* 解除锁定 */ clearTimeout($this._timer); if (!$this._ClosedCurve) return; var cartesian = $this.getCatesian3FromPX(evt.position); $this._positions.pop(); $this._positions.push(cartesian); $this._ClosedCurveData = $this._positions.concat(); $this.viewer.entities.remove($this._ClosedCurve); //移除 $this._ClosedCurve = null; $this._positions = []; $this.floatingPoint.position.setValue(cartesian); var lnglatArr = []; for (var i = 0; i < $this._ClosedCurveData.length; i++) { var lnglat = $this.cartesianToLatlng($this._ClosedCurveData[i]); lnglatArr.push(lnglat) } $this._ClosedCurveData = lnglatArr; var ClosedCurve = $this.addload(lnglatArr); //加载 $this._entities_ClosedCurve.push(ClosedCurve); $this._ClosedCurveLast = ClosedCurve; $this.clearPoint(); $this.destroy(); $this._tooltip.setVisible(false); }, $this.Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK); } //创建直线闭合曲面 createClosedCurve() { // console.log(this._positions) var $this = this; $this.pLists = '' var arrowEntity = $this.viewer.entities.add({ polygon: { hierarchy: new $this.Cesium.CallbackProperty( function() { if ($this._positions.length < 2) return let pnts = [] for (let p = 0; p < $this._positions.length; p++) { pnts.push($this.cartesianToLatlng($this._positions[p])) } pnts.push(pnts[0], pnts[1]) let [normals, pList] = [ [], [] ] for (let i = 0; i < pnts.length - 2; i++) { let normalPoints = $this.getBisectorNormals($this.t, pnts[i], pnts[i + 1], pnts[i + 2]) normals = normals.concat(normalPoints) } let count = normals.length normals = [normals[count - 1]].concat(normals.slice(0, count - 1)) for (let i = 0; i < pnts.length - 2; i++) { let pnt1 = pnts[i] let pnt2 = pnts[i + 1] pList.push($this.LatlngTocartesian(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) pList.push($this.LatlngTocartesian(pnt)) } pList.push($this.LatlngTocartesian(pnt2)) } $this.pLists = pList return new $this.Cesium.PolygonHierarchy(pList); }, false), show: true, fill: true, clampToGround: true, material: $this.polygonMaterial }, polyline: { positions: new $this.Cesium.CallbackProperty( function() { return $this.pLists }, false ), show: true, material: new Cesium.PolylineDashMaterialProperty({ color: Cesium.Color.YELLOW, }), width: 3, clampToGround: true } }) $this._entities_ClosedCurve.push(arrowEntity); return arrowEntity } //创建点 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; } /** * 世界坐标转经纬度 */ 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]; } //销毁 destroy() { if (this.handler) { this.handler.destroy(); this.handler = null; } } clearPoint() { this.DrawEndEvent.raiseEvent(this._ClosedCurveLast, this._ClosedCurveData); for (var i = 0; i < this._entities_point.length; i++) { this.viewer.entities.remove(this._entities_point[i]); } this._entities_point = []; //脏数据 } //清空实体对象 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_ClosedCurve.length; i++) { this.viewer.entities.remove(this._entities_ClosedCurve[i]); } this.floatingPoint = null; //标识点 this._ClosedCurve = null; //活动闭合曲面 this._ClosedCurveLast = null; //最后一个闭合曲面 this._positions = []; //活动点 this._entities_point = []; //脏数据 this._entities_ClosedCurve = []; //脏数据 this._ClosedCurveData = 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; } // 求取闭合曲面坐标函数/ //闭合曲面配置函数 /** * getBisectorNormals * @param t * @param pnt1 * @param pnt2 * @param pnt3 * @returns {[*,*]} */ getBisectorNormals(t, pnt1, pnt2, pnt3) { let $this = this 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] } /** * 计算两个坐标之间的距离 * @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 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 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 DrawClosedCurve