From b2d307b308cf2b43ab79ad8afbeb9d7dcea21819 Mon Sep 17 00:00:00 2001 From: zh <972939975@qq.com> Date: Thu, 21 Aug 2025 16:34:30 +0800 Subject: [PATCH 01/17] =?UTF-8?q?=E5=B9=BF=E5=91=8A=E7=89=8C=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E5=B1=9E=E6=80=A7=E6=A1=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Obj/Base/BillboardObject/index.js | 370 +++++++++++++++++++++++++- src/Obj/Element/cy_html_tabs.js | 15 +- 2 files changed, 380 insertions(+), 5 deletions(-) diff --git a/src/Obj/Base/BillboardObject/index.js b/src/Obj/Base/BillboardObject/index.js index 3bd6bfc..b637a4f 100644 --- a/src/Obj/Base/BillboardObject/index.js +++ b/src/Obj/Base/BillboardObject/index.js @@ -40,6 +40,10 @@ import { import { getGoodsList } from '../../../Tools/getGoodsList' class BillboardObject extends Base { + #_postRenderEvent = null + #_destroyMouseEvent = null + + /** * @constructor * @description 创建点标注 @@ -145,11 +149,18 @@ class BillboardObject extends Base { this.options.attribute.goods.content || [] this.options.attributeType = options.attributeType || 'richText' this.options.coordinate = options.coordinate || '' + this.options.attributeBoxState = options.attributeBoxState || false this.operate = {} this._elms = {} this.previous = { positions: { ...this.options.positions } } + this.options.attributePos = options.attributePos || { + x: 60, + y: 60, + width: 200, + height: 120 + } this.entity this._proj = this.sdk.proj @@ -182,7 +193,107 @@ class BillboardObject extends Base { + this.#_destroyMouseEvent = () => { + this.attributeElm && (this.attributeElm.style.pointerEvents = 'unset') + this.sdk.viewer._element.onmousemove = null + document.removeEventListener('mouseup', this.#_destroyMouseEvent) + document.removeEventListener('mouseleave', this.#_destroyMouseEvent) + } + this.#_postRenderEvent = () => { + let siteInfoPosition = Cesium.Cartesian3.fromDegrees( + this.options.positions.lng, + this.options.positions.lat + ) + if (this.attributeElm && this.entity) { + let winpos = this.sdk.viewer.scene.cartesianToCanvasCoordinates( + siteInfoPosition + ) + let pixelOffset = this.entity.label.pixelOffset.getValue() + if (winpos) { + let scale = getCurrentBillboardScale(this.entity, this.sdk.viewer.scene) + let height = ((this.entity.billboard.height.getValue() * (this.options.billboard.scale || 0)) + this.options.label.fontSize) * (1 - (scale * scale)) + let flag = false + let lineElm = this.attributeElm.getElementsByClassName('billboard-attribute-box-line')[0] + let leftTopElm = this.attributeElm.getElementsByClassName('left-top')[0] + let rightTopElm = this.attributeElm.getElementsByClassName('right-top')[0] + this.attributeElm.style.left = (winpos.x + this.options.attributePos.x).toFixed(0) + 'px' + this.attributeElm.style.top = (winpos.y + pixelOffset.y - (this.options.label.show ? (this.options.label.fontSize / 2) : -(this.options.label.fontSize / 2)) - this.attributeElm.offsetHeight - this.options.attributePos.y + height).toFixed(0) + 'px' + this.attributeElm.style.width = this.options.attributePos.width + 'px' + this.attributeElm.style.height = this.options.attributePos.height + 'px' + if (this.options.attributePos.x < -this.options.attributePos.width / 2) { + flag = true + lineElm.style.left = 'unset' + lineElm.style.right = '0' + leftTopElm.style.display = 'block' + rightTopElm.style.display = 'none' + } + else { + lineElm.style.left = '0' + lineElm.style.right = 'unset' + leftTopElm.style.display = 'none' + rightTopElm.style.display = 'block' + } + + let lineLength + let lineAngleRad + let lineAngle + let x + let y + if (flag) { + x = this.attributeElm.offsetWidth + this.options.attributePos.x + y = this.options.attributePos.y ? this.options.attributePos.y : 0 + } + else { + x = this.options.attributePos.x + y = this.options.attributePos.y ? this.options.attributePos.y : 0 + } + lineLength = Math.sqrt((x * x) + (y * y)).toFixed(2); + lineAngleRad = Math.atan(x / y); + lineAngle = parseFloat((lineAngleRad * 180 / Math.PI).toFixed(2)); + if (this.options.attributePos.y < 0) { + lineAngle = lineAngle + 180 + } + // if(this.options.attributePos.y<-this.options.attributePos.height/2) { + // lineElm.style.bottom = 'unset' + // lineElm.style.top = '0' + // } + // else { + // lineElm.style.bottom = -lineLength + 'px' + // lineElm.style.top = 'unset' + // } + lineElm.style.height = lineLength + 'px' + lineElm.style.transform = 'rotate(' + lineAngle + 'deg)' + } + } + } + + function getCurrentBillboardScale(entity, scene) { + // 获取相机到Billboard的距离 + const distance = Cesium.Cartesian3.distance( + scene.camera.positionWC, + entity.position.getValue() + ); + // 获取缩放距离配置 + const scaleByDistance = entity.billboard.scaleByDistance ? entity.billboard.scaleByDistance.getValue() : undefined; + + if (!scaleByDistance) { + // 如果没有设置距离缩放,则使用基础缩放值 + return 1.0; + } + + // 解析缩放距离参数 [near, nearScale, far, farScale] + const { near, nearValue, far, farValue } = scaleByDistance; + if (distance <= near) { + return nearValue; + } else if (distance >= far) { + return farValue; + } else { + // 计算中间距离的缩放值(线性插值) + const t = (distance - near) / (far - near); + return Cesium.Math.lerp(nearValue, farValue, t); + } + } this.sdk.addIncetance(this.options.id, this) @@ -243,6 +354,7 @@ class BillboardObject extends Base { that.entity.billboard.imgHeight = 0 that.entity.billboard.image = canvas addCluster(that.sdk, that.entity) + that.attributeBoxState && (that.attributeBoxState = true) } return } @@ -267,6 +379,7 @@ class BillboardObject extends Base { return img }, false) addCluster(that.sdk, that.entity) + that.attributeBoxState && (that.attributeBoxState = true) } }) } @@ -298,6 +411,7 @@ class BillboardObject extends Base { that.entity.billboard.imgHeight = height that.entity.billboard.image = canvas addCluster(that.sdk, that.entity) + that.attributeBoxState && (that.attributeBoxState = true) } } image.onerror = function (err) { @@ -309,6 +423,7 @@ class BillboardObject extends Base { that.entity.billboard.imgHeight = 0 that.entity.billboard.image = canvas addCluster(that.sdk, that.entity) + that.attributeBoxState && (that.attributeBoxState = true) } }; } @@ -465,15 +580,36 @@ class BillboardObject extends Base { return this.options.show } set show(v) { - if(!this.isShowView) { + if (!this.isShowView) { this.options.show = v this.originalOptions.show = v } - if(!this.showView || this.showView == 3) { + if (!this.showView || this.showView == 3) { this.entity && (this.entity.show = this.options.show) + if (this.attributeBoxState && this.options.show) { + this.attributeBoxState = this.options.show + } + else { + // 关闭属性框 + document.addEventListener('mouseup', this.#_destroyMouseEvent); + document.addEventListener('mouseleave', this.#_destroyMouseEvent); + if (this.attributeElm) { + this.sdk.viewer._element.removeChild(this.attributeElm) + this.attributeElm = null + } + this.sdk.viewer.scene.postRender.removeEventListener(this.#_postRenderEvent) + } } else { this.entity && (this.entity.show = false) + // 关闭属性框 + document.addEventListener('mouseup', this.#_destroyMouseEvent); + document.addEventListener('mouseleave', this.#_destroyMouseEvent); + if (this.attributeElm) { + this.sdk.viewer._element.removeChild(this.attributeElm) + this.attributeElm = null + } + this.sdk.viewer.scene.postRender.removeEventListener(this.#_postRenderEvent) } syncData(this.sdk, this.options.id) syncSplitData(this.sdk, this.options.id) @@ -1716,6 +1852,22 @@ class BillboardObject extends Base { this.cameraSelect && this.cameraSelect() this.ISCSelect && this.ISCSelect() this.goodsSelect && this.goodsSelect() + + let col = document.createElement('div') + col.className = 'col' + col.style.flex = '0 0 80px' + col.innerHTML = ` + 属性框 + + ` + + let row = this._DialogObject._element.content.getElementsByClassName('attribute')[0].getElementsByClassName('row')[0] + row.appendChild(col) + let boxSwitch = col.getElementsByClassName('btn-switch')[0] + boxSwitch.checked = this.attributeBoxState + boxSwitch.addEventListener('change', (e) => { + this.attributeBoxState = boxSwitch.checked + }) let tagData = this.attributeSelect let attributeElm = this._DialogObject._element.content.getElementsByClassName( 'attribute-select-box' @@ -2736,6 +2888,220 @@ class BillboardObject extends Base { (this.originalOptions.customView = this.options.customView) } } + + get attributeBoxState() { + return this.options.attributeBoxState + } + + set attributeBoxState(state) { + state = state ? true : false + this.options.attributeBoxState = state + document.addEventListener('mouseup', this.#_destroyMouseEvent); + document.addEventListener('mouseleave', this.#_destroyMouseEvent); + if (this.attributeElm) { + this.sdk.viewer._element.removeChild(this.attributeElm) + this.attributeElm = null + } + this.sdk.viewer.scene.postRender.removeEventListener(this.#_postRenderEvent) + if (state && this.sdk && this.sdk.viewer && this.sdk.viewer._element && this.show) { + let attributeElm = document.createElement('div') + this.attributeElm = attributeElm + attributeElm.className = 'billboard-attribute-box' + attributeElm.style.top = '0px' + attributeElm.style.left = '0px' + attributeElm.style.width = 0 + attributeElm.style.height = 0 + // attributeElm.innerHTML = this.options.richTextContent + this.sdk.viewer._element.appendChild(attributeElm) + let linkHtml = '' + let goodsHtml = '' + let richTextHtml = '' + for (let i = 0; i < this.options.attribute.link.content.length; i++) { + linkHtml += `` + } + if (this.options.attribute.goods && this.options.attribute.goods.content && this.options.attribute.goods.content.length > 0) { + goodsHtml += ` +
+
+
+
序号
+
名称
+
数量
+
+
+
+ ` + for (let i = 0; i < this.options.attribute.goods.content.length; i++) { + goodsHtml += `
+
${i + 1}
+
${this.options.attribute.goods.content[i].name}
+
${this.options.attribute.goods.content[i].cnt}
+
` + } + goodsHtml += `
` + } + if (this.options.richTextContent) { + richTextHtml = ` + ${this.options.richTextContent} + ` + } + + let boxHtml = ` + + + ` + + if (!linkHtml && !goodsHtml && !richTextHtml) { + boxHtml = boxHtml + '

暂无属性信息

' + } + else { + boxHtml = boxHtml + ` + + ${richTextHtml} + ${goodsHtml} + ${linkHtml} + +
` + } + + attributeElm.innerHTML = boxHtml + + if (attributeElm.getElementsByClassName('tabs')[0]) { + let tabsElm = new cy_tabs(attributeElm.getElementsByClassName('tabs')[0], undefined, this.sdk) + } + + let imgElm = attributeElm.getElementsByTagName('img') + for (let i = 0; i < imgElm.length; i++) { + if (!imgElm[i].style.width) { + imgElm[i].style.width = '100%' + } + } + + this.sdk.viewer.scene.postRender.addEventListener(this.#_postRenderEvent) + + let leftTopElm = attributeElm.getElementsByClassName('left-top')[0] + let rightTopElm = attributeElm.getElementsByClassName('right-top')[0] + let leftOnmousedown = (e) => { + console.log(1111111111) + if (this.options.attributePos.width < 200) { + this.options.attributePos.width = 200 + } + if (this.options.attributePos.height < 120) { + this.options.attributePos.height = 120 + } + let x = e.x + let y = e.y + let width = this.options.attributePos.width + let height = this.options.attributePos.height + let positionx = this.options.attributePos.x + this.sdk.viewer._element.onmousemove = (e2) => { + this.options.attributePos.width = width + (x - e2.x) + this.options.attributePos.height = height + (y - e2.y) + if (this.options.attributePos.width < 200) { + this.options.attributePos.width = 200 + } + else { + this.options.attributePos.x = positionx - (x - e2.x) + } + if (this.options.attributePos.height < 120) { + this.options.attributePos.height = 120 + } + // this.options.attributePos.y = positiony + (y - e2.y) + } + document.addEventListener('mouseup', this.#_destroyMouseEvent); + document.addEventListener('mouseleave', this.#_destroyMouseEvent); + } + let rightOnmousedown = (e) => { + let x = e.x + let y = e.y + if (this.options.attributePos.width < 200) { + this.options.attributePos.width = 200 + } + if (this.options.attributePos.height < 120) { + this.options.attributePos.height = 120 + } + let width = this.options.attributePos.width + let height = this.options.attributePos.height + this.sdk.viewer._element.onmousemove = (e2) => { + this.options.attributePos.width = width + (e2.x - x) + this.options.attributePos.height = height + (y - e2.y) + } + document.addEventListener('mouseup', this.#_destroyMouseEvent); + document.addEventListener('mouseleave', this.#_destroyMouseEvent); + } + // leftTopElm.onmousedown = (e) => { + // console.log(1111111111) + // if (this.options.attributePos.width < 200) { + // this.options.attributePos.width = 200 + // } + // if (this.options.attributePos.height < 120) { + // this.options.attributePos.height = 120 + // } + // let x = e.x + // let y = e.y + // let width = this.options.attributePos.width + // let height = this.options.attributePos.height + // let positionx = this.options.attributePos.x + // this.sdk.viewer._element.onmousemove = (e2) => { + // this.options.attributePos.width = width + (x - e2.x) + // this.options.attributePos.height = height + (y - e2.y) + // if (this.options.attributePos.width < 200) { + // this.options.attributePos.width = 200 + // } + // else { + // this.options.attributePos.x = positionx - (x - e2.x) + // } + // if (this.options.attributePos.height < 120) { + // this.options.attributePos.height = 120 + // } + // // this.options.attributePos.y = positiony + (y - e2.y) + // } + // document.addEventListener('mouseup', this.#_destroyMouseEvent); + // document.addEventListener('mouseleave', this.#_destroyMouseEvent); + // } + // rightTopElm.onmousedown = (e) => { + // let x = e.x + // let y = e.y + // if (this.options.attributePos.width < 200) { + // this.options.attributePos.width = 200 + // } + // if (this.options.attributePos.height < 120) { + // this.options.attributePos.height = 120 + // } + // let width = this.options.attributePos.width + // let height = this.options.attributePos.height + // this.sdk.viewer._element.onmousemove = (e2) => { + // this.options.attributePos.width = width + (e2.x - x) + // this.options.attributePos.height = height + (y - e2.y) + // } + // document.addEventListener('mouseup', this.#_destroyMouseEvent); + // document.addEventListener('mouseleave', this.#_destroyMouseEvent); + // } + + attributeElm.onmousedown = (e) => { + attributeElm.style.pointerEvents = 'none' + if (e.target.className.indexOf('left-top') != -1) { + leftOnmousedown(e) + } + else if (e.target.className.indexOf('right-top') != -1) { + rightOnmousedown(e) + } + else { + let x = e.x + let y = e.y + let oldX = this.options.attributePos.x + let oldXY = this.options.attributePos.y + let height = this.options.attributePos.height + this.sdk.viewer._element.onmousemove = (e2) => { + this.options.attributePos.x = oldX + (e2.x - x) + this.options.attributePos.y = oldXY - (e2.y - y) + } + document.addEventListener('mouseup', this.#_destroyMouseEvent); + document.addEventListener('mouseleave', this.#_destroyMouseEvent); + } + } + } + } } export default BillboardObject diff --git a/src/Obj/Element/cy_html_tabs.js b/src/Obj/Element/cy_html_tabs.js index 1270e29..464c37b 100644 --- a/src/Obj/Element/cy_html_tabs.js +++ b/src/Obj/Element/cy_html_tabs.js @@ -1,6 +1,13 @@ class cy_tabs { - constructor(id, clickTabCallBack, sdk) { - let elm = document.getElementById(id); + constructor(boxElm, clickTabCallBack, sdk) { + let elm + if(typeof boxElm === 'string') { + elm = document.getElementById(boxElm); + } + else { + elm = boxElm + } + // let elm = document.getElementById(id); let pane = elm.getElementsByTagName('DIV-cy-tab-pane') let tabTop = `
` @@ -37,7 +44,9 @@ tabContent = tabContent + `
` let BoxElm = document.createElement('div'); - BoxElm.setAttribute('id', id) + if(typeof boxElm === 'string') { + BoxElm.setAttribute('id', boxElm) + } BoxElm.setAttribute('class', 'DIV-cy-tabs') BoxElm.innerHTML = tabTop + tabContent elm.parentNode.insertBefore(BoxElm, elm); From 2aade7eef96d8761a900feb28dc26d5c3e630267 Mon Sep 17 00:00:00 2001 From: zh <972939975@qq.com> Date: Thu, 21 Aug 2025 16:37:49 +0800 Subject: [PATCH 02/17] =?UTF-8?q?=E8=BD=A8=E8=BF=B9=E8=BF=90=E5=8A=A8?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=B2=B9=E8=80=97=E5=B1=9E=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Obj/Base/LabelObject/index.js | 34 ++--- src/Obj/Base/TrajectoryMotion/_element.js | 17 +++ src/Obj/Base/TrajectoryMotion/index.js | 132 ++++++++++++++++--- static/custom/css/index.css | 152 +++++++++++++++++++++- 4 files changed, 302 insertions(+), 33 deletions(-) diff --git a/src/Obj/Base/LabelObject/index.js b/src/Obj/Base/LabelObject/index.js index dfd9dc1..322dd8a 100644 --- a/src/Obj/Base/LabelObject/index.js +++ b/src/Obj/Base/LabelObject/index.js @@ -14,6 +14,8 @@ import { class LabelObject extends Base { #updateBillboardImageTimeout + #canvas = document.createElement('canvas') + #canvas2 = document.createElement('canvas') constructor(sdk, options = {}, model) { super(sdk, options) this.model = model @@ -438,11 +440,12 @@ class LabelObject extends Base { this.updateBillboardImage() } updateBillboardImage() { - clearTimeout(this.#updateBillboardImageTimeout) - this.#updateBillboardImageTimeout = setTimeout(() => { - clearTimeout(this.#updateBillboardImageTimeout) - this.entity.billboard.image = this.getcanvas() - }, 500) + this.entity.billboard.image = this.getcanvas() + // clearTimeout(this.#updateBillboardImageTimeout) + // this.#updateBillboardImageTimeout = setTimeout(() => { + // clearTimeout(this.#updateBillboardImageTimeout) + // this.entity.billboard.image = this.getcanvas() + // }, 500) } get lineColor() { return this.options.pixelOffset @@ -489,9 +492,8 @@ class LabelObject extends Base { // } getcanvas() { - const canvas = document.createElement('canvas') - const ctx = canvas.getContext('2d') - + const ctx = this.#canvas.getContext('2d') + ctx.clearRect(0, 0, this.#canvas.width, this.#canvas.height); ctx.font = this.options.fontSize + 'px ' + this.font let texts = this.options.text.split('\n') let canvasWidth = 0 @@ -509,9 +511,8 @@ class LabelObject extends Base { if (canvasWidth < this.options.lineWidth) { canvasWidth = this.options.lineWidth } - canvas.width = canvasWidth - - canvas.height = this.options.pixelOffset + canvasHeight + this.#canvas.width = canvasWidth + this.#canvas.height = this.options.pixelOffset + canvasHeight const linearGradient = ctx.createLinearGradient( 0, 0, @@ -558,15 +559,14 @@ class LabelObject extends Base { ctx.stroke() ctx.closePath() - const canvas2 = document.createElement('canvas') - const ctx2 = canvas2.getContext('2d') - canvas2.width = canvas.width + 10 - canvas2.height = canvas.height + 10 - ctx2.drawImage(canvas, 5, 5); + const ctx2 = this.#canvas2.getContext('2d') + this.#canvas2.width = this.#canvas.width + 10 + this.#canvas2.height = this.#canvas.height + 10 + ctx2.drawImage(this.#canvas, 5, 5); // const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); // ctx.putImageData(imageData, 40, 40); - return canvas2 + return this.#canvas2.toDataURL("image/png") } remove() { diff --git a/src/Obj/Base/TrajectoryMotion/_element.js b/src/Obj/Base/TrajectoryMotion/_element.js index 3303918..81497e3 100644 --- a/src/Obj/Base/TrajectoryMotion/_element.js +++ b/src/Obj/Base/TrajectoryMotion/_element.js @@ -182,6 +182,23 @@ function html() { +
+
+
+ 油耗 +
+ + L/100km + +
+
+
+ 总油耗 + +
+
+
+ ` } diff --git a/src/Obj/Base/TrajectoryMotion/index.js b/src/Obj/Base/TrajectoryMotion/index.js index 56a8c6e..7e5af05 100644 --- a/src/Obj/Base/TrajectoryMotion/index.js +++ b/src/Obj/Base/TrajectoryMotion/index.js @@ -78,6 +78,7 @@ class TrajectoryMotion extends Base { this.options.line.smooth = options.line.smooth ? options.line.smooth : false this.options.line.noseToTail = options.line.noseToTail ? options.line.noseToTail : false this.positions_smooth = [] + this.options.unitFuelConsumption = options.unitFuelConsumption || 0 this.options.ground = options.ground || false this.options.state = (options.state || options.state === false) ? options.state : true this.options.routeDirection = (options.routeDirection || options.routeDirection === false) ? options.routeDirection : true @@ -180,6 +181,7 @@ class TrajectoryMotion extends Base { this.realTimeLine && (this.realTimeLine.show = (!this.showView || this.showView == 3) ? true : false) } this.label && (this.label.show = (!this.showView || this.showView == 3) ? this.options.label.show : false) + this.fuelLabel && (this.fuelLabel.show = (!this.showView || this.showView == 3) ? this.options.fuelShow : false) } else { this.model.show = (!this.showView || this.showView == 3) ? this.options.show : false @@ -202,6 +204,7 @@ class TrajectoryMotion extends Base { this.keyPoints[i].show = (!this.showView || this.showView == 3) ? show : false } this.label && (this.label.show = false) + this.fuelLabel && (this.fuelLabel.show = false) this.viewFollow = false } @@ -256,14 +259,17 @@ class TrajectoryMotion extends Base { // Cesium.Matrix4.multiplyByTranslation(this.model.modelMatrix, new Cesium.Cartesian3(0, 0, -difference), this.model.modelMatrix) // Cesium.Matrix4.getTranslation(this.model.modelMatrix, this.model.position) this.label && (this.label.show = this.label.show) + this.fuelLabel && (this.fuelLabel.show = this.fuelLabel.show) if (this.options.label.position) { setTimeout(() => { if (this.options.label.position.alt) { this.label && (this.label.position = [this.options.label.position.lng, this.options.label.position.lat, this.options.label.position.alt]) + this.fuelLabel && (this.fuelLabel.position = [this.options.label.position.lng, this.options.label.position.lat, this.options.label.position.alt]) } else { this.getClampToHeight({ lng: this.options.label.position.lng, lat: this.options.label.position.lat }).then((height) => { this.label && (this.label.position = [this.options.label.position.lng, this.options.label.position.lat, height]) + this.fuelLabel && (this.fuelLabel.position = [this.options.label.position.lng, this.options.label.position.lat, height]) }) } }, 0) @@ -1163,18 +1169,21 @@ class TrajectoryMotion extends Base { show = false } if (this.show) { - this.label && (this.label.show = show) - if (this.options.label.position) { - setTimeout(() => { - if (this.options.label.position.alt) { - this.label && (this.label.position = [this.options.label.position.lng, this.options.label.position.lat, this.options.label.position.alt]) - } - else { - this.getClampToHeight({ lng: this.options.label.position.lng, lat: this.options.label.position.lat }).then((height) => { - this.label && (this.label.position = [this.options.label.position.lng, this.options.label.position.lat, height]) - }) - } - }, 0); + if (this.label) { + this.label.show = show + this.label.pixelOffset = this.options.label.pixelOffset + (this.fuelShow ? this.labelFontSize + 20 : 0) + if (this.options.label.position) { + setTimeout(() => { + if (this.options.label.position.alt) { + this.label && (this.label.position = [this.options.label.position.lng, this.options.label.position.lat, this.options.label.position.alt]) + } + else { + this.getClampToHeight({ lng: this.options.label.position.lng, lat: this.options.label.position.lat }).then((height) => { + this.label && (this.label.position = [this.options.label.position.lng, this.options.label.position.lat, height]) + }) + } + }, 0); + } } } else { @@ -1192,6 +1201,7 @@ class TrajectoryMotion extends Base { set labelFontFamily(v) { this.options.label.fontFamily = v || 0 this.label && (this.label.fontFamily = this.options.label.fontFamily) + this.fuelLabel && (this.fuelLabel.fontFamily = this.options.label.fontFamily) let name = getFontFamilyName(this.labelFontFamily) || '' this._elms.labelFontFamily && @@ -1206,6 +1216,7 @@ class TrajectoryMotion extends Base { set labelColor(v) { this.options.label.color = v this.label && (this.label.color = v) + this.fuelLabel && (this.fuelLabel.color = v) if (this._elms.labelColor) { this._elms.labelColor.forEach((item, i) => { let labelColorPicker = new YJColorPicker({ @@ -1233,6 +1244,13 @@ class TrajectoryMotion extends Base { set labelFontSize(v) { this.options.label.fontSize = v this.label && (this.label.fontSize = v) + if (this.fuelLabel) { + this.fuelLabel.fontSize = v + this.label.pixelOffset = this.options.label.pixelOffset + v + 20 + } + else { + this.label.pixelOffset = this.options.label.pixelOffset + } this._elms.labelFontSize && this._elms.labelFontSize.forEach((item) => { item.value = v }) @@ -1244,6 +1262,7 @@ class TrajectoryMotion extends Base { set labelScaleByDistance(v) { this.options.label.scaleByDistance = v this.label && (this.label.scaleByDistance = v) + this.fuelLabel && (this.fuelLabel.scaleByDistance = v) this._elms.labelScaleByDistance && this._elms.labelScaleByDistance.forEach((item) => { item.checked = v }) @@ -1259,6 +1278,7 @@ class TrajectoryMotion extends Base { } this.options.label.near = near this.label && (this.label.near = near) + this.fuelLabel && (this.fuelLabel.near = near) this._elms.labelNear && this._elms.labelNear.forEach((item) => { item.value = near }) @@ -1274,11 +1294,66 @@ class TrajectoryMotion extends Base { } this.options.label.far = far this.label && (this.label.far = far) + this.fuelLabel && (this.fuelLabel.far = far) this._elms.labelFar && this._elms.labelFar.forEach((item) => { item.value = far }) } + get unitFuelConsumption() { + return this.options.unitFuelConsumption + } + + set unitFuelConsumption(v) { + this.options.unitFuelConsumption = v + this._elms.unitFuelConsumption && this._elms.unitFuelConsumption.forEach((item) => { + item.value = v + }) + } + + get fuelShow() { + return this.options.fuelShow + } + + set fuelShow(v) { + this.options.fuelShow = v + let show = v + if (this.show && (!this.showView || this.showView == 3)) { + show = v + } + else { + show = false + } + if (this.show) { + if (this.fuelLabel) { + this.fuelLabel.show = show + this.label.pixelOffset = this.options.label.pixelOffset + (show ? this.labelFontSize + 20 : 0) + } + else { + this.label.pixelOffset = this.options.label.pixelOffset + } + if (this.options.label.position) { + setTimeout(() => { + if (this.options.label.position.alt) { + this.fuelLabel && (this.fuelLabel.position = [this.options.label.position.lng, this.options.label.position.lat, this.options.label.position.alt]) + } + else { + this.getClampToHeight({ lng: this.options.label.position.lng, lat: this.options.label.position.lat }).then((height) => { + this.fuelLabel && (this.fuelLabel.position = [this.options.label.position.lng, this.options.label.position.lat, height]) + }) + } + }, 0); + } + } + else { + this.fuelLabel && (this.fuelLabel.show = false) + this.label.pixelOffset = this.options.label.pixelOffset + } + this._elms.fuelShow && this._elms.fuelShow.forEach((item) => { + item.checked = v + }) + } + // 创建路径 static addLine(that) { let positions_smooth = that.renewLinePositions(that.options.line.positions) @@ -1397,6 +1472,7 @@ class TrajectoryMotion extends Base { } let pos = that.smooth ? that.positions_smooth : Cesium.Cartesian3.fromDegreesArrayHeights(fromDegreesArrayHeights) TrajectoryMotion.createLabel(that) + TrajectoryMotion.createFuelLabel(that) that.modelMove(pos) @@ -1406,13 +1482,13 @@ class TrajectoryMotion extends Base { static async createLabel(that) { let labelPosition = that.cartesian3Towgs84(that.model.position, that.sdk.viewer) that.label = new LabelObject(that.sdk, { - show: that.options.show ? (that.options.model.show ? that.options.label.show : false) : false, + show: that.options.show ? (that.options.label.show ? true : false) : false, position: [labelPosition.lng, labelPosition.lat, labelPosition.alt], text: that.options.name, fontSize: that.options.label.fontSize, fontFamily: that.options.label.fontFamily, color: that.options.label.color, - pixelOffset: that.options.label.pixelOffset, + pixelOffset: that.options.label.pixelOffset + (that.options.fuelShow ? that.options.label.fontSize + 20 : 0), backgroundColor: that.options.label.backgroundColor, lineColor: that.options.label.lineColor, lineWidth: that.options.label.lineWidth, @@ -1422,6 +1498,26 @@ class TrajectoryMotion extends Base { }, that.model) } + static async createFuelLabel(that) { + let labelPosition = that.cartesian3Towgs84(that.model.position, that.sdk.viewer) + that.fuelLabel = new LabelObject(that.sdk, { + show: that.options.show ? (that.options.fuelShow ? true : false) : false, + // show: true, + position: [labelPosition.lng, labelPosition.lat, labelPosition.alt], + text: '总油耗:', + fontSize: that.options.label.fontSize, + fontFamily: that.options.label.fontFamily, + color: that.options.label.color, + pixelOffset: 0, + backgroundColor: ['#6e6e6e', '#6e6e6e'], + lineColor: '#00ffff00', + lineWidth: 0, + scaleByDistance: that.options.label.scaleByDistance, + near: that.options.label.near, + far: that.options.label.far + }, that.model) + } + // 创建关键点 static async addKeyPoint(that) { for (let i = 0; i < that.options.line.positions.length; i++) { @@ -1527,7 +1623,7 @@ class TrajectoryMotion extends Base { setPosition(startDistance) setTimeout(() => { _this.model.isMove = false - }, 500); + }, 1000); animateUpdate() @@ -1550,6 +1646,8 @@ class TrajectoryMotion extends Base { } async function setPosition(distance) { + _this.totalFuelConsumption = Number((distance / 100 * _this.unitFuelConsumption).toFixed(2)) + _this.fuelLabel.text = '总油耗:' + _this.totalFuelConsumption + ' L' _this.model.isMove = true let sdk2D = get2DView() let splitSdk = getSdk() @@ -1761,6 +1859,7 @@ class TrajectoryMotion extends Base { } let labelPosition = _this.cartesian3Towgs84(position, _this.sdk.viewer) _this.label.position = [labelPosition.lng, labelPosition.lat, labelPosition.alt] + _this.fuelLabel.position = [labelPosition.lng, labelPosition.lat, labelPosition.alt] lastDistance = distance // console.log(position) _this.realTimeRouteArray.push(position) @@ -2157,6 +2256,7 @@ class TrajectoryMotion extends Base { this.sdk.viewer.entities.remove(this.line) this.sdk.viewer.entities.remove(this.realTimeLine) this.label && this.label.remove() + this.fuelLabel && this.fuelLabel.remove() for (let i = 0; i < this.keyPointShow.length; i++) { this.sdk.viewer.entities.remove(this.keyPointShow[i]) } @@ -2164,6 +2264,7 @@ class TrajectoryMotion extends Base { this.realTimeLine = null this.model = null this.label = null + this.fuelLabel = null if (this._DialogObject && !this._DialogObject.isDestroy) { this._DialogObject.close() this._DialogObject = null @@ -2217,6 +2318,7 @@ class TrajectoryMotion extends Base { this.model && (this.model.show = false) } this.labelShow = this.originalOptions.label.show + this.fuelLabelShow = this.originalOptions.fuelShow this.labelColor = this.originalOptions.label.color this.labelFontSize = this.originalOptions.label.fontSize this.labelFontFamily = this.originalOptions.label.fontFamily diff --git a/static/custom/css/index.css b/static/custom/css/index.css index f7ce847..f5d1ff7 100644 --- a/static/custom/css/index.css +++ b/static/custom/css/index.css @@ -1429,6 +1429,24 @@ margin-bottom: 10px; display: flex; position: relative; + overflow-y: auto; +} + +.DIV-cy-tabs .DIV-cy-tab-top::-webkit-scrollbar { + width: 4px; + height: 4px; +} + +.DIV-cy-tabs .DIV-cy-tab-top::-webkit-scrollbar-thumb { + border-radius: 5px; + -webkit-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2); + background-color: rgba(var(--color-sdk-base-rgb)); +} + +.DIV-cy-tabs .DIV-cy-tab-top::-webkit-scrollbar-track { + -webkit-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2); + border-radius: 5px; + background-color: rgba(var(--color-sdk-base-rgb), 0.1); } .DIV-cy-tabs .DIV-cy-tab-top::after { @@ -1473,7 +1491,14 @@ border-bottom: 2px solid #dddddd00; position: relative; z-index: 2; + white-space: nowrap; + user-select: none; cursor: pointer; + -webkit-pointer-events: auto; + -moz-pointer-events: auto; + -ms-pointer-events: auto; + -o-pointer-events: auto; + pointer-events: auto; } .DIV-cy-tabs .DIV-cy-tab-pane-title-p span { @@ -3550,22 +3575,35 @@ border: 1.5px solid; border-image: linear-gradient(to bottom, var(--color-sdk-gradual)) 1; color: #fff; + min-width: 200px; + min-height: 120px; + box-sizing: border-box; + /* -webkit-pointer-events: none; + -moz-pointer-events: none; + -ms-pointer-events: none; + -o-pointer-events: none; + pointer-events: none; */ } + .billboard-attribute-box .DIV-cy-tabs { height: 100%; display: flex; flex-direction: column; } + .billboard-attribute-box .DIV-cy-tabs .DIV-cy-tab-top .DIV-cy-tab-pane-title { padding: 0 2px; } + .billboard-attribute-box .DIV-cy-tabs .DIV-cy-tab-top .DIV-cy-tab-pane-title:first-child { padding-left: 0; } + .billboard-attribute-box .DIV-cy-tabs .DIV-cy-tab-top .DIV-cy-tab-pane-title:last-child { padding-right: 0; } -.billboard-attribute-box .DIV-cy-tabs .DIV-cy-tab-top .DIV-cy-tab-pane-title span{ + +.billboard-attribute-box .DIV-cy-tabs .DIV-cy-tab-top .DIV-cy-tab-pane-title span { margin: 0 5px; } @@ -3573,4 +3611,116 @@ padding: 0 5px 5px 5px; box-sizing: border-box; flex: 1; + overflow: auto; +} + +.billboard-attribute-box .DIV-cy-tabs .DIV-cy-tab-content::-webkit-scrollbar { + width: 8px; + height: 8px; +} + +.billboard-attribute-box .DIV-cy-tabs .DIV-cy-tab-content::-webkit-scrollbar-thumb { + border-radius: 5px; + -webkit-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2); + background-color: rgba(var(--color-sdk-base-rgb)); +} + +.billboard-attribute-box .DIV-cy-tabs .DIV-cy-tab-content::-webkit-scrollbar-track { + -webkit-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2); + border-radius: 5px; + background-color: rgba(var(--color-sdk-base-rgb), 0.1); +} + +.billboard-attribute-box .DIV-cy-tabs .DIV-cy-tab-content-pane { + width: 100%; + height: 100%; +} + +.billboard-attribute-box .DIV-cy-tabs .DIV-cy-tab-content-pane iframe { + border: none; +} + +.billboard-attribute-box .billboard-attribute-box-line { + position: absolute; + width: 0px; + /* border-left: 1px solid rgba(var(--color-sdk-base-rgb), 0.5); */ + border-left: 1px solid rgba(var(--color-sdk-base-rgb), 1); + /* transform: rotate(45deg); */ + transform-origin: 0px 0px; + -webkit-pointer-events: none; + -moz-pointer-events: none; + -ms-pointer-events: none; + -o-pointer-events: none; + pointer-events: none; +} + +.billboard-attribute-box .drag-nook { + position: absolute; + width: 12px; + height: 12px; + display: block; + user-select: none; + -webkit-pointer-events: auto; + -moz-pointer-events: auto; + -ms-pointer-events: auto; + -o-pointer-events: auto; + pointer-events: auto; + z-index: 3; + clip-path: polygon(0% 100%, 100% 100%, 50% 50%); + background-image: linear-gradient(to top, #ffffff 1px, #00000000 1px); + background-size: 100% 3px; + /* background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAJRJREFUWEftltsJgFAMQ5NNdBJ1Eh3NTdRJdJPIBQXF50/p/WgHSNNDCSGch877EQbyIyCpBtAa/cZEsj9qXwhImgEURgZA8rTzzkAiYGVgITm+ErC6/Ek3vycMAkHAnYCkAUDKAosZSTaeSZiCqIwgCgJBIG8CWyXzKySSZBGBu+afStYBqIxMfJdSo8WPslHJgsAKWjkmIRBy/c8AAAAASUVORK5CYII='); */ +} + +.billboard-attribute-box .drag-nook.left-top { + top: -6px; + left: -6px; + cursor: se-resize; + transform: rotate(-45deg); + display: none; +} + +.billboard-attribute-box .drag-nook.right-top { + top: -6px; + right: -6px; + cursor: ne-resize; + transform: rotate(45deg); + display: none; +} + +.billboard-attribute-box .table { + background-color: #ffffff00; + color: #ffffff; + overflow: hidden; + border: 1px solid rgba(var(--color-sdk-base-rgb), 0.5); +} + +.billboard-attribute-box .table .table-head .tr { + border-top: none; + border-left: none; + border-right: none; +} + +.billboard-attribute-box .table .tr { + display: flex; + border: 1px solid rgba(var(--color-sdk-base-rgb), 0.5); + border-right: none; +} +.billboard-attribute-box .table .tr .th, .billboard-attribute-box .table .tr .td { + border-right: 1px solid rgba(var(--color-sdk-base-rgb), 0.5); + display: flex; + justify-content: center; +} +.billboard-attribute-box .table .tr .th:last-child, .billboard-attribute-box .table .tr .td:last-child { + border-right: none; +} + + +.billboard-attribute-box .table .table-body .tr { + border-bottom: none; + border-left: none; +} + +.billboard-attribute-box .table .table-body .tr:first-child { + border-top: none; } \ No newline at end of file From 15460702f9dcabbab494af6255ab833d76e14bc1 Mon Sep 17 00:00:00 2001 From: zh <972939975@qq.com> Date: Fri, 22 Aug 2025 15:48:18 +0800 Subject: [PATCH 03/17] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=B1=9E=E6=80=A7?= =?UTF-8?q?=E4=BF=A1=E6=81=AF=E5=90=8E=E5=90=8C=E6=AD=A5=E5=B1=9E=E6=80=A7?= =?UTF-8?q?=E6=A1=86=E5=86=85=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Obj/Base/BillboardObject/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Obj/Base/BillboardObject/index.js b/src/Obj/Base/BillboardObject/index.js index 8ce5099..9f37bdc 100644 --- a/src/Obj/Base/BillboardObject/index.js +++ b/src/Obj/Base/BillboardObject/index.js @@ -1855,7 +1855,7 @@ class BillboardObject extends Base { let col = document.createElement('div') col.className = 'col' - col.style.flex = '0 0 80px' + col.style.flex = '0 0 110px' col.innerHTML = ` 属性框 @@ -2379,6 +2379,7 @@ class BillboardObject extends Base { this.attributeCamera = this.options.attribute.camera.content this.attributeGoods = this.options.attribute.goods.content this.attributeISC = this.options.attribute.ISC.content + this.attributeBoxState = this.options.attributeBoxState this.cameraSelect && this.cameraSelect() this.goodsSelect && this.goodsSelect() } From 1a1aa9f6a5f4e302ceafdc4cadd569487d3f2996 Mon Sep 17 00:00:00 2001 From: zh <972939975@qq.com> Date: Fri, 22 Aug 2025 17:51:45 +0800 Subject: [PATCH 04/17] =?UTF-8?q?=E5=B1=9E=E6=80=A7=E6=A1=86=E4=BD=8D?= =?UTF-8?q?=E7=BD=AE=E9=AB=98=E5=BA=A6=E8=AE=A1=E7=AE=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Obj/Base/BillboardObject/index.js | 35 +++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/src/Obj/Base/BillboardObject/index.js b/src/Obj/Base/BillboardObject/index.js index 9f37bdc..98f8857 100644 --- a/src/Obj/Base/BillboardObject/index.js +++ b/src/Obj/Base/BillboardObject/index.js @@ -42,6 +42,7 @@ import { getGoodsList } from '../../../Tools/getGoodsList' class BillboardObject extends Base { #_postRenderEvent = null #_destroyMouseEvent = null + #_billboardHeight = 0 /** @@ -122,6 +123,7 @@ class BillboardObject extends Base { this.options.positions.alt = Number( Number(options.positions.alt || 0).toFixed(2) ) + this.#_billboardHeight = this.options.positions.alt // this.options.diffuseShow = options.diffuseShow || false // this.options.diffuseRadius = (options.diffuseRadius || options.diffuseRadius === 0) ? options.diffuseRadius : 10 // this.options.diffuseDuration = (options.diffuseDuration || options.diffuseDuration === 0) ? options.diffuseDuration : 2000 @@ -203,7 +205,8 @@ class BillboardObject extends Base { this.#_postRenderEvent = () => { let siteInfoPosition = Cesium.Cartesian3.fromDegrees( this.options.positions.lng, - this.options.positions.lat + this.options.positions.lat, + this.#_billboardHeight ) if (this.attributeElm && this.entity) { let winpos = this.sdk.viewer.scene.cartesianToCanvasCoordinates( @@ -698,6 +701,26 @@ class BillboardObject extends Base { if (this.entity) { this.entity.billboard.heightReference = heightMode this.entity.label.heightReference = heightMode + if(heightMode == Cesium.HeightReference.CLAMP_TO_GROUND) { + if (this.sdk.viewer.scene.terrainProvider.availability) { + Cesium.sampleTerrainMostDetailed( + this.sdk.viewer.scene.terrainProvider, + [ + Cesium.Cartographic.fromDegrees( + this.options.positions.lng, + this.options.positions.lat + ) + ] + ).then(position => { + this.#_billboardHeight = position[0].height + }) + } else { + this.#_billboardHeight = 0 + } + } + else { + this.#_billboardHeight = this.options.positions.alt + } } this._elms.heightMode && (this._elms.heightMode.value = heightModeName) } @@ -752,6 +775,7 @@ class BillboardObject extends Base { } set alt(v) { this.options.positions.alt = Number(Number(v).toFixed(2)) + this.#_billboardHeight = this.options.positions.alt // this.scan && (this.scan.alt = v) // this.diffuse && (this.diffuse.alt = v) this.renewPoint() @@ -2387,6 +2411,7 @@ class BillboardObject extends Base { async remove() { await remove_entity_from_cluster(this.sdk.viewer, this.entity) this.entity = null + this.attributeBoxState = false if (!this.sdk.viewer || !this.sdk.viewer.entities) { return } @@ -2592,6 +2617,7 @@ class BillboardObject extends Base { switch (this._elms.heightMode.value) { case '海拔高度': heightElm.value = this.options.positions.alt + this.#_billboardHeight = this.options.positions.alt break case '相对地表': if (this.sdk.viewer.scene.terrainProvider.availability) { @@ -2607,15 +2633,18 @@ class BillboardObject extends Base { heightElm.value = Number( (this.options.positions.alt - position[0].height).toFixed(2) ) + this.#_billboardHeight = this.options.positions.alt }) } else { heightElm.value = this.options.positions.alt + this.#_billboardHeight = this.options.positions.alt } break case '依附地表': break case '依附模型': heightElm.value = this.options.positions.alt + this.#_billboardHeight = this.options.positions.alt break } } @@ -2979,11 +3008,7 @@ class BillboardObject extends Base { } this.sdk.viewer.scene.postRender.addEventListener(this.#_postRenderEvent) - - let leftTopElm = attributeElm.getElementsByClassName('left-top')[0] - let rightTopElm = attributeElm.getElementsByClassName('right-top')[0] let leftOnmousedown = (e) => { - console.log(1111111111) if (this.options.attributePos.width < 200) { this.options.attributePos.width = 200 } From 427b38a6c39b92c241257413469a27f50f6d7c1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=8F=E5=A4=A7=E8=83=86?= <1101282782@qq.com> Date: Fri, 22 Aug 2025 18:26:57 +0800 Subject: [PATCH 05/17] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=BC=B9=E6=A1=86?= =?UTF-8?q?=E6=8A=96=E5=8A=A8=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- static/custom/css/index.css | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/static/custom/css/index.css b/static/custom/css/index.css index 421e48e..8e8ad55 100644 --- a/static/custom/css/index.css +++ b/static/custom/css/index.css @@ -3046,9 +3046,9 @@ /* 文本框 */ .popup-textarea{ /* width: 212px; */ - width: 161.6px; + width: 161px; /* height: 154px; */ - height: 119.2px; + height: 119px; display: block; pointer-events: none; position: absolute; @@ -3058,6 +3058,8 @@ padding: 5px 5px 0px 5px; } .popup-textarea textarea{ + width: 158px; + height: 95px; background-color: unset!important; border: unset!important; color: #fff; From fb2681fdbe15412dc9cab9f7b40517f64650c90b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=8F=E5=A4=A7=E8=83=86?= <1101282782@qq.com> Date: Fri, 22 Aug 2025 18:28:38 +0800 Subject: [PATCH 06/17] =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/In/index.js | 4 +- src/Obj/Base/BatchModel/_element_拓展.js | 229 +++ src/Obj/Base/Road/index.js | 8 +- src/Obj/Base/RoadObject/_element.js | 39 + src/Obj/Base/RoadObject/eventBinding.js | 92 + src/Obj/Base/RoadObject/index-8.4.js | 1549 +++++++++++++++ src/Obj/Base/RoadObject/index-last.js | 1704 +++++++++++++++++ src/Obj/Base/RoadObject/index-拐角连接.js | 889 +++++++++ src/Obj/Base/RoadObject/index-直接连接.js | 615 ++++++ src/Obj/Base/RoadObject/index-面大小一致.js | 1023 ++++++++++ src/Obj/Base/RoadObject/index.js | 1207 ++++++++++++ src/Obj/Element/Dialog/eventBinding.js | 10 +- .../Materail/RoadTextureMaterialProperty.js | 113 ++ src/Obj/Materail/index.js | 2 + static/img/roadPhoto.png | Bin 0 -> 40435 bytes static/img/roadTexture.png | Bin 0 -> 1927 bytes static/img/sidePhoto.png | Bin 0 -> 47987 bytes 17 files changed, 7477 insertions(+), 7 deletions(-) create mode 100644 src/Obj/Base/BatchModel/_element_拓展.js create mode 100644 src/Obj/Base/RoadObject/_element.js create mode 100644 src/Obj/Base/RoadObject/eventBinding.js create mode 100644 src/Obj/Base/RoadObject/index-8.4.js create mode 100644 src/Obj/Base/RoadObject/index-last.js create mode 100644 src/Obj/Base/RoadObject/index-拐角连接.js create mode 100644 src/Obj/Base/RoadObject/index-直接连接.js create mode 100644 src/Obj/Base/RoadObject/index-面大小一致.js create mode 100644 src/Obj/Base/RoadObject/index.js create mode 100644 src/Obj/Materail/RoadTextureMaterialProperty.js create mode 100644 static/img/roadPhoto.png create mode 100644 static/img/roadTexture.png create mode 100644 static/img/sidePhoto.png diff --git a/src/In/index.js b/src/In/index.js index ebfff60..40f7487 100644 --- a/src/In/index.js +++ b/src/In/index.js @@ -186,7 +186,7 @@ import Frustum from '../Obj/AirLine/frustum' import DrawTakeOff from '../Obj/AirLine/DrawTakeOff' import FlowLine from '../Obj/Base/FlowLine' import Sunshine from '../Global/efflect/Sunshine' -// import Road2 from '../Obj/Base/RoadObject' +import Road2 from '../Obj/Base/RoadObject' import TextBox from '../Obj/Base/TextBox' import BatchModel from '../Obj/Base/BatchModel' @@ -261,7 +261,7 @@ if (!window.YJ) { // GenerateRoute Dialog, FlowLine, - // Road2, + Road2, TextBox, BatchModel }, diff --git a/src/Obj/Base/BatchModel/_element_拓展.js b/src/Obj/Base/BatchModel/_element_拓展.js new file mode 100644 index 0000000..e64eaa5 --- /dev/null +++ b/src/Obj/Base/BatchModel/_element_拓展.js @@ -0,0 +1,229 @@ +import { attributeElm } from '../../Element/elm_html' + +function html(that) { + return ` + +
+
+
+ 颜色 +
+
+
+
+
+
+ +
+
+
+
+
添加方式 +
+
+
+
+
+
+
+ 朝向偏移 +
+ + ° + +
+
+
+
+
+

模型间隔

+
+
+
+ + 自定义距离 +
+
+
+
+ + 固定距离 +
+
+
+ 模型间隔 +
+ + + +
+
+
+

线型选择

+
+
+
+ + 折线 +
+
+
+
+ + 曲线 +
+
+
+ 线条数量 +
+ + + +
+
+
+ 线条间隔 +
+ + + +
+
+
+
+
+
+ + 随机采样 +
+
+
+ 随机数量 +
+ + + +
+
+
+
+
+
+
+ + 网格采样 +
+
+
+ 首边间隔 +
+ + + +
+
+
+ 次边间隔 +
+ + + +
+
+
+
+ +
+ + +
+
+ 高度模式 +
+
+
+
+
+
+
缩放 +
+ + 是否等比例缩放 +
+
+
+
+
+
+ +
+
+
+ 新增模型风格设置 + +
+
+ +
+
+ +
+
+ 显隐 + +
+
+ 图标 +
+ +
+
+
+ 默认图标 +
+ +
+
+
+ 图标倍数 +
+ + + +
+
+
+
+
+

文字设置

+
+
+ 显隐 + +
+
+ 字体选择 +
+
+
+ 文字大小 +
+ + px + +
+
+
+ 文字颜色 +
+
+
+
+
+
+
+ + ` +} + +export { html } diff --git a/src/Obj/Base/Road/index.js b/src/Obj/Base/Road/index.js index bb43225..2e946f1 100644 --- a/src/Obj/Base/Road/index.js +++ b/src/Obj/Base/Road/index.js @@ -5,7 +5,7 @@ class Corridor extends Base { // /** // * @constructor // * @description 道路 - // * @param sdk + // * @param sdk // * @param options {object} 属性 // * @param options.name{string} 名称 // * @param options.image{string | HTMLImageElement | HTMLCanvasElement | HTMLVideoElement} 指定 Image、URL、Canvas 或 Video 的属性 @@ -67,7 +67,7 @@ class Corridor extends Base { // material: new Cesium.ImageMaterialProperty({ // image: this.options.image, // repeat: new Cesium.Cartesian2(100, 1.0), - // color: Cesium.Color.TOMATO + // color: Cesium.Color.TOMATO // }) // } // }); @@ -217,10 +217,10 @@ class Corridor extends Base { let aa = 0 const shader = ` - uniform sampler2D image; + uniform sampler2D image; uniform vec4 color; uniform vec2 repeat; - + czm_material czm_getMaterial(czm_materialInput materialInput){ czm_material material=czm_getDefaultMaterial(materialInput); mat2 rotationMatrix = mat2(cos(radians(-rotate)), sin(radians(-rotate)), -sin(radians(-rotate)), cos(radians(-rotate))); diff --git a/src/Obj/Base/RoadObject/_element.js b/src/Obj/Base/RoadObject/_element.js new file mode 100644 index 0000000..49b49dc --- /dev/null +++ b/src/Obj/Base/RoadObject/_element.js @@ -0,0 +1,39 @@ +function html() { + return ` + +
+
+
+ 名称 + +
+
+ 道路类型 +
+
+
+
+ +
+
+
+ 车道宽度 +
+ + +
+
+
+ 人行道宽度 +
+ + +
+
+
+
+ + ` +} + +export { html } diff --git a/src/Obj/Base/RoadObject/eventBinding.js b/src/Obj/Base/RoadObject/eventBinding.js new file mode 100644 index 0000000..ccbf026 --- /dev/null +++ b/src/Obj/Base/RoadObject/eventBinding.js @@ -0,0 +1,92 @@ +class eventBinding { + constructor() { + this.element = {} + } + static event = {} + + getEvent(name) { + return eventBinding.event[name] + } + + getEventAll() { + return eventBinding.event + } + + setEvent(name, event) { + eventBinding.event[name] = event + } + + on(that, elements) { + for (let i = 0; i < elements.length; i++) { + let Event = [] + let isEvent = false + let removeName = [] + if (!elements[i] || !elements[i].attributes) { + continue; + } + for (let m of elements[i].attributes) { + switch (m.name) { + case '@model': { + isEvent = true + if (elements[i].type == 'checkbox') { + Event.push((e) => { that[m.value] = e.target.checked }) + elements[i].checked = that[m.value] + } + else { + Event.push((e) => { + let value = e.target.value + if (e.target.type == 'number') { + value = Number(value) + } + that[m.value] = value + }) + if (elements[i].nodeName == 'IMG') { + elements[i].src = that[m.value] + } + else { + elements[i].value = that[m.value] + } + } + if (this.element[m.value]) { + this.element[m.value].push(elements[i]) + } + else { + this.element[m.value] = [elements[i]] + } + removeName.push(m.name) + break; + } + case '@click': { + elements[i].addEventListener('click', (e) => { + if (typeof (that.Dialog[m.value]) === 'function') { + that.Dialog[m.value](e) + } + }); + removeName.push(m.name) + // elements[i].attributes.removeNamedItem(m.name) + break; + } + } + // elements[i].attributes[m] = undefined + } + for (let n = 0; n < removeName.length; n++) { + elements[i].attributes.removeNamedItem(removeName[n]) + } + + if (isEvent) { + let ventType = 'input' + if (elements[i].tagName != 'INPUT' || elements[i].type == 'checkbox') { + ventType = 'change' + } + elements[i].addEventListener(ventType, (e) => { + for (let t = 0; t < Event.length; t++) { + Event[t](e) + } + }); + } + } + } +} + +const EventBinding = new eventBinding(); +export default EventBinding; \ No newline at end of file diff --git a/src/Obj/Base/RoadObject/index-8.4.js b/src/Obj/Base/RoadObject/index-8.4.js new file mode 100644 index 0000000..48f1d50 --- /dev/null +++ b/src/Obj/Base/RoadObject/index-8.4.js @@ -0,0 +1,1549 @@ +/** + * @description 道路 + */ +import Dialog from '../../Element/Dialog'; +import { html } from "./_element"; +import EventBinding from '../../Element/Dialog/eventBinding'; +import Base from "../index"; +import { syncData } from '../../../Global/MultiViewportMode' +import { setSplitDirection, syncSplitData, setActiveId } from '../../../Global/SplitScreen' +import { setActiveViewer, closeRotateAround, closeViewFollow } from '../../../Global/global' + +class Road extends Base { + /** + * @constructor + * @param sdk + * @description 道路 + * @param options {object} 道路属性 + * @param options.name=未命名对象 {string} 名称 + * @param options.carRoadWidth=2 {number} 车道宽度 + * @param options.sideWidth=2 {number} 人行道宽度 + * @param options.positions=[] {array} 道路positions + * @param options.roadImage='' {string} 车道贴图 + * @param options.sideImage='' {string} 人行道贴图 + * @param Dialog {object} 弹框对象 + * @param Dialog.confirmCallBack {function} 弹框确认时的回调 + * */ + constructor(sdk, options = {}, _Dialog = {}) { + super(sdk, options); + this.viewer = this.sdk.viewer + this.options.name = options.name || '道路' + this.options.carRoadWidth = options.carRoadWidth || 10 + this.options.sideWidth = options.sideWidth || 5 + this.options.positions = options.positions || [] + this.options.roadImage = options.roadImage || (this.getSourceRootPath() + '/img/roadPhoto.png') + this.options.sideImage = options.sideImage || (this.getSourceRootPath() + '/img/sidePhoto.png') + this.options.show = (options.show || options.show === false) ? options.show : true + this.Dialog = _Dialog + this._EventBinding = new EventBinding() + this._elms = {}; + this.positionArea = [] + this.positions = [] + this.lineEntity = '' + this.crossArr = [] + this.pointArr = [] + this.positionChangeIndex = [] + + this.sdk.addIncetance(this.options.id, this) + Road.create(this) + } + // 创建道路 + static create(that) { + let positions = [] + that.options.positions.forEach(v => { + positions.push(new Cesium.Cartesian3.fromDegrees(v.lng, v.lat, v.alt)) + }) + let newPosi = [] + for (let i = 0; i < positions.length - 1; i++) { + const start = positions[i]; + const end = positions[i + 1]; + newPosi.push([start, end]) + that.pointArr = newPosi + } + + let area = [[], [], []] + + // area[1] = that.getRectangle(positions, that.options.carRoadWidth) + area[1][0] = that.getRectangle(newPosi, that.options.carRoadWidth) + let sideArr = that.getSideRectangle(area[1][0], that.options.sideWidth) + area[0] = sideArr.left + area[2] = sideArr.right + + // area[1] = that.createLineBufferPolygon2(positions, that.options.carRoadWidth / 2) + // area[1] = newPositions + // area[0] = that.createLineBufferPolygonSide(area[1][2], -that.options.sideWidth) + // area[2] = that.createLineBufferPolygonSide(area[1][1], that.options.sideWidth) + + //判断道路边是否相交 + for (let i = 0; i < area[0].length - 1; i++) { + + let leftItem = area[0][i] + let leftItem2 = area[0][i + 1] + let rightItem = area[2][i] + let rightItem2 = area[2][i + 1] + let carItem = area[1][0][i] + let carItem2 = area[1][0][i + 1] + let leftLine = that.getIntersects(leftItem[2], leftItem[3], leftItem2[2], leftItem2[3]) + // that.sdk.viewer.entities.add({ + // polyline: { + // positions: [leftItem[2], leftItem[3]], + // width: 2.0, + // clampToGround: true, + // material: new Cesium.PolylineGlowMaterialProperty({ + // color: Cesium.Color.YELLOW, + // }), + // }, + // }); + // that.sdk.viewer.entities.add({ + // polyline: { + // positions: [leftItem2[2], leftItem2[3]], + // width: 2.0, + // clampToGround: true, + // material: new Cesium.PolylineGlowMaterialProperty({ + // color: Cesium.Color.YELLOW, + // }), + // }, + // }); + + let rightLine = that.getIntersects(rightItem[0], rightItem[1], rightItem2[0], rightItem2[1]) + // if (!leftLine && !rightLine) { + // for (let index = 0; index < 4; index++) { + // let positions = [] + // index === 0 ? positions.push(leftItem[2], leftItem[3]) : index === 1 ? positions.push(leftItem2[2], leftItem2[3]) : index === 2 ? positions.push(rightItem[0], rightItem[1]) : positions.push(rightItem2[0], rightItem2[1]) + // that.sdk.viewer.entities.add({ + // polyline: { + // positions: positions, + // width: 2.0, + // clampToGround: true, + // material: new Cesium.PolylineGlowMaterialProperty({ + // color: index === 0 ? Cesium.Color.RED : index === 1 ? Cesium.Color.BLUE : index === 2 ? Cesium.Color.YELLOW : Cesium.Color.GREEN, + // }), + // }, + // }); + + // } + // } + + + + for (let index = 0; index < 4; index++) { + // that.sdk.viewer.entities.add({ + // name: 'node-secondary-edit-point', + // index: index, + // position: area[0][i][index], + // billboard: { + // image: that.getSourceRootPath() + '/img/point.png', + // width: 15, + // height: 15, + // disableDepthTestDistance: Number.POSITIVE_INFINITY, + // color: Cesium.Color.WHITE.withAlpha(0.99) + // }, + // label: { + // text: '' + index + ':', + // pixelOffset: { x: 0, y: -20 }, + // }, + // }) + + // that.sdk.viewer.entities.add({ + // name: 'node-secondary-edit-point', + // index: index, + // position: area[2][i][index], + // billboard: { + // image: that.getSourceRootPath() + '/img/point.png', + // width: 15, + // height: 15, + // disableDepthTestDistance: Number.POSITIVE_INFINITY, + // color: Cesium.Color.WHITE.withAlpha(0.99) + // }, + // label: { + // text: '' + index + '/', + // pixelOffset: { x: 0, y: -20 }, + // }, + // }) + } + + + if (leftLine) {//左侧相交 + // that.sdk.viewer.entities.add({ + // name: 'node-secondary-edit-point', + // index: 1, + // position: leftLine, + // billboard: { + // image: that.getSourceRootPath() + '/img/locate2.png', + // width: 15, + // height: 15, + // disableDepthTestDistance: Number.POSITIVE_INFINITY, + // color: Cesium.Color.WHITE.withAlpha(0.99) + // }, + // label: { + // text: '', + // pixelOffset: { x: 0, y: -20 }, + // }, + // }) + + //获取右侧延长交点 + let point1 = that.getExtendPoint(rightItem[0], rightItem[1], 1000) + let point2 = that.getExtendPoint(rightItem2[1], rightItem2[0], 1000) + + that.sdk.viewer.entities.add({ + polyline: { + positions: [rightItem[0], point1], + width: 2.0, + clampToGround: true, + material: new Cesium.PolylineGlowMaterialProperty({ + color: Cesium.Color.AQUA, + }), + }, + }); + that.sdk.viewer.entities.add({ + polyline: { + positions: [rightItem2[1], point2], + width: 2.0, + clampToGround: true, + material: new Cesium.PolylineGlowMaterialProperty({ + color: Cesium.Color.AQUA, + }), + }, + }); + + that.sdk.viewer.entities.add({ + name: 'node-secondary-edit-point', + index: i, + position: point1, + billboard: { + image: that.getSourceRootPath() + '/img/point.png', + width: 15, + height: 15, + disableDepthTestDistance: Number.POSITIVE_INFINITY, + color: Cesium.Color.WHITE.withAlpha(0.99) + }, + label: { + text: '' + 7 + "", + pixelOffset: { x: 0, y: -20 }, + }, + }) + that.sdk.viewer.entities.add({ + name: 'node-secondary-edit-point', + index: i, + position: point2, + billboard: { + image: that.getSourceRootPath() + '/img/end.png', + width: 15, + height: 15, + disableDepthTestDistance: Number.POSITIVE_INFINITY, + color: Cesium.Color.WHITE.withAlpha(0.99) + }, + label: { + text: '' + 7 + "", + pixelOffset: { x: 0, y: -20 }, + }, + }) + + + let rightIntersection = that.getIntersects(rightItem[0], point1, rightItem2[1], point2) + if (!rightIntersection) { + return + } + + //将其他几条边都延长 + let leftLineNeiPoint = that.getExtendPoint(leftItem[0], leftItem[1], 1000) + let carLeftPoint = that.getExtendPoint(carItem[3], carItem[2], 1000) + let carRightPoint = that.getExtendPoint(carItem[0], carItem[1], 1000) + + let rightLineNeiPoint = that.getExtendPoint(rightItem[3], rightItem[2], 1000) + + + + //跟左侧里相交点 + let leftLineNei = that.getIntersects(leftLine, rightIntersection, leftItem[0], leftLineNeiPoint) + //跟车道左侧相交点 + let carLeft = that.getIntersects(leftLine, rightIntersection, carItem[3], carLeftPoint) + + //跟车道右侧相交点 + let carRight = that.getIntersects(leftLine, rightIntersection, carItem[0], carRightPoint) + let rightLineNei = that.getIntersects(leftLine, rightIntersection, rightItem[3], rightLineNeiPoint) + // let leftLineNei = that.getIntersects(leftLine, rightItem[2], leftItem[0], leftItem[3]) + // let carLeft = that.getIntersects(leftLine, rightItem[2], carItem[3], carItem[2]) + // let carRight = that.getIntersects(leftLine, rightItem[2], carItem[0], carItem[1]) + // let rightLineNei = that.getIntersects(leftLine, rightItem[2], rightItem[0], rightItem[3]) + + // let leftLineNei = that.getIntersects(leftLine, intersection, leftItem[0], leftItem[3]) + // //跟车道左侧相交点 + // let carLeft = that.getIntersects(leftLine, intersection, carItem[3], carItem[2]) + // //跟车道右侧相交点 + // let carRight = that.getIntersects(leftLine, intersection, carItem[0], carItem[1]) + // let rightLineNei = that.getIntersects(leftLine, intersection, rightItem[0], rightItem[3]) + + leftItem[2] = leftLine + leftItem[1] = leftLineNei + // carItem[2] = carLeft + carItem[2] = leftLineNei + // carItem[1] = carRight + carItem[1] = rightLineNei + rightItem[2] = rightLineNei + rightItem[1] = rightIntersection + + + // that.sdk.viewer.entities.add({ + // name: 'node-secondary-edit-point', + // index: 1, + // position: leftLineNei, + // billboard: { + // image: that.getSourceRootPath() + '/img/locate2.png', + // width: 15, + // height: 15, + // disableDepthTestDistance: Number.POSITIVE_INFINITY, + // color: Cesium.Color.WHITE.withAlpha(0.99) + // }, + // label: { + // text: '', + // pixelOffset: { x: 0, y: -20 }, + // }, + // }) + that.sdk.viewer.entities.add({ + name: 'node-secondary-edit-point', + index: 1, + position: rightIntersection, + billboard: { + image: that.getSourceRootPath() + '/img/locate2.png', + width: 15, + height: 15, + disableDepthTestDistance: Number.POSITIVE_INFINITY, + color: Cesium.Color.WHITE.withAlpha(0.99) + }, + label: { + text: '', + pixelOffset: { x: 0, y: -20 }, + }, + }) + + + + //将其他几条边都延长 + let leftLineNeiPoint2 = that.getExtendPoint(leftItem2[1], leftItem2[0], 1000) + let carLeftPoint2 = that.getExtendPoint(carItem2[2], carItem2[3], 1000) + let carRightPoint2 = that.getExtendPoint(carItem2[1], carItem2[0], 1000) + let rightLineNeiPoint2 = that.getExtendPoint(rightItem2[2], rightItem2[3], 1000) + + // let leftLineNei2 = that.getIntersects(leftLine, rightItem2[1], leftItem2[0], leftItem2[3]) + // let carLeft2 = that.getIntersects(leftLine, rightItem2[1], carItem2[3], carItem2[2]) + // let carRight2 = that.getIntersects(leftLine, rightItem2[1], carItem2[0], carItem2[1]) + // let rightLineNei2 = that.getIntersects(leftLine, rightItem2[1], rightItem2[0], rightItem2[3]) + + + + let leftLineNei2 = that.getIntersects(leftLine, rightIntersection, leftItem2[1], leftLineNeiPoint2) + let carLeft2 = that.getIntersects(leftLine, rightIntersection, carItem2[2], carLeftPoint2) + let carRight2 = that.getIntersects(leftLine, rightIntersection, carItem2[1], carRightPoint2) + let rightLineNei2 = that.getIntersects(leftLine, rightIntersection, rightItem2[2], rightLineNeiPoint2) + + // let arr = [leftLine, rightIntersection, leftItem2[3], leftLineNeiPoint2] + // arr.forEach((item, index) => { + // that.sdk.viewer.entities.add({ + // name: 'node-secondary-edit-point', + // index: i, + // position: item, + // billboard: { + // image: that.getSourceRootPath() + '/img/point.png', + // width: 15, + // height: 15, + // disableDepthTestDistance: Number.POSITIVE_INFINITY, + // color: Cesium.Color.WHITE.withAlpha(0.99) + // }, + // label: { + // text: '' + index, + // pixelOffset: { x: 0, y: -20 }, + // }, + // }) + // }) + + // let leftLineNei2 = that.getIntersects(leftLine, intersection, leftItem2[0], leftItem2[3]) + // //跟车道左侧相交点 + // let carLeft2 = that.getIntersects(leftLine, intersection, carItem2[3], carItem2[2]) + // //跟车道右侧相交点 + // let carRight2 = that.getIntersects(leftLine, intersection, carItem2[0], carItem2[1]) + // let rightLineNei2 = that.getIntersects(leftLine, intersection, rightItem2[0], rightItem2[3]) + + leftItem2[3] = leftLine + leftItem2[0] = leftLineNei + // carItem2[3] = carLeft + carItem2[3] = leftLineNei + // carItem2[0] = carRight + carItem2[0] = rightLineNei + rightItem2[3] = rightLineNei + rightItem2[0] = rightIntersection + + + } else if (rightLine) {//右侧相交 + + //获取左侧延长交点 + let point1 = that.getExtendPoint(leftItem[3], leftItem[2], 1000) + let point2 = that.getExtendPoint(leftItem2[2], leftItem2[3], 1000) + + that.sdk.viewer.entities.add({ + polyline: { + positions: [leftItem[3], point1], + width: 2.0, + clampToGround: true, + material: new Cesium.PolylineGlowMaterialProperty({ + color: Cesium.Color.AQUA, + }), + }, + }); + that.sdk.viewer.entities.add({ + polyline: { + positions: [leftItem2[2], point2], + width: 2.0, + clampToGround: true, + material: new Cesium.PolylineGlowMaterialProperty({ + color: Cesium.Color.AQUA, + }), + }, + }); + + let rightIntersection = that.getIntersects(leftItem[3], point1, leftItem2[2], point2) + if (!rightIntersection) { + return + } + //将其他几条边都延长 + let leftLineNeiPoint = that.getExtendPoint(leftItem[0], leftItem[1], 1000) + + let carLeftPoint = that.getExtendPoint(carItem[3], carItem[2], 1000) + let carRightPoint = that.getExtendPoint(carItem[0], carItem[1], 1000) + let rightLineNeiPoint = that.getExtendPoint(rightItem[3], rightItem[2], 1000) + + // //跟左侧里相交点 + let leftLineNei = that.getIntersects(rightLine, rightIntersection, leftItem[0], leftLineNeiPoint) + //跟车道左侧相交点 + let carLeft = that.getIntersects(rightLine, rightIntersection, carItem[3], carLeftPoint) + //跟车道右侧相交点 + let carRight = that.getIntersects(rightLine, rightIntersection, carItem[0], carRightPoint) + let rightLineNei = that.getIntersects(rightLine, rightIntersection, rightItem[3], rightLineNeiPoint) + // //跟左侧里相交点 + // let leftLineNei = that.getIntersects(rightLine, leftItem[2], leftItem[0], leftItem[3]) + // //跟车道左侧相交点 + // let carLeft = that.getIntersects(rightLine, leftItem[2], carItem[3], carItem[2]) + // //跟车道右侧相交点 + // let carRight = that.getIntersects(rightLine, leftItem[2], carItem[0], carItem[1]) + // let rightLineNei = that.getIntersects(rightLine, leftItem[2], rightItem[0], rightItem[3]) + + + leftItem[2] = rightIntersection + leftItem[1] = leftLineNei + // carItem[2] = carLeft + carItem[2] = leftLineNei + // carItem[1] = carRight + carItem[1] = rightLineNei + rightItem[2] = rightLineNei + rightItem[1] = rightLine + + //将其他几条边都延长 + let leftLineNeiPoint2 = that.getExtendPoint(leftItem2[2], leftItem2[3], 1000) + let carLeftPoint2 = that.getExtendPoint(carItem2[2], carItem2[3], 1000) + let carRightPoint2 = that.getExtendPoint(carItem2[1], carItem2[0], 1000) + let rightLineNeiPoint2 = that.getExtendPoint(rightItem2[2], rightItem2[3], 1000) + + let leftLineNei2 = that.getIntersects(rightLine, rightIntersection, leftItem2[1], leftLineNeiPoint2) + //跟车道左侧相交点 + let carLeft2 = that.getIntersects(rightLine, rightIntersection, carItem2[2], carLeftPoint2) + //跟车道右侧相交点 + let carRight2 = that.getIntersects(rightLine, rightIntersection, carItem2[1], carRightPoint2) + let rightLineNei2 = that.getIntersects(rightLine, rightIntersection, rightItem2[2], rightLineNeiPoint2) + // let leftLineNei2 = that.getIntersects(rightLine, leftItem2[1], leftItem2[0], leftItem2[3]) + // //跟车道左侧相交点 + // let carLeft2 = that.getIntersects(rightLine, leftItem2[1], carItem2[3], carItem2[2]) + // //跟车道右侧相交点 + // let carRight2 = that.getIntersects(rightLine, leftItem2[1], carItem2[0], carItem2[1]) + // let rightLineNei2 = that.getIntersects(rightLine, leftItem2[1], rightItem2[0], rightItem2[3]) + + leftItem2[3] = rightIntersection + leftItem2[0] = leftLineNei + // carItem2[3] = carLeft + carItem2[3] = leftLineNei + // carItem2[0] = carRight + carItem2[0] = rightLineNei + rightItem2[3] = rightLineNei + rightItem2[0] = rightLine + + // leftItem[2] = rightIntersection + // leftItem[1] = leftLineNei + // // carItem[2] = carLeft + // carItem[2] = leftLineNei + // // carItem[1] = carRight + // carItem[1] = rightLineNei + // rightItem[2] = rightLineNei + // rightItem[1] = rightLine + } + + // area[0][i] = leftItem + // area[0][i + 1] = leftItem2 + // area[2][i] = rightItem + // area[2][i + 1] = rightItem2 + // area[1][0][i] = carItem + // area[1][0][i + 1] = carItem2 + } + if (that.viewer.entities.getById(that.options.id)) { + that.viewer.entities.getById(that.options.id)._children.forEach((item) => { + that.viewer.entities.remove(item); + }); + that.viewer.entities.remove(that.viewer.entities.getById(that.options.id)) + } + that.lineEntity = that.viewer.entities.add(new Cesium.Entity({ id: that.options.id, show: that.options.show })) + + const myImg = new Image() + myImg.src = that.options.roadImage + myImg.onload = function () { + area[1][0].forEach((item, index) => { + that.viewer.entities.add({ + // id: that.options.id, + parent: that.lineEntity, + polygon: { + hierarchy: new Cesium.PolygonHierarchy(item), + heightReference: Cesium.HeightReference.CLAMP_TO_GROUND, + classificationType: Cesium.ClassificationType.BOTH, + distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0, 10000), + material: new Cesium.ImageMaterialProperty({ + image: that.options.roadImage, + transparent: true,// 如果图片有透明部分,需要设置为 true + repeat: that.calculateTextureRepeat(item, myImg) + }), + // stRotation: that.calculateRoadAngle(positions[index], positions[index + 1]) + stRotation: that.calculateRoadAngle(item[0], item[1]) + // material: Cesium.Color.fromRandom({ alpha: 0.5 }), + } + }); + }) + } + + const myImg2 = new Image() + myImg2.src = that.options.sideImage + myImg2.onload = function () { + area[0].forEach((item, index) => { + that.viewer.entities.add({ + parent: that.lineEntity, + polygon: { + hierarchy: new Cesium.PolygonHierarchy(item), + material: new Cesium.ImageMaterialProperty({ + image: that.options.sideImage, + transparent: true,// 如果图片有透明部分,需要设置为 true + repeat: that.calculateTextureRepeat(item, myImg2) + }), + stRotation: that.calculateRoadAngle(positions[index], positions[index + 1]) + // material: Cesium.Color.fromRandom({ alpha: 0.5 }), + } + }); + }) + + area[2].forEach((item, index) => { + that.viewer.entities.add({ + parent: that.lineEntity, + polygon: { + hierarchy: new Cesium.PolygonHierarchy(item), + material: new Cesium.ImageMaterialProperty({ + image: that.options.sideImage, + transparent: true,// 如果图片有透明部分,需要设置为 true + repeat: that.calculateTextureRepeat(item, myImg2) + }), + stRotation: that.calculateRoadAngle(positions[index], positions[index + 1]) + // material: Cesium.Color.fromRandom({ alpha: 0.5 }), + } + }); + }) + } + + } + getSideRectangle(positions, width) { + let right = [] + let left = [] + let that = this + positions.forEach((item, i) => { + // console.log(that.positionChangeIndex, 'positionChangeIndex') + // if (that.positionChangeIndex[i + '']) { + // console.log('1111') + // right.push([item[2], item[3]]) + // left.push([item[0], item[1]]) + // } else { + right.push([item[0], item[1]]) + // left.push([item[2], item[3]]) + left.push([item[3], item[2]]) + // } + + + for (let index = 0; index < 2; index++) { + let ArrArr = [] + index === 0 ? ArrArr.push(item[0], item[1]) : ArrArr.push(item[2], item[3]); + // that.sdk.viewer.entities.add({ + // polyline: { + // positions: ArrArr, + // width: 2.0, + // clampToGround: true, + // material: new Cesium.PolylineGlowMaterialProperty({ + // color: index === 0 ? Cesium.Color.RED : Cesium.Color.BLUE, + // }), + // }, + // }); + } + }) + let rightPosi = that.getRectangle(right, width, 'side') + let newRightPosi = [] + right.forEach((item, index) => { + newRightPosi.push([rightPosi[index][0], rightPosi[index][1], item[1], item[0]]) + }) + + + let leftPosi = this.getRectangle(left, width, 'side') + let newLeftPosi = [] + left.forEach((item, index) => { + // newLeftPosi.push([item[0], item[1], leftPosi[index][2]], leftPosi[index][3]) + newLeftPosi.push([item[0], item[1], leftPosi[index][2], leftPosi[index][3]]) + }) + return { left: newLeftPosi, right: newRightPosi } + } + getRectangle(positions, width, type) { + let areaArr = [] + let newPositions = [] + let that = this + // for (let i = 0; i < positions.length - 1; i++) { + for (let i = 0; i < positions.length; i++) { + const start = positions[i][0]; + const end = positions[i][1]; + + + areaArr[i] = [] + let posi = [] + let Outlinegeometry = new Cesium.CorridorGeometry({ + positions: [start, end], + width: width, + cornerType: Cesium.CornerType.MITERED, + vertexFormat: Cesium.MaterialAppearance.MaterialSupport.ALL.vertexFormat + }) + let geometry = Cesium.CorridorGeometry.createGeometry(Outlinegeometry) + for (let j = 0; j < geometry.attributes.position.values.length; j += 3) { + let val = that.cartesian3Towgs84(new Cesium.Cartesian3(geometry.attributes.position.values[j], geometry.attributes.position.values[j + 1], geometry.attributes.position.values[j + 2]), that.sdk.viewer) + posi.push([val.lng, val.lat]) + } + + for (let x = 0; x < geometry.indices.length; x += 3) { + areaArr[i].push(turf.polygon([[posi[geometry.indices[x]], posi[geometry.indices[x + 1]], posi[geometry.indices[x + 2]], posi[geometry.indices[x]]]])) + } + + let geojson = turf.union(areaArr[i][0], areaArr[i][1]); + let arr = [] + geojson.geometry.coordinates[0].pop() + geojson.geometry.coordinates[0].forEach(item => { + arr.push(new Cesium.Cartesian3.fromDegrees(item[0], item[1])) + }) + let dotResult, angle + const tempVec = new Cesium.Cartesian3(); + + // 计算并归一化第一个向量 + const vector1 = Cesium.Cartesian3.normalize( + Cesium.Cartesian3.subtract(that.pointArr[i][1], that.pointArr[i][0], tempVec), + tempVec + ); + + // 计算并归一化第二个向量 + const vector2 = Cesium.Cartesian3.normalize( + Cesium.Cartesian3.subtract(arr[1], arr[0], new Cesium.Cartesian3()), + new Cesium.Cartesian3() + ); + + dotResult = Cesium.Cartesian3.dot(vector1, vector2); + if ((0.999 < dotResult && dotResult < 1.001) || (-0.999 > dotResult && dotResult > -1.001)) {//调整方向 + // let newArr = [] + // newArr[0] = arr[2] + // newArr[1] = arr[3] + // newArr[2] = arr[0] + // newArr[3] = arr[1] + // newPositions.push(newArr) + //判断方向是否正确 + const vec1 = Cesium.Cartesian3.normalize( + Cesium.Cartesian3.subtract(start, arr[0], new Cesium.Cartesian3()), + new Cesium.Cartesian3() + ); + + // 计算并归一化第二个向量 + const vec2 = Cesium.Cartesian3.normalize( + Cesium.Cartesian3.subtract(arr[1], arr[0], new Cesium.Cartesian3()), + new Cesium.Cartesian3() + ); + + let dRst = Cesium.Cartesian3.dot(vec1, vec2); + + + if (0 < dRst && dRst < 0.0001) { + newPositions.push(arr) + } else { + + let dis1 = that.distancePointToLine(start, arr[0], arr[1]) + let dis2 = that.distancePointToLine(start, arr[2], arr[3]) + let newArr = [] + if (dis1 > dis2) {// + newArr[0] = arr[3] + newArr[1] = arr[0] + newArr[2] = arr[1] + newArr[3] = arr[2] + } else { + newArr[0] = arr[2] + newArr[1] = arr[3] + newArr[2] = arr[0] + newArr[3] = arr[1] + } + newPositions.push(newArr) + + + // let newArr = [] + // newArr[0] = arr[2] + // newArr[1] = arr[3] + // newArr[2] = arr[0] + // newArr[3] = arr[1] + // newPositions.push(newArr) + } + + // newPositions.push(arr) + + if (!type) { + for (let index = 0; index < 4; index++) { + that.sdk.viewer.entities.add({ + name: 'node-secondary-edit-point', + index: index, + position: arr[index], + billboard: { + image: this.getSourceRootPath() + '/img/point.png', + width: 15, + height: 15, + disableDepthTestDistance: Number.POSITIVE_INFINITY, + color: Cesium.Color.WHITE.withAlpha(0.99) + }, + label: { + text: '' + index + '’', + pixelOffset: { x: 0, y: -20 }, + }, + }) + + } + + } + } else { + let dis1 = that.distancePointToLine(that.pointArr[i][0], arr[0], arr[1]) + let dis2 = that.distancePointToLine(that.pointArr[i][0], arr[2], arr[3]) + let newArr = [] + if (dis1 > dis2) { + newArr[0] = arr[3] + newArr[1] = arr[0] + newArr[2] = arr[1] + newArr[3] = arr[2] + } else { + newArr[0] = arr[1] + newArr[1] = arr[2] + newArr[2] = arr[3] + newArr[3] = arr[0] + } + newPositions.push(newArr) + + if (!type) { + for (let index = 0; index < 4; index++) { + that.sdk.viewer.entities.add({ + name: 'node-secondary-edit-point', + index: index, + // position: newArr[index], + position: newArr[index], + billboard: { + image: this.getSourceRootPath() + '/img/point.png', + width: 15, + height: 15, + disableDepthTestDistance: Number.POSITIVE_INFINITY, + color: Cesium.Color.WHITE.withAlpha(0.99) + }, + label: { + text: '' + index, + pixelOffset: { x: 0, y: -20 }, + }, + }) + } + } + // !type && that.positionChangeIndex.push(i + "") + } + // } + + // if (!type) { + // // if (cross > 0 && !type) {//调整方向 + // if ((0.999 < dotResult && dotResult < 1.001) || (-0.999 > dotResult && dotResult > -1.001)) {//调整方向 + // newPositions.push(arr) + // } else { + // let newArr = [] + // newArr[0] = arr[1] + // newArr[1] = arr[2] + // newArr[2] = arr[3] + // newArr[3] = arr[0] + // newPositions.push(newArr) + // } + // } else { + // newPositions.push(arr) + // } + } + return newPositions + } + distancePointToLine(p, a, b) { + let ab = new Cesium.Cartesian3(); + Cesium.Cartesian3.subtract(b, a, ab); + + let ap = new Cesium.Cartesian3(); + Cesium.Cartesian3.subtract(p, a, ap); + + let abNormalized = new Cesium.Cartesian3(); + Cesium.Cartesian3.normalize(ab, abNormalized); + + let apProjectionMagnitude = Cesium.Cartesian3.dot(ap, abNormalized); + let apProjection = Cesium.Cartesian3.multiplyByScalar(abNormalized, apProjectionMagnitude, new Cesium.Cartesian3()); + + let footPoint = new Cesium.Cartesian3(); + Cesium.Cartesian3.add(a, apProjection, footPoint); + + return Cesium.Cartesian3.distance(p, footPoint); + } + getExtendPoint2(position1, position2, distance) { + // let position1 = Cesium.Cartesian3.fromDegrees(p1[0], p1[1], 0); + // let position2 = Cesium.Cartesian3.fromDegrees(p2[0], p2[1], 0); + let pot = Cesium.Cartesian3.subtract(position2, position1, new Cesium.Cartesian3());//方向 + var dir = Cesium.Cartesian3.normalize(pot, new Cesium.Cartesian3());//向量归一化 + + var ray = new Cesium.Ray(position1, dir); + let np = Cesium.Ray.getPoint(ray, distance * 10);//计算延长点 + return np + } + getExtendPoint3(pos1, pos2, distance) { + const geodesic = new Cesium.EllipsoidGeodesic(); + geodesic.setEndPoints(pos1, pos2); + + // 关键步骤:获取实际测地距离 + const actualDist = geodesic.surfaceDistance; + const ratio = Math.max(1.1, distance / actualDist); // 强制外延比例 + + // 基于地理坐标的弧度插值 + const carto1 = Cesium.Cartographic.fromCartesian(pos1); + const carto2 = Cesium.Cartographic.fromCartesian(pos2); + + const resultCarto = new Cesium.Cartographic( + carto1.longitude + (carto2.longitude - carto1.longitude) * ratio, + carto1.latitude + (carto2.latitude - carto1.latitude) * ratio, + 0 + ); + + return Cesium.Cartesian3.fromRadians( + resultCarto.longitude, + resultCarto.latitude + ); + } + getExtendPoint(position1, position2, distance) { + // 1. 向量计算与归一化 + const direction = Cesium.Cartesian3.subtract( + position2, + position1, + new Cesium.Cartesian3() + ); + Cesium.Cartesian3.normalize(direction, direction); + + // 2. 使用Ellipsoid修正地球曲率影响 + const ellipsoid = Cesium.Ellipsoid.WGS84; + const geodesic = new Cesium.EllipsoidGeodesic(); + geodesic.setEndPoints(position1, position2); + const actualDistance = geodesic.surfaceDistance; // 获取实际测地距离 + if (distance <= actualDistance) distance = actualDistance * 100; // 强制向外延伸 + + // 3. 考虑椭球面曲率的精确延长 + const surfaceNormal = ellipsoid.geodeticSurfaceNormal(position1); + const projection = Cesium.Cartesian3.multiplyByScalar( + surfaceNormal, + Cesium.Cartesian3.dot(direction, surfaceNormal), + new Cesium.Cartesian3() + ); + Cesium.Cartesian3.subtract(direction, projection, direction); + Cesium.Cartesian3.normalize(direction, direction); + + // 4. 最终坐标计算 + return Cesium.Cartesian3.add( + position2, + Cesium.Cartesian3.multiplyByScalar(direction, distance, new Cesium.Cartesian3()), + new Cesium.Cartesian3() + ); + } + getArr(arr1, arr2) { + arr2 = arr2.reverse() + let polygon = [] + for (let index = 0; index < arr1.length - 1; index++) { + polygon.push([arr1[index], arr1[index + 1], arr2[index + 1], arr2[index]]) + } + return polygon + } + calculateRoadAngle2(startPoint, endPoint) { + // 1. 获取地表法向量 + const normal = this.sdk.viewer.scene.globe.ellipsoid.geodeticSurfaceNormal( + startPoint, new Cesium.Cartesian3()); + + // 2. 构建带椭球参数的ENU矩阵 + const enuMatrix = Cesium.Transforms.eastNorthUpToFixedFrame( + startPoint, this.sdk.viewer.scene.globe.ellipsoid, normal); + const inverseMatrix = Cesium.Matrix4.inverse( + enuMatrix, new Cesium.Matrix4()); + + // 3. 转换坐标并计算相对向量 + const localEnd = Cesium.Matrix4.multiplyByPoint( + inverseMatrix, endPoint, new Cesium.Cartesian3()); + const heightFactor = Math.abs(localEnd.z) / 1000; // 高度差补偿 + + // 4. 使用四象限反正切计算角度 + const angle = Math.atan2(localEnd.y, localEnd.x); + const adjustedAngle = angle - (heightFactor * 0.01); // 高度补偿 + let result = Cesium.Math.toDegrees(adjustedAngle) + return result; + } + calculateRoadAngle3(startPoint, endPoint) { + // 1. 获取地表法向量 + const normal = Cesium.Ellipsoid.WGS84.geodeticSurfaceNormal(startPoint); + + // 2. 构建精确ENU坐标系 + const enuMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(startPoint, undefined, normal); + const inverseMatrix = Cesium.Matrix4.inverse(enuMatrix, new Cesium.Matrix4()); + + // 3. 转换终点并计算水平向量 + const localEnd = Cesium.Matrix4.multiplyByPoint(inverseMatrix, endPoint, new Cesium.Cartesian3()); + const horizontalVec = new Cesium.Cartesian2(localEnd.x, localEnd.y); + Cesium.Cartesian2.normalize(horizontalVec, horizontalVec); + + const north = new Cesium.Cartesian2(1, 0); + + const angle = Cesium.Cartesian2.angleBetween(north, horizontalVec); + const cross = Cesium.Cartesian2.cross(north, horizontalVec, new Cesium.Cartesian2()); + return cross < 0 ? angle : -angle; + } + calculateRoadAngle(startPoint, endPoint) { + // 1. 获取精确地形法向量(需配合地形服务使用) + const normal = Cesium.Ellipsoid.WGS84.geodeticSurfaceNormal(startPoint); + + // 2. 构建ENU坐标系(增加异常处理) + if (!Cesium.defined(startPoint) || !Cesium.defined(endPoint)) { + return 0; + } + + const enuMatrix = Cesium.Transforms.eastNorthUpToFixedFrame( + startPoint, undefined, normal); + const inverseMatrix = Cesium.Matrix4.inverse( + enuMatrix, new Cesium.Matrix4()); + + // 3. 三维向量转换计算 + const localEnd = Cesium.Matrix4.multiplyByPoint( + inverseMatrix, endPoint, new Cesium.Cartesian3()); + + // 4. 使用三维向量计算角度(保留高程信息) + const north3D = new Cesium.Cartesian3(1, 0, 0); + const direction3D = new Cesium.Cartesian3( + localEnd.x, localEnd.y, localEnd.z); + Cesium.Cartesian3.normalize(direction3D, direction3D); + + // 5. 计算带符号角度(考虑三维空间关系) + const angle = Cesium.Cartesian3.angleBetween( + north3D, direction3D); + const cross = Cesium.Cartesian3.cross( + north3D, direction3D, new Cesium.Cartesian3()); + + // 6. 返回带符号角度(Z轴分量为负则取反) + return cross.z < 0 ? angle : -angle; + } + calculatePolygonOrientation(positions) { + + // 假设 position 是 Cesium.Cartesian3 对象,表示地球上的某个点 + var position = positions[0] + // 获取东、北、上坐标系 + var eastNorthUp = Cesium.Transforms.eastNorthUpToFixedFrame(position); + // northAxis 是北方向向量 + var northAxis = eastNorthUp.getColumn(1, new Cesium.Cartesian3()); + Cesium.Cartesian3.normalize(northAxis, northAxis); + + const direction = Cesium.Cartesian3.subtract(positions[0], positions[1], new Cesium.Cartesian3()); + Cesium.Cartesian3.normalize(direction, direction); + + + const dot = Cesium.Cartesian3.dot(northAxis, direction); + const magA = Cesium.Cartesian3.magnitude(northAxis); + const magB = Cesium.Cartesian3.magnitude(direction); + return Math.acos(dot / (magA * magB)); + } + calculateTextureRepeat(polygonPositions, textureSize, meterPerPixel = 0.01) { + // 验证纹理尺寸 + if (!textureSize.width || !textureSize.height) { + throw new Error('Texture size must contain width and height in pixels'); + } + + let widthDis = Cesium.Cartesian3.distance(polygonPositions[0], polygonPositions[1]) + let heightDis = Cesium.Cartesian3.distance(polygonPositions[0], polygonPositions[3]) + + // 计算纹理实际尺寸(米) + // const textureWidthMeters = textureSize.width * meterPerPixel; + // const textureHeightMeters = textureSize.height * meterPerPixel; + const textureWidthMeters = textureSize.width; + const textureHeightMeters = textureSize.height; + console.log(textureWidthMeters, textureHeightMeters, widthDis, heightDis, '宽度') + + // 保持宽高比计算重复次数 + const repeatX = widthDis / textureWidthMeters; + const repeatY = heightDis / textureHeightMeters; + const aspectRatio = textureWidthMeters / textureHeightMeters; + console.log(aspectRatio, 'aspectRatio') + + // 选择主导轴并保持比例 + const dominantRepeat = Math.max(repeatX, repeatY); + let x = Math.max(1, Math.ceil(widthDis / (aspectRatio * heightDis))) + console.log(x, '10') + return new Cesium.Cartesian2( + x, + 1 + ); + } + calculateTextureRepeat2(polygonPositions, textureSize, meterPerPixel = 0.01) { + // 验证纹理尺寸 + if (!textureSize.width || !textureSize.height) { + throw new Error('Texture size must contain width and height in pixels'); + } + + // 创建多边形几何体 + const geometry = Cesium.PolygonGeometry.createGeometry( + new Cesium.PolygonGeometry({ + polygonHierarchy: new Cesium.PolygonHierarchy(polygonPositions), + vertexFormat: Cesium.VertexFormat.POSITION_ONLY + }) + ); + + // 计算多边形面积(平方米) + let area = 0; + const indices = geometry.indices; + const positions = geometry.attributes.position.values; + for (let i = 0; i < indices.length; i += 3) { + const i0 = indices[i] * 3; + const i1 = indices[i + 1] * 3; + const i2 = indices[i + 2] * 3; + + const p0 = new Cesium.Cartesian3(positions[i0], positions[i0 + 1], positions[i0 + 2]); + const p1 = new Cesium.Cartesian3(positions[i1], positions[i1 + 1], positions[i1 + 2]); + const p2 = new Cesium.Cartesian3(positions[i2], positions[i2 + 1], positions[i2 + 2]); + + const cross = Cesium.Cartesian3.cross( + Cesium.Cartesian3.subtract(p1, p0, new Cesium.Cartesian3()), + Cesium.Cartesian3.subtract(p2, p0, new Cesium.Cartesian3()), + new Cesium.Cartesian3() + ); + area += Cesium.Cartesian3.magnitude(cross) * 0.5; + } + + // 将像素尺寸转换为实际尺寸(平方米) + const textureWidthMeters = textureSize.width * meterPerPixel; + const textureHeightMeters = textureSize.height * meterPerPixel; + const textureArea = textureWidthMeters * textureHeightMeters; + + // 计算各轴向重复次数 + const repeatX = Math.sqrt(area) / textureWidthMeters; + const repeatY = Math.sqrt(area) / textureHeightMeters; + + return new Cesium.Cartesian2(Math.max(1, Math.ceil(repeatX)), 1.0); + } + swapLastElements(arr1, arr2) { + const last = arr1[arr1.length - 1] + const first = arr2[0] + arr1[arr1.length - 1] = first + arr2[0] = last + + return [arr1, arr2]; + } + createLineBufferPolygonSide(positions, width) { + let area = [] + for (let i = 0; i < positions.length; i++) { + const posi = positions[i]; + + const dir = Cesium.Cartesian3.subtract(posi[1], posi[0], new Cesium.Cartesian3()); + Cesium.Cartesian3.normalize(dir, dir); + + // 获取垂直向量(基于Z轴) + const perp = Cesium.Cartesian3.cross(dir, Cesium.Cartesian3.UNIT_Z, new Cesium.Cartesian3()); + Cesium.Cartesian3.normalize(perp, perp); + + // 生成偏移向量 + const offset = Cesium.Cartesian3.multiplyByScalar(perp, width, new Cesium.Cartesian3()); + let point1 = Cesium.Cartesian3.add(posi[0], offset, new Cesium.Cartesian3()) + let point3 = Cesium.Cartesian3.add(posi[1], offset, new Cesium.Cartesian3()) + + // i == positions.length - 2 ? area.push(start, point1, end, point3) : area.push(start, point1) + area.push([posi[0], point1, point3, posi[1]]) + } + // let arr = [] + // for (let i = 0; i < area.length - 2; i += 2) { + // arr.push([area[i], area[i + 1], area[i + 3], area[i + 2]]) + // } + return area + } + createLineBufferPolygon2(positions, width) { + let area = [] + let leftPositions = []; + let rightPositions = []; + + for (let i = 0; i < positions.length - 1; i++) { + const start = positions[i]; + // const end = positions[i + 1] || positions[i - 1]; + const end = positions[i + 1]; + + const dir = Cesium.Cartesian3.subtract(end, start, new Cesium.Cartesian3()); + Cesium.Cartesian3.normalize(dir, dir); + + // 获取垂直向量(基于Z轴) + const perp = Cesium.Cartesian3.cross(dir, Cesium.Cartesian3.UNIT_Z, new Cesium.Cartesian3()); + Cesium.Cartesian3.normalize(perp, perp); + + const dir2 = Cesium.Cartesian3.subtract(start, end, new Cesium.Cartesian3()); + Cesium.Cartesian3.normalize(dir2, dir2); + + // 获取垂直向量(基于Z轴) + const perp2 = Cesium.Cartesian3.cross(dir2, Cesium.Cartesian3.UNIT_Z, new Cesium.Cartesian3()); + Cesium.Cartesian3.normalize(perp2, perp2); + + + + + // 生成偏移向量 + const offset = Cesium.Cartesian3.multiplyByScalar(perp, width, new Cesium.Cartesian3()); + const offset2 = Cesium.Cartesian3.multiplyByScalar(perp, -width, new Cesium.Cartesian3()); + + const offsetEnd = Cesium.Cartesian3.multiplyByScalar(perp2, -width, new Cesium.Cartesian3()); + const offsetEnd2 = Cesium.Cartesian3.multiplyByScalar(perp2, width, new Cesium.Cartesian3()); + + let point1 = Cesium.Cartesian3.add(start, offset, new Cesium.Cartesian3()) + let point2 = Cesium.Cartesian3.add(start, offset2, new Cesium.Cartesian3()) + let point3 = Cesium.Cartesian3.add(end, offsetEnd, new Cesium.Cartesian3()) + let point4 = Cesium.Cartesian3.add(end, offsetEnd2, new Cesium.Cartesian3()) + + area.push([point1, point3, point4, point2]) + + rightPositions.push([point1, point3]) + leftPositions.push([point2, point4]) + + // if (i == positions.length - 2) { + // area.push(point1, point2, point3, point4) + // rightPositions.push(point1) + // leftPositions.push(point2) + // leftPositions.push(point4) + // rightPositions.push(point3) + // } else { + // area.push(point1, point2) + // rightPositions.push(point1) + // leftPositions.push(point2) + // } + } + // let arr = [] + // for (let i = 0; i < area.length - 2; i += 2) { + // arr.push([area[i], area[i + 1], area[i + 3], area[i + 2]]) + // } + let that = this + // return [arr, rightPositions, leftPositions] + return [area, rightPositions, leftPositions] + } + getIntersects2(segment1Start, segment1End, segment2Start, segment2End) { + // 构造平面(使用线段2的起点和方向向量) + let direction = Cesium.Cartesian3.subtract(segment2End, segment2Start, new Cesium.Cartesian3()); + let plane = Cesium.Plane.fromPointNormal(segment2Start, direction); + + // 计算线段1与平面的交点(增加容差参数) + let intersection = Cesium.IntersectionTests.lineSegmentPlane( + segment1Start, segment1End, plane, 1e-10 + ); + if (!intersection) return null; + + // 使用参数方程验证交点有效性 + let vecToIntersect = Cesium.Cartesian3.subtract(intersection, segment2Start, new Cesium.Cartesian3()); + let t = Cesium.Cartesian3.dot(vecToIntersect, direction) / + Cesium.Cartesian3.magnitudeSquared(direction); + + let result = (t >= -1e-10 && t <= 1.0 + 1e-10) ? intersection : null; + return result; + } + getIntersects(point1, point2, point3, point4) { + let carPoint1 = this.getLonLat(point1) + let carPoint2 = this.getLonLat(point2) + let carPoint3 = this.getLonLat(point3) + let carPoint4 = this.getLonLat(point4) + var line1 = turf.lineString([ + [carPoint1.lon, carPoint1.lat], + [carPoint2.lon, carPoint2.lat] + ]); + var line2 = turf.lineString([ + [carPoint3.lon, carPoint3.lat], + [carPoint4.lon, carPoint4.lat] + ]); + var intersects = turf.lineIntersect(line1, line2); + if (intersects.features.length > 0) { + return Cesium.Cartesian3.fromDegrees(intersects.features[0].geometry.coordinates[0], intersects.features[0].geometry.coordinates[1]) + } + } + getLonLat(point) { + let pointDe = Cesium.Cartographic.fromCartesian(point) + const longitude = Cesium.Math.toDegrees(pointDe.longitude); + const latitude = Cesium.Math.toDegrees(pointDe.latitude); + return { lon: longitude, lat: latitude } + + } + createLineBufferPolygon(viewer, positions, width) { + // 计算每个线段的左右偏移点 + const leftPositions = []; + const rightPositions = []; + + for (let i = 0; i < positions.length; i++) { + const start = positions[i]; + const end = positions[i + 1] || positions[i - 1]; + + // 计算线段方向向量 + const direction = Cesium.Cartesian3.subtract(end, start, new Cesium.Cartesian3()); + // const direction = Cesium.Cartesian3.subtract(start, end, new Cesium.Cartesian3()); + Cesium.Cartesian3.normalize(direction, direction); + + // 计算垂直向量(使用上向量叉积) + const up = Cesium.Cartesian3.UNIT_Z; + const perpendicular = Cesium.Cartesian3.cross(direction, up, new Cesium.Cartesian3()); + Cesium.Cartesian3.normalize(perpendicular, perpendicular); + + // 计算左右偏移点 + const leftOffset = Cesium.Cartesian3.multiplyByScalar( + perpendicular, + width, + new Cesium.Cartesian3() + ); + + if (width > 0) { + rightPositions.unshift(Cesium.Cartesian3.add(start, leftOffset, new Cesium.Cartesian3())); + } else if (width < 0) { + rightPositions.push(Cesium.Cartesian3.add(start, leftOffset, new Cesium.Cartesian3())); + } + + } + return rightPositions + } + //计算角度 + calculateAangle(arr) { + // let fromDegreesArray = that.calSector(that.options.center, that.options.radius, that.options.startAngle, that.options.endAngle, 360, true) + + function getAangle(start, end) { + let rad = Math.PI / 180, + lat1 = start.y * rad, + lat2 = end.y * rad, + lon1 = start.x * rad, + lon2 = end.x * rad; + const a = Math.sin(lon2 - lon1) * Math.cos(lat2); + const b = + Math.cos(lat1) * Math.sin(lat2) - + Math.sin(lat1) * Math.cos(lat2) * Math.cos(lon2 - lon1); + const radians = Math.atan2(a, b) + const degrees = radians % (2 * Math.PI); + let bearing = 450 - ((degrees * 180) / Math.PI < 0 + ? 360 + (degrees * 180) / Math.PI + : (degrees * 180) / Math.PI) - 90; + return 360 - (bearing % 360) + } + + let center = arr[0] + let pos84_1 = arr[1] + let pos84_2 = arr[2] + + let start = { x: center.lng, y: center.lat } + let end1 = { x: pos84_1.lng, y: pos84_1.lat } + let end2 = { x: pos84_2.lng, y: pos84_2.lat } + + let angle1 = getAangle(start, end1) + let angle2 = getAangle(start, end2) + + return { + angle1, + angle2 + } + } + + get carRoadWidth() { + return this.options.carRoadWidth + } + + set carRoadWidth(v) { + this.options.carRoadWidth = v + Road.create(this) + + } + get sideWidth() { + return this.options.sideWidth + } + set sideWidth(v) { + this.options.sideWidth = v + Road.create(this) + } + /** + * @description 编辑框 + * @param state=false {boolean} 状态: true打开, false关闭 + */ + async edit(state = false) { + let _this = this + this.originalOptions = this.deepCopyObj(this.options) + + if (this._DialogObject && this._DialogObject.close) { + this._DialogObject.close() + this._DialogObject = null + } + + if (state) { + this._DialogObject = await new Dialog(this.sdk, this.originalOptions, { + title: '道路属性', left: '180px', top: '100px', + confirmCallBack: (options) => { + this.name = this.name.trim() + if (!this.name) { + this.name = '道路' + } + this.originalOptions = this.deepCopyObj(this.options) + this._DialogObject.close() + this.Dialog.confirmCallBack && this.Dialog.confirmCallBack(this.originalOptions) + syncData(this.sdk, this.options.id) + syncSplitData(this.sdk, this.options.id) + }, + resetCallBack: () => { + this.reset() + this.Dialog.resetCallBack && this.Dialog.resetCallBack() + }, + closeCallBack: () => { + this.reset() + this.Dialog.closeCallBack && this.Dialog.closeCallBack() + }, + showCallBack: (show) => { + this.show = show + this.Dialog.showCallBack && this.Dialog.showCallBack() + } + }, true) + this._DialogObject._element.body.className = this._DialogObject._element.body.className + ' road-surface' + let contentElm = document.createElement('div'); + contentElm.innerHTML = html() + this._DialogObject.contentAppChild(contentElm) + + + // 下拉选项 + // let heightModeData = [ + // { + // name: '海拔高度', + // value: '海拔高度', + // key: '0', + // }, + // { + // name: '相对地表', + // value: '相对地表', + // key: '1', + // }, + // { + // name: '依附模型', + // value: '依附模型', + // key: '2', + // } + // ] + // let heightModeObject = legp( + // this._DialogObject._element.content.getElementsByClassName( + // 'road-box' + // )[0], + // '.road-type' + // ) + // if (heightModeObject) { + // heightModeObject.legp_search(heightModeData) + // let heightModeDataLegpElm = this._DialogObject._element.content + // .getElementsByClassName('road-type')[0] + // .getElementsByTagName('input')[0] + // for (let i = 0; i < heightModeData.length; i++) { + // if (heightModeData[i].key == this.heightMode) { + // heightModeDataLegpElm.value = heightModeData[i].value + // heightModeObject.legp_searchActive( + // heightModeData[i].value + // ) + // break + // } + // } + // heightModeDataLegpElm.addEventListener('input', () => { + // for (let i = 0; i < heightModeData.length; i++) { + // if (heightModeData[i].value === heightModeDataLegpElm.value) { + // this.heightMode = heightModeData[i].key + // break + // } + // } + // }) + + + // this._elms.height = heightElm + // this._elms.heightBox = heightBoxElm + // this._elms.heightMode = heightModeDataLegpElm + // this._elms.heightConfirm = heightConfirmElm + // this._elms.heightModeObject = heightModeObject + + // heightConfirmElm.addEventListener('click', () => { + // this.positionEditing = false + // for (let i = 0; i < this.options.positions.length; i++) { + // this.options.positions[i].alt = Number((this.options.positions[i].alt + Number(heightElm.value)).toFixed(2)) + // this._elms.alt[i].innerHTML = this.options.positions[i].alt + // } + // let fromDegreesArray = this.renewPositions(this.options.positions) + // this.entity.polyline.positions = Cesium.Cartesian3.fromDegreesArrayHeights( + // fromDegreesArray + // ) + + // this.positionEditing = false + // PolylineObject.closeNodeEdit(this) + // }) + // } + + + + + + let all_elm = contentElm.getElementsByTagName("*") + this._EventBinding.on(this, all_elm) + this._elms = this._EventBinding.element + } else { + // if (this._element_style) { + // document.getElementsByTagName('head')[0].removeChild(this._element_style) + // this._element_style = null + // } + // if (this._DialogObject && this._DialogObject.remove) { + // this._DialogObject.remove() + // this._DialogObject = null + // } + } + } + + reset() { + if (!this.viewer.entities.getById(this.options.id)) { + return + } + this.name = this.originalOptions.name + this.carRoadWidth = this.originalOptions.carRoadWidth + this.sideWidth = this.originalOptions.sideWidth + this.positions = this.originalOptions.positions + this.roadImage = this.originalOptions.roadImage + this.sideImage = this.originalOptions.sideImage + } + + /** + * 飞到对应实体 + */ + async flyTo(options = {}) { + setActiveViewer(0) + closeRotateAround(this.sdk) + closeViewFollow(this.sdk) + + if (this.options.customView && this.options.customView.relativePosition && this.options.customView.orientation) { + let orientation = { + heading: Cesium.Math.toRadians(this.options.customView.orientation.heading || 0.0), + pitch: Cesium.Math.toRadians(this.options.customView.orientation.pitch || -60.0), + roll: Cesium.Math.toRadians(this.options.customView.orientation.roll || 0.0) + } + + let lng = this.options.customView.relativePosition.lng + let lat = this.options.customView.relativePosition.lat + let alt = this.options.customView.relativePosition.alt + let destination = Cesium.Cartesian3.fromDegrees(lng, lat, alt) + + let position = { lng: 0, lat: 0 } + if (this.options.position) { + position = { ...this.options.position } + } + else if (this.options.positions) { + position = { ...this.options.positions[0] } + } + else if (this.options.center) { + position = { ...this.options.center } + } + else if (this.options.start) { + position = { ...this.options.start } + } + else { + if (this.options.hasOwnProperty('lng')) { + position.lng = this.options.lng + } + if (this.options.hasOwnProperty('lat')) { + position.lat = this.options.lat + } + if (this.options.hasOwnProperty('alt')) { + position.alt = this.options.alt + } + } + // 如果没有高度值,则获取紧贴高度计算 + // if (!position.hasOwnProperty('alt')) { + // position.alt = await this.getClampToHeight(position) + // } + lng = this.options.customView.relativePosition.lng + position.lng + lat = this.options.customView.relativePosition.lat + position.lat + alt = this.options.customView.relativePosition.alt + position.alt + destination = Cesium.Cartesian3.fromDegrees(lng, lat, alt) + this.sdk.viewer.camera.flyTo({ + destination: destination, + orientation: orientation + }) + } + else { + let positionArray = [] + for (let i = 0; i < this.positions.length; i++) { + let a = Cesium.Cartesian3.fromDegrees( + this.positions[i][0], + this.positions[i][1], + this.options.height + this.options.heightDifference / 2 + ) + positionArray.push(a.x, a.y, a.z) + } + let BoundingSphere = Cesium.BoundingSphere.fromVertices(positionArray) + this.viewer.camera.flyToBoundingSphere(BoundingSphere, { + offset: { + heading: Cesium.Math.toRadians(0.0), + pitch: Cesium.Math.toRadians(-20.0), + roll: Cesium.Math.toRadians(0.0) + } + }) + + } + } + + getSphere() { + return new Promise((resolve) => { + // entity没有加载完成时 state 不会等于0 所以设置定时器直到获取到为止 + const interval = setInterval(() => { + const sphere = new Cesium.BoundingSphere() + const state = this.sdk.viewer._dataSourceDisplay.getBoundingSphere( + this.viewer.entities.getById(this.options.id), + false, + sphere + ) + if (state === Cesium.BoundingSphereState.DONE) { + clearInterval(interval) + } + }, 1000) + }) + } + + /** + * 删除 + */ + async remove() { + this.positions = [] + this.lineEntity = null + + if (this.viewer.entities.getById(this.options.id)) { + this.viewer.entities.getById(this.options.id)._children.forEach((item) => { + this.viewer.entities.remove(item); + }); + this.viewer.entities.remove(this.viewer.entities.getById(this.options.id)) + } + + if (this._DialogObject && !this._DialogObject.isDestroy) { + this._DialogObject.close() + this._DialogObject = null + } + await this.sdk.removeIncetance(this.options.id) + await syncData(this.sdk, this.options.id) + } + + flicker() { } +} + +export default Road diff --git a/src/Obj/Base/RoadObject/index-last.js b/src/Obj/Base/RoadObject/index-last.js new file mode 100644 index 0000000..e6a958e --- /dev/null +++ b/src/Obj/Base/RoadObject/index-last.js @@ -0,0 +1,1704 @@ +/** + * @description 道路 + */ +import Dialog from '../../Element/Dialog'; +import { html } from "./_element"; +import EventBinding from '../../Element/Dialog/eventBinding'; +import Base from "../index"; +import { syncData } from '../../../Global/MultiViewportMode' +import { setSplitDirection, syncSplitData, setActiveId } from '../../../Global/SplitScreen' +import { setActiveViewer, closeRotateAround, closeViewFollow } from '../../../Global/global' + +class Road extends Base { + /** + * @constructor + * @param sdk + * @description 道路 + * @param options {object} 道路属性 + * @param options.name=未命名对象 {string} 名称 + * @param options.carRoadWidth=2 {number} 车道宽度 + * @param options.sideWidth=2 {number} 人行道宽度 + * @param options.positions=[] {array} 道路positions + * @param options.roadImage='' {string} 车道贴图 + * @param options.sideImage='' {string} 人行道贴图 + * @param Dialog {object} 弹框对象 + * @param Dialog.confirmCallBack {function} 弹框确认时的回调 + * */ + constructor(sdk, options = {}, _Dialog = {}) { + super(sdk, options); + this.viewer = this.sdk.viewer + this.options.name = options.name || '道路' + this.options.carRoadWidth = options.carRoadWidth || 10 + this.options.sideWidth = options.sideWidth || 5 + this.options.positions = options.positions || [] + this.options.roadImage = options.roadImage || (this.getSourceRootPath() + '/img/roadPhoto.png') + this.options.sideImage = options.sideImage || (this.getSourceRootPath() + '/img/sidePhoto.png') + this.options.show = (options.show || options.show === false) ? options.show : true + this.Dialog = _Dialog + this._EventBinding = new EventBinding() + this._elms = {}; + this.positionArea = [] + this.positions = [] + this.lineEntity = '' + this.crossArr = [] + this.pointArr = [] + this.positionChangeIndex = [] + + this.sdk.addIncetance(this.options.id, this) + // Road.create(this) + const myImg = new Image() + myImg.src = this.options.roadImage + myImg.onload = () => { + console.log('加载完成') + this.createCorridor(myImg, this.options.positions) + } + } + + // 创建走廊几何体 + createCorridor(myImg, posis) { + let posiArr = [] + posis.forEach(item => { + posiArr.push(item.lng, item.lat) + }) + console.log(posiArr, 'posiArr') + + const positions = Cesium.Cartesian3.fromDegreesArray(posiArr); + + const corridorInstance = new Cesium.GeometryInstance({ + geometry: new Cesium.CorridorGeometry({ + positions: positions, + width: this.options.carRoadWidth, + vertexFormat: Cesium.VertexFormat.POSITION_AND_ST, + cornerType: Cesium.CornerType.MITERED, + extrudedHeight: 10.0 + }), + attributes: { + color: Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.WHITE) + } + }); + + const primitive = new Cesium.Primitive({ + geometryInstances: corridorInstance, + appearance: new Cesium.MaterialAppearance({ + material: new Cesium.Material({ + fabric: { + type: 'Image', + uniforms: { + image: myImg + } + } + }), + translucent: false + }), + asynchronous: false + }); + + this.sdk.viewer.scene.primitives.add(primitive); + + // 定位到走廊 + // this.sdk.viewer.camera.flyTo({ + // destination: Cesium.Cartesian3.fromDegrees(103.70271877620554, 41.95279346036913, 100), + // // orientation: { + // // heading: Cesium.Math.toRadians(0), + // // pitch: Cesium.Math.toRadians(-30), + // // roll: 0.0 + // // } + // }); + + return primitive; + } + // 横断面计算函数 + computeCrossSection(width, slope) { + const halfWidth = width / 2; + return [ + new Cesium.Cartesian2(-halfWidth, 0), + new Cesium.Cartesian2(-halfWidth, -slope * 10), + new Cesium.Cartesian2(halfWidth, -slope * 10), + new Cesium.Cartesian2(halfWidth, 0) + ]; + } + computeST(geometry) { + // 安全校验:确保几何体包含位置属性 + if (!geometry.attributes.position) { + throw new Error('Geometry must contain position attribute'); + } + + const positions = geometry.attributes.position.values; + const vertexCount = positions.length / 3; + const st = new Float32Array(vertexCount * 2); + + // 计算UV坐标(改进版) + for (let i = 0; i < vertexCount; i++) { + const vertexIndex = i * 3; + const uvIndex = i * 2; + + // 根据顶点在横截面中的位置计算V坐标 + const isTopVertex = positions[vertexIndex + 2] > 0; // Z坐标判断顶面/底面 + st[uvIndex + 1] = isTopVertex ? 1 : 0; + + // 根据路径比例计算U坐标(自动适应不同路段长度) + const pathProgress = i % 4; // 每4个顶点为一个横截面 + st[uvIndex] = (Math.floor(i / 4) + pathProgress / 4) / (vertexCount / 4) * 10; + } + return st; + } + // 创建道路 + static create(that) { + let positions = [] + that.options.positions.forEach(v => { + positions.push(new Cesium.Cartesian3.fromDegrees(v.lng, v.lat, v.alt)) + }) + let newPosi = [] + for (let i = 0; i < positions.length - 1; i++) { + const start = positions[i]; + const end = positions[i + 1]; + newPosi.push([start, end]) + that.pointArr = newPosi + // 1. 定义道路中心线路径(WGS84坐标) + const roadPath = [start, end]; + // 3. 计算纹理坐标(确保贴图沿道路方向连续) + console.log(roadPath, 'roadPath') + // 4. 创建道路几何体 + const roadGeometry = new Cesium.PolylineVolumeGeometry({ + polylinePositions: roadPath, + shapePositions: that.computeCrossSection(20, 0.5), + vertexFormat: Cesium.VertexFormat.POSITION_AND_ST, + cornerType: Cesium.CornerType.ROUNDED + }); + console.log('kkkkk') + + // 5. 使用Primitive渲染带贴图的道路 + const roadPrimitive = that.viewer.scene.primitives.add( + new Cesium.Primitive({ + geometryInstances: new Cesium.GeometryInstance({ + geometry: roadGeometry, + attributes: { + st: new Cesium.GeometryAttribute({ + componentDatatype: Cesium.ComponentDatatype.FLOAT, + componentsPerAttribute: 2, + values: (function () { + if (!roadGeometry.attributes.position) { + console.error('Road geometry lacks position data'); + return new Float32Array(0); + } + return that.computeST(roadGeometry); + })() + }) + } + }), + appearance: new Cesium.MaterialAppearance({ + material: new Cesium.Material({ + fabric: { + type: 'Image', + uniforms: { + image: that.options.roadImage, // 沥青纹理示例 + repeat: new Cesium.Cartesian2(1, 1) + } + }, + translucent: false + }), + faceForward: false // 确保双面渲染 + }) + }) + ); + + } + + let area = [[], [], []] + + // area[1] = that.getRectangle(positions, that.options.carRoadWidth) + area[1][0] = that.getRectangle(newPosi, that.options.carRoadWidth) + let sideArr = that.getSideRectangle(area[1][0], that.options.sideWidth) + area[0] = sideArr.left + area[2] = sideArr.right + + // 参数化生成路基模型 + + + + + + + + // area[1] = that.createLineBufferPolygon2(positions, that.options.carRoadWidth / 2) + // area[1] = newPositions + // area[0] = that.createLineBufferPolygonSide(area[1][2], -that.options.sideWidth) + // area[2] = that.createLineBufferPolygonSide(area[1][1], that.options.sideWidth) + + //判断道路边是否相交 + // for (let i = 0; i < area[0].length - 1; i++) { + + // let leftItem = area[0][i] + // let leftItem2 = area[0][i + 1] + // let rightItem = area[2][i] + // let rightItem2 = area[2][i + 1] + // let carItem = area[1][0][i] + // let carItem2 = area[1][0][i + 1] + // let leftLine = that.getIntersects(leftItem[2], leftItem[3], leftItem2[2], leftItem2[3]) + // // that.sdk.viewer.entities.add({ + // // polyline: { + // // positions: [leftItem[2], leftItem[3]], + // // width: 2.0, + // // clampToGround: true, + // // material: new Cesium.PolylineGlowMaterialProperty({ + // // color: Cesium.Color.YELLOW, + // // }), + // // }, + // // }); + // // that.sdk.viewer.entities.add({ + // // polyline: { + // // positions: [leftItem2[2], leftItem2[3]], + // // width: 2.0, + // // clampToGround: true, + // // material: new Cesium.PolylineGlowMaterialProperty({ + // // color: Cesium.Color.YELLOW, + // // }), + // // }, + // // }); + + // let rightLine = that.getIntersects(rightItem[0], rightItem[1], rightItem2[0], rightItem2[1]) + // // if (!leftLine && !rightLine) { + // // for (let index = 0; index < 4; index++) { + // // let positions = [] + // // index === 0 ? positions.push(leftItem[2], leftItem[3]) : index === 1 ? positions.push(leftItem2[2], leftItem2[3]) : index === 2 ? positions.push(rightItem[0], rightItem[1]) : positions.push(rightItem2[0], rightItem2[1]) + // // that.sdk.viewer.entities.add({ + // // polyline: { + // // positions: positions, + // // width: 2.0, + // // clampToGround: true, + // // material: new Cesium.PolylineGlowMaterialProperty({ + // // color: index === 0 ? Cesium.Color.RED : index === 1 ? Cesium.Color.BLUE : index === 2 ? Cesium.Color.YELLOW : Cesium.Color.GREEN, + // // }), + // // }, + // // }); + + // // } + // // } + + + + // for (let index = 0; index < 4; index++) { + // // that.sdk.viewer.entities.add({ + // // name: 'node-secondary-edit-point', + // // index: index, + // // position: area[0][i][index], + // // billboard: { + // // image: that.getSourceRootPath() + '/img/point.png', + // // width: 15, + // // height: 15, + // // disableDepthTestDistance: Number.POSITIVE_INFINITY, + // // color: Cesium.Color.WHITE.withAlpha(0.99) + // // }, + // // label: { + // // text: '' + index + ':', + // // pixelOffset: { x: 0, y: -20 }, + // // }, + // // }) + + // // that.sdk.viewer.entities.add({ + // // name: 'node-secondary-edit-point', + // // index: index, + // // position: area[2][i][index], + // // billboard: { + // // image: that.getSourceRootPath() + '/img/point.png', + // // width: 15, + // // height: 15, + // // disableDepthTestDistance: Number.POSITIVE_INFINITY, + // // color: Cesium.Color.WHITE.withAlpha(0.99) + // // }, + // // label: { + // // text: '' + index + '/', + // // pixelOffset: { x: 0, y: -20 }, + // // }, + // // }) + // } + + + // if (leftLine) {//左侧相交 + // // that.sdk.viewer.entities.add({ + // // name: 'node-secondary-edit-point', + // // index: 1, + // // position: leftLine, + // // billboard: { + // // image: that.getSourceRootPath() + '/img/locate2.png', + // // width: 15, + // // height: 15, + // // disableDepthTestDistance: Number.POSITIVE_INFINITY, + // // color: Cesium.Color.WHITE.withAlpha(0.99) + // // }, + // // label: { + // // text: '', + // // pixelOffset: { x: 0, y: -20 }, + // // }, + // // }) + + // //获取右侧延长交点 + // let point1 = that.getExtendPoint(rightItem[0], rightItem[1], 1000) + // let point2 = that.getExtendPoint(rightItem2[1], rightItem2[0], 1000) + + // that.sdk.viewer.entities.add({ + // polyline: { + // positions: [rightItem[0], point1], + // width: 2.0, + // clampToGround: true, + // material: new Cesium.PolylineGlowMaterialProperty({ + // color: Cesium.Color.AQUA, + // }), + // }, + // }); + // that.sdk.viewer.entities.add({ + // polyline: { + // positions: [rightItem2[1], point2], + // width: 2.0, + // clampToGround: true, + // material: new Cesium.PolylineGlowMaterialProperty({ + // color: Cesium.Color.AQUA, + // }), + // }, + // }); + + // that.sdk.viewer.entities.add({ + // name: 'node-secondary-edit-point', + // index: i, + // position: point1, + // billboard: { + // image: that.getSourceRootPath() + '/img/point.png', + // width: 15, + // height: 15, + // disableDepthTestDistance: Number.POSITIVE_INFINITY, + // color: Cesium.Color.WHITE.withAlpha(0.99) + // }, + // label: { + // text: '' + 7 + "", + // pixelOffset: { x: 0, y: -20 }, + // }, + // }) + // that.sdk.viewer.entities.add({ + // name: 'node-secondary-edit-point', + // index: i, + // position: point2, + // billboard: { + // image: that.getSourceRootPath() + '/img/end.png', + // width: 15, + // height: 15, + // disableDepthTestDistance: Number.POSITIVE_INFINITY, + // color: Cesium.Color.WHITE.withAlpha(0.99) + // }, + // label: { + // text: '' + 7 + "", + // pixelOffset: { x: 0, y: -20 }, + // }, + // }) + + + // let rightIntersection = that.getIntersects(rightItem[0], point1, rightItem2[1], point2) + // if (!rightIntersection) { + // return + // } + + // //将其他几条边都延长 + // let leftLineNeiPoint = that.getExtendPoint(leftItem[0], leftItem[1], 1000) + // let carLeftPoint = that.getExtendPoint(carItem[3], carItem[2], 1000) + // let carRightPoint = that.getExtendPoint(carItem[0], carItem[1], 1000) + + // let rightLineNeiPoint = that.getExtendPoint(rightItem[3], rightItem[2], 1000) + + + + // //跟左侧里相交点 + // let leftLineNei = that.getIntersects(leftLine, rightIntersection, leftItem[0], leftLineNeiPoint) + // //跟车道左侧相交点 + // let carLeft = that.getIntersects(leftLine, rightIntersection, carItem[3], carLeftPoint) + + // //跟车道右侧相交点 + // let carRight = that.getIntersects(leftLine, rightIntersection, carItem[0], carRightPoint) + // let rightLineNei = that.getIntersects(leftLine, rightIntersection, rightItem[3], rightLineNeiPoint) + // // let leftLineNei = that.getIntersects(leftLine, rightItem[2], leftItem[0], leftItem[3]) + // // let carLeft = that.getIntersects(leftLine, rightItem[2], carItem[3], carItem[2]) + // // let carRight = that.getIntersects(leftLine, rightItem[2], carItem[0], carItem[1]) + // // let rightLineNei = that.getIntersects(leftLine, rightItem[2], rightItem[0], rightItem[3]) + + // // let leftLineNei = that.getIntersects(leftLine, intersection, leftItem[0], leftItem[3]) + // // //跟车道左侧相交点 + // // let carLeft = that.getIntersects(leftLine, intersection, carItem[3], carItem[2]) + // // //跟车道右侧相交点 + // // let carRight = that.getIntersects(leftLine, intersection, carItem[0], carItem[1]) + // // let rightLineNei = that.getIntersects(leftLine, intersection, rightItem[0], rightItem[3]) + + // leftItem[2] = leftLine + // leftItem[1] = leftLineNei + // // carItem[2] = carLeft + // carItem[2] = leftLineNei + // // carItem[1] = carRight + // carItem[1] = rightLineNei + // rightItem[2] = rightLineNei + // rightItem[1] = rightIntersection + + + // // that.sdk.viewer.entities.add({ + // // name: 'node-secondary-edit-point', + // // index: 1, + // // position: leftLineNei, + // // billboard: { + // // image: that.getSourceRootPath() + '/img/locate2.png', + // // width: 15, + // // height: 15, + // // disableDepthTestDistance: Number.POSITIVE_INFINITY, + // // color: Cesium.Color.WHITE.withAlpha(0.99) + // // }, + // // label: { + // // text: '', + // // pixelOffset: { x: 0, y: -20 }, + // // }, + // // }) + // that.sdk.viewer.entities.add({ + // name: 'node-secondary-edit-point', + // index: 1, + // position: rightIntersection, + // billboard: { + // image: that.getSourceRootPath() + '/img/locate2.png', + // width: 15, + // height: 15, + // disableDepthTestDistance: Number.POSITIVE_INFINITY, + // color: Cesium.Color.WHITE.withAlpha(0.99) + // }, + // label: { + // text: '', + // pixelOffset: { x: 0, y: -20 }, + // }, + // }) + + + + // //将其他几条边都延长 + // let leftLineNeiPoint2 = that.getExtendPoint(leftItem2[1], leftItem2[0], 1000) + // let carLeftPoint2 = that.getExtendPoint(carItem2[2], carItem2[3], 1000) + // let carRightPoint2 = that.getExtendPoint(carItem2[1], carItem2[0], 1000) + // let rightLineNeiPoint2 = that.getExtendPoint(rightItem2[2], rightItem2[3], 1000) + + // // let leftLineNei2 = that.getIntersects(leftLine, rightItem2[1], leftItem2[0], leftItem2[3]) + // // let carLeft2 = that.getIntersects(leftLine, rightItem2[1], carItem2[3], carItem2[2]) + // // let carRight2 = that.getIntersects(leftLine, rightItem2[1], carItem2[0], carItem2[1]) + // // let rightLineNei2 = that.getIntersects(leftLine, rightItem2[1], rightItem2[0], rightItem2[3]) + + + + // let leftLineNei2 = that.getIntersects(leftLine, rightIntersection, leftItem2[1], leftLineNeiPoint2) + // let carLeft2 = that.getIntersects(leftLine, rightIntersection, carItem2[2], carLeftPoint2) + // let carRight2 = that.getIntersects(leftLine, rightIntersection, carItem2[1], carRightPoint2) + // let rightLineNei2 = that.getIntersects(leftLine, rightIntersection, rightItem2[2], rightLineNeiPoint2) + + // // let arr = [leftLine, rightIntersection, leftItem2[3], leftLineNeiPoint2] + // // arr.forEach((item, index) => { + // // that.sdk.viewer.entities.add({ + // // name: 'node-secondary-edit-point', + // // index: i, + // // position: item, + // // billboard: { + // // image: that.getSourceRootPath() + '/img/point.png', + // // width: 15, + // // height: 15, + // // disableDepthTestDistance: Number.POSITIVE_INFINITY, + // // color: Cesium.Color.WHITE.withAlpha(0.99) + // // }, + // // label: { + // // text: '' + index, + // // pixelOffset: { x: 0, y: -20 }, + // // }, + // // }) + // // }) + + // // let leftLineNei2 = that.getIntersects(leftLine, intersection, leftItem2[0], leftItem2[3]) + // // //跟车道左侧相交点 + // // let carLeft2 = that.getIntersects(leftLine, intersection, carItem2[3], carItem2[2]) + // // //跟车道右侧相交点 + // // let carRight2 = that.getIntersects(leftLine, intersection, carItem2[0], carItem2[1]) + // // let rightLineNei2 = that.getIntersects(leftLine, intersection, rightItem2[0], rightItem2[3]) + + // leftItem2[3] = leftLine + // leftItem2[0] = leftLineNei + // // carItem2[3] = carLeft + // carItem2[3] = leftLineNei + // // carItem2[0] = carRight + // carItem2[0] = rightLineNei + // rightItem2[3] = rightLineNei + // rightItem2[0] = rightIntersection + + + // } else if (rightLine) {//右侧相交 + + // //获取左侧延长交点 + // let point1 = that.getExtendPoint(leftItem[3], leftItem[2], 1000) + // let point2 = that.getExtendPoint(leftItem2[2], leftItem2[3], 1000) + + // that.sdk.viewer.entities.add({ + // polyline: { + // positions: [leftItem[3], point1], + // width: 2.0, + // clampToGround: true, + // material: new Cesium.PolylineGlowMaterialProperty({ + // color: Cesium.Color.AQUA, + // }), + // }, + // }); + // that.sdk.viewer.entities.add({ + // polyline: { + // positions: [leftItem2[2], point2], + // width: 2.0, + // clampToGround: true, + // material: new Cesium.PolylineGlowMaterialProperty({ + // color: Cesium.Color.AQUA, + // }), + // }, + // }); + + // let rightIntersection = that.getIntersects(leftItem[3], point1, leftItem2[2], point2) + // if (!rightIntersection) { + // return + // } + // //将其他几条边都延长 + // let leftLineNeiPoint = that.getExtendPoint(leftItem[0], leftItem[1], 1000) + + // let carLeftPoint = that.getExtendPoint(carItem[3], carItem[2], 1000) + // let carRightPoint = that.getExtendPoint(carItem[0], carItem[1], 1000) + // let rightLineNeiPoint = that.getExtendPoint(rightItem[3], rightItem[2], 1000) + + // // //跟左侧里相交点 + // let leftLineNei = that.getIntersects(rightLine, rightIntersection, leftItem[0], leftLineNeiPoint) + // //跟车道左侧相交点 + // let carLeft = that.getIntersects(rightLine, rightIntersection, carItem[3], carLeftPoint) + // //跟车道右侧相交点 + // let carRight = that.getIntersects(rightLine, rightIntersection, carItem[0], carRightPoint) + // let rightLineNei = that.getIntersects(rightLine, rightIntersection, rightItem[3], rightLineNeiPoint) + // // //跟左侧里相交点 + // // let leftLineNei = that.getIntersects(rightLine, leftItem[2], leftItem[0], leftItem[3]) + // // //跟车道左侧相交点 + // // let carLeft = that.getIntersects(rightLine, leftItem[2], carItem[3], carItem[2]) + // // //跟车道右侧相交点 + // // let carRight = that.getIntersects(rightLine, leftItem[2], carItem[0], carItem[1]) + // // let rightLineNei = that.getIntersects(rightLine, leftItem[2], rightItem[0], rightItem[3]) + + + // leftItem[2] = rightIntersection + // leftItem[1] = leftLineNei + // // carItem[2] = carLeft + // carItem[2] = leftLineNei + // // carItem[1] = carRight + // carItem[1] = rightLineNei + // rightItem[2] = rightLineNei + // rightItem[1] = rightLine + + // //将其他几条边都延长 + // let leftLineNeiPoint2 = that.getExtendPoint(leftItem2[2], leftItem2[3], 1000) + // let carLeftPoint2 = that.getExtendPoint(carItem2[2], carItem2[3], 1000) + // let carRightPoint2 = that.getExtendPoint(carItem2[1], carItem2[0], 1000) + // let rightLineNeiPoint2 = that.getExtendPoint(rightItem2[2], rightItem2[3], 1000) + + // let leftLineNei2 = that.getIntersects(rightLine, rightIntersection, leftItem2[1], leftLineNeiPoint2) + // //跟车道左侧相交点 + // let carLeft2 = that.getIntersects(rightLine, rightIntersection, carItem2[2], carLeftPoint2) + // //跟车道右侧相交点 + // let carRight2 = that.getIntersects(rightLine, rightIntersection, carItem2[1], carRightPoint2) + // let rightLineNei2 = that.getIntersects(rightLine, rightIntersection, rightItem2[2], rightLineNeiPoint2) + // // let leftLineNei2 = that.getIntersects(rightLine, leftItem2[1], leftItem2[0], leftItem2[3]) + // // //跟车道左侧相交点 + // // let carLeft2 = that.getIntersects(rightLine, leftItem2[1], carItem2[3], carItem2[2]) + // // //跟车道右侧相交点 + // // let carRight2 = that.getIntersects(rightLine, leftItem2[1], carItem2[0], carItem2[1]) + // // let rightLineNei2 = that.getIntersects(rightLine, leftItem2[1], rightItem2[0], rightItem2[3]) + + // leftItem2[3] = rightIntersection + // leftItem2[0] = leftLineNei + // // carItem2[3] = carLeft + // carItem2[3] = leftLineNei + // // carItem2[0] = carRight + // carItem2[0] = rightLineNei + // rightItem2[3] = rightLineNei + // rightItem2[0] = rightLine + + // // leftItem[2] = rightIntersection + // // leftItem[1] = leftLineNei + // // // carItem[2] = carLeft + // // carItem[2] = leftLineNei + // // // carItem[1] = carRight + // // carItem[1] = rightLineNei + // // rightItem[2] = rightLineNei + // // rightItem[1] = rightLine + // } + + // // area[0][i] = leftItem + // // area[0][i + 1] = leftItem2 + // // area[2][i] = rightItem + // // area[2][i + 1] = rightItem2 + // // area[1][0][i] = carItem + // // area[1][0][i + 1] = carItem2 + // } + // if (that.viewer.entities.getById(that.options.id)) { + // that.viewer.entities.getById(that.options.id)._children.forEach((item) => { + // that.viewer.entities.remove(item); + // }); + // that.viewer.entities.remove(that.viewer.entities.getById(that.options.id)) + // } + // that.lineEntity = that.viewer.entities.add(new Cesium.Entity({ id: that.options.id, show: that.options.show })) + + // const myImg = new Image() + // myImg.src = that.options.roadImage + // myImg.onload = function () { + // area[1][0].forEach((item, index) => { + + + + + // // that.viewer.entities.add({ + // // // id: that.options.id, + // // parent: that.lineEntity, + // // polygon: { + // // hierarchy: new Cesium.PolygonHierarchy(item), + // // heightReference: Cesium.HeightReference.CLAMP_TO_GROUND, + // // classificationType: Cesium.ClassificationType.BOTH, + // // distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0, 10000), + // // material: new Cesium.ImageMaterialProperty({ + // // image: that.options.roadImage, + // // transparent: true,// 如果图片有透明部分,需要设置为 true + // // repeat: that.calculateTextureRepeat(item, myImg) + // // }), + // // // stRotation: that.calculateRoadAngle(positions[index], positions[index + 1]) + // // stRotation: that.calculateRoadAngle(item[0], item[1]) + // // // material: Cesium.Color.fromRandom({ alpha: 0.5 }), + // // } + // // }); + // }) + // } + + // const myImg2 = new Image() + // myImg2.src = that.options.sideImage + // myImg2.onload = function () { + // area[0].forEach((item, index) => { + // that.viewer.entities.add({ + // parent: that.lineEntity, + // polygon: { + // hierarchy: new Cesium.PolygonHierarchy(item), + // material: new Cesium.ImageMaterialProperty({ + // image: that.options.sideImage, + // transparent: true,// 如果图片有透明部分,需要设置为 true + // repeat: that.calculateTextureRepeat(item, myImg2) + // }), + // stRotation: that.calculateRoadAngle(positions[index], positions[index + 1]) + // // material: Cesium.Color.fromRandom({ alpha: 0.5 }), + // } + // }); + // }) + + // area[2].forEach((item, index) => { + // that.viewer.entities.add({ + // parent: that.lineEntity, + // polygon: { + // hierarchy: new Cesium.PolygonHierarchy(item), + // material: new Cesium.ImageMaterialProperty({ + // image: that.options.sideImage, + // transparent: true,// 如果图片有透明部分,需要设置为 true + // repeat: that.calculateTextureRepeat(item, myImg2) + // }), + // stRotation: that.calculateRoadAngle(positions[index], positions[index + 1]) + // // material: Cesium.Color.fromRandom({ alpha: 0.5 }), + // } + // }); + // }) + // } + + } + getSideRectangle(positions, width) { + let right = [] + let left = [] + let that = this + positions.forEach((item, i) => { + // console.log(that.positionChangeIndex, 'positionChangeIndex') + // if (that.positionChangeIndex[i + '']) { + // console.log('1111') + // right.push([item[2], item[3]]) + // left.push([item[0], item[1]]) + // } else { + right.push([item[0], item[1]]) + // left.push([item[2], item[3]]) + left.push([item[3], item[2]]) + // } + + + for (let index = 0; index < 2; index++) { + let ArrArr = [] + index === 0 ? ArrArr.push(item[0], item[1]) : ArrArr.push(item[2], item[3]); + // that.sdk.viewer.entities.add({ + // polyline: { + // positions: ArrArr, + // width: 2.0, + // clampToGround: true, + // material: new Cesium.PolylineGlowMaterialProperty({ + // color: index === 0 ? Cesium.Color.RED : Cesium.Color.BLUE, + // }), + // }, + // }); + } + }) + let rightPosi = that.getRectangle(right, width, 'side') + let newRightPosi = [] + right.forEach((item, index) => { + newRightPosi.push([rightPosi[index][0], rightPosi[index][1], item[1], item[0]]) + }) + + + let leftPosi = this.getRectangle(left, width, 'side') + let newLeftPosi = [] + left.forEach((item, index) => { + // newLeftPosi.push([item[0], item[1], leftPosi[index][2]], leftPosi[index][3]) + newLeftPosi.push([item[0], item[1], leftPosi[index][2], leftPosi[index][3]]) + }) + return { left: newLeftPosi, right: newRightPosi } + } + getRectangle(positions, width, type) { + let areaArr = [] + let newPositions = [] + let that = this + // for (let i = 0; i < positions.length - 1; i++) { + for (let i = 0; i < positions.length; i++) { + const start = positions[i][0]; + const end = positions[i][1]; + + + areaArr[i] = [] + let posi = [] + let Outlinegeometry = new Cesium.CorridorGeometry({ + positions: [start, end], + width: width, + cornerType: Cesium.CornerType.MITERED, + vertexFormat: Cesium.MaterialAppearance.MaterialSupport.ALL.vertexFormat + }) + let geometry = Cesium.CorridorGeometry.createGeometry(Outlinegeometry) + for (let j = 0; j < geometry.attributes.position.values.length; j += 3) { + let val = that.cartesian3Towgs84(new Cesium.Cartesian3(geometry.attributes.position.values[j], geometry.attributes.position.values[j + 1], geometry.attributes.position.values[j + 2]), that.sdk.viewer) + posi.push([val.lng, val.lat]) + } + + for (let x = 0; x < geometry.indices.length; x += 3) { + areaArr[i].push(turf.polygon([[posi[geometry.indices[x]], posi[geometry.indices[x + 1]], posi[geometry.indices[x + 2]], posi[geometry.indices[x]]]])) + } + + let geojson = turf.union(areaArr[i][0], areaArr[i][1]); + let arr = [] + geojson.geometry.coordinates[0].pop() + geojson.geometry.coordinates[0].forEach(item => { + arr.push(new Cesium.Cartesian3.fromDegrees(item[0], item[1])) + }) + let dotResult, angle + const tempVec = new Cesium.Cartesian3(); + + // 计算并归一化第一个向量 + const vector1 = Cesium.Cartesian3.normalize( + Cesium.Cartesian3.subtract(that.pointArr[i][1], that.pointArr[i][0], tempVec), + tempVec + ); + + // 计算并归一化第二个向量 + const vector2 = Cesium.Cartesian3.normalize( + Cesium.Cartesian3.subtract(arr[1], arr[0], new Cesium.Cartesian3()), + new Cesium.Cartesian3() + ); + + dotResult = Cesium.Cartesian3.dot(vector1, vector2); + if ((0.999 < dotResult && dotResult < 1.001) || (-0.999 > dotResult && dotResult > -1.001)) {//调整方向 + // let newArr = [] + // newArr[0] = arr[2] + // newArr[1] = arr[3] + // newArr[2] = arr[0] + // newArr[3] = arr[1] + // newPositions.push(newArr) + //判断方向是否正确 + const vec1 = Cesium.Cartesian3.normalize( + Cesium.Cartesian3.subtract(start, arr[0], new Cesium.Cartesian3()), + new Cesium.Cartesian3() + ); + + // 计算并归一化第二个向量 + const vec2 = Cesium.Cartesian3.normalize( + Cesium.Cartesian3.subtract(arr[1], arr[0], new Cesium.Cartesian3()), + new Cesium.Cartesian3() + ); + + let dRst = Cesium.Cartesian3.dot(vec1, vec2); + + + if (0 < dRst && dRst < 0.0001) { + newPositions.push(arr) + } else { + + let dis1 = that.distancePointToLine(start, arr[0], arr[1]) + let dis2 = that.distancePointToLine(start, arr[2], arr[3]) + let newArr = [] + if (dis1 > dis2) {// + newArr[0] = arr[3] + newArr[1] = arr[0] + newArr[2] = arr[1] + newArr[3] = arr[2] + } else { + newArr[0] = arr[2] + newArr[1] = arr[3] + newArr[2] = arr[0] + newArr[3] = arr[1] + } + newPositions.push(newArr) + + + // let newArr = [] + // newArr[0] = arr[2] + // newArr[1] = arr[3] + // newArr[2] = arr[0] + // newArr[3] = arr[1] + // newPositions.push(newArr) + } + + // newPositions.push(arr) + + if (!type) { + for (let index = 0; index < 4; index++) { + that.sdk.viewer.entities.add({ + name: 'node-secondary-edit-point', + index: index, + position: arr[index], + billboard: { + image: this.getSourceRootPath() + '/img/point.png', + width: 15, + height: 15, + disableDepthTestDistance: Number.POSITIVE_INFINITY, + color: Cesium.Color.WHITE.withAlpha(0.99) + }, + label: { + text: '' + index + '’', + pixelOffset: { x: 0, y: -20 }, + }, + }) + + } + + } + } else { + let dis1 = that.distancePointToLine(that.pointArr[i][0], arr[0], arr[1]) + let dis2 = that.distancePointToLine(that.pointArr[i][0], arr[2], arr[3]) + let newArr = [] + if (dis1 > dis2) { + newArr[0] = arr[3] + newArr[1] = arr[0] + newArr[2] = arr[1] + newArr[3] = arr[2] + } else { + newArr[0] = arr[1] + newArr[1] = arr[2] + newArr[2] = arr[3] + newArr[3] = arr[0] + } + newPositions.push(newArr) + + if (!type) { + for (let index = 0; index < 4; index++) { + that.sdk.viewer.entities.add({ + name: 'node-secondary-edit-point', + index: index, + // position: newArr[index], + position: newArr[index], + billboard: { + image: this.getSourceRootPath() + '/img/point.png', + width: 15, + height: 15, + disableDepthTestDistance: Number.POSITIVE_INFINITY, + color: Cesium.Color.WHITE.withAlpha(0.99) + }, + label: { + text: '' + index, + pixelOffset: { x: 0, y: -20 }, + }, + }) + } + } + // !type && that.positionChangeIndex.push(i + "") + } + // } + + // if (!type) { + // // if (cross > 0 && !type) {//调整方向 + // if ((0.999 < dotResult && dotResult < 1.001) || (-0.999 > dotResult && dotResult > -1.001)) {//调整方向 + // newPositions.push(arr) + // } else { + // let newArr = [] + // newArr[0] = arr[1] + // newArr[1] = arr[2] + // newArr[2] = arr[3] + // newArr[3] = arr[0] + // newPositions.push(newArr) + // } + // } else { + // newPositions.push(arr) + // } + } + return newPositions + } + distancePointToLine(p, a, b) { + let ab = new Cesium.Cartesian3(); + Cesium.Cartesian3.subtract(b, a, ab); + + let ap = new Cesium.Cartesian3(); + Cesium.Cartesian3.subtract(p, a, ap); + + let abNormalized = new Cesium.Cartesian3(); + Cesium.Cartesian3.normalize(ab, abNormalized); + + let apProjectionMagnitude = Cesium.Cartesian3.dot(ap, abNormalized); + let apProjection = Cesium.Cartesian3.multiplyByScalar(abNormalized, apProjectionMagnitude, new Cesium.Cartesian3()); + + let footPoint = new Cesium.Cartesian3(); + Cesium.Cartesian3.add(a, apProjection, footPoint); + + return Cesium.Cartesian3.distance(p, footPoint); + } + getExtendPoint2(position1, position2, distance) { + // let position1 = Cesium.Cartesian3.fromDegrees(p1[0], p1[1], 0); + // let position2 = Cesium.Cartesian3.fromDegrees(p2[0], p2[1], 0); + let pot = Cesium.Cartesian3.subtract(position2, position1, new Cesium.Cartesian3());//方向 + var dir = Cesium.Cartesian3.normalize(pot, new Cesium.Cartesian3());//向量归一化 + + var ray = new Cesium.Ray(position1, dir); + let np = Cesium.Ray.getPoint(ray, distance * 10);//计算延长点 + return np + } + getExtendPoint3(pos1, pos2, distance) { + const geodesic = new Cesium.EllipsoidGeodesic(); + geodesic.setEndPoints(pos1, pos2); + + // 关键步骤:获取实际测地距离 + const actualDist = geodesic.surfaceDistance; + const ratio = Math.max(1.1, distance / actualDist); // 强制外延比例 + + // 基于地理坐标的弧度插值 + const carto1 = Cesium.Cartographic.fromCartesian(pos1); + const carto2 = Cesium.Cartographic.fromCartesian(pos2); + + const resultCarto = new Cesium.Cartographic( + carto1.longitude + (carto2.longitude - carto1.longitude) * ratio, + carto1.latitude + (carto2.latitude - carto1.latitude) * ratio, + 0 + ); + + return Cesium.Cartesian3.fromRadians( + resultCarto.longitude, + resultCarto.latitude + ); + } + getExtendPoint(position1, position2, distance) { + // 1. 向量计算与归一化 + const direction = Cesium.Cartesian3.subtract( + position2, + position1, + new Cesium.Cartesian3() + ); + Cesium.Cartesian3.normalize(direction, direction); + + // 2. 使用Ellipsoid修正地球曲率影响 + const ellipsoid = Cesium.Ellipsoid.WGS84; + const geodesic = new Cesium.EllipsoidGeodesic(); + geodesic.setEndPoints(position1, position2); + const actualDistance = geodesic.surfaceDistance; // 获取实际测地距离 + if (distance <= actualDistance) distance = actualDistance * 100; // 强制向外延伸 + + // 3. 考虑椭球面曲率的精确延长 + const surfaceNormal = ellipsoid.geodeticSurfaceNormal(position1); + const projection = Cesium.Cartesian3.multiplyByScalar( + surfaceNormal, + Cesium.Cartesian3.dot(direction, surfaceNormal), + new Cesium.Cartesian3() + ); + Cesium.Cartesian3.subtract(direction, projection, direction); + Cesium.Cartesian3.normalize(direction, direction); + + // 4. 最终坐标计算 + return Cesium.Cartesian3.add( + position2, + Cesium.Cartesian3.multiplyByScalar(direction, distance, new Cesium.Cartesian3()), + new Cesium.Cartesian3() + ); + } + getArr(arr1, arr2) { + arr2 = arr2.reverse() + let polygon = [] + for (let index = 0; index < arr1.length - 1; index++) { + polygon.push([arr1[index], arr1[index + 1], arr2[index + 1], arr2[index]]) + } + return polygon + } + calculateRoadAngle2(startPoint, endPoint) { + // 1. 获取地表法向量 + const normal = this.sdk.viewer.scene.globe.ellipsoid.geodeticSurfaceNormal( + startPoint, new Cesium.Cartesian3()); + + // 2. 构建带椭球参数的ENU矩阵 + const enuMatrix = Cesium.Transforms.eastNorthUpToFixedFrame( + startPoint, this.sdk.viewer.scene.globe.ellipsoid, normal); + const inverseMatrix = Cesium.Matrix4.inverse( + enuMatrix, new Cesium.Matrix4()); + + // 3. 转换坐标并计算相对向量 + const localEnd = Cesium.Matrix4.multiplyByPoint( + inverseMatrix, endPoint, new Cesium.Cartesian3()); + const heightFactor = Math.abs(localEnd.z) / 1000; // 高度差补偿 + + // 4. 使用四象限反正切计算角度 + const angle = Math.atan2(localEnd.y, localEnd.x); + const adjustedAngle = angle - (heightFactor * 0.01); // 高度补偿 + let result = Cesium.Math.toDegrees(adjustedAngle) + return result; + } + calculateRoadAngle3(startPoint, endPoint) { + // 1. 获取地表法向量 + const normal = Cesium.Ellipsoid.WGS84.geodeticSurfaceNormal(startPoint); + + // 2. 构建精确ENU坐标系 + const enuMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(startPoint, undefined, normal); + const inverseMatrix = Cesium.Matrix4.inverse(enuMatrix, new Cesium.Matrix4()); + + // 3. 转换终点并计算水平向量 + const localEnd = Cesium.Matrix4.multiplyByPoint(inverseMatrix, endPoint, new Cesium.Cartesian3()); + const horizontalVec = new Cesium.Cartesian2(localEnd.x, localEnd.y); + Cesium.Cartesian2.normalize(horizontalVec, horizontalVec); + + const north = new Cesium.Cartesian2(1, 0); + + const angle = Cesium.Cartesian2.angleBetween(north, horizontalVec); + const cross = Cesium.Cartesian2.cross(north, horizontalVec, new Cesium.Cartesian2()); + return cross < 0 ? angle : -angle; + } + calculateRoadAngle(startPoint, endPoint) { + // 1. 获取精确地形法向量(需配合地形服务使用) + const normal = Cesium.Ellipsoid.WGS84.geodeticSurfaceNormal(startPoint); + + // 2. 构建ENU坐标系(增加异常处理) + if (!Cesium.defined(startPoint) || !Cesium.defined(endPoint)) { + return 0; + } + + const enuMatrix = Cesium.Transforms.eastNorthUpToFixedFrame( + startPoint, undefined, normal); + const inverseMatrix = Cesium.Matrix4.inverse( + enuMatrix, new Cesium.Matrix4()); + + // 3. 三维向量转换计算 + const localEnd = Cesium.Matrix4.multiplyByPoint( + inverseMatrix, endPoint, new Cesium.Cartesian3()); + + // 4. 使用三维向量计算角度(保留高程信息) + const north3D = new Cesium.Cartesian3(1, 0, 0); + const direction3D = new Cesium.Cartesian3( + localEnd.x, localEnd.y, localEnd.z); + Cesium.Cartesian3.normalize(direction3D, direction3D); + + // 5. 计算带符号角度(考虑三维空间关系) + const angle = Cesium.Cartesian3.angleBetween( + north3D, direction3D); + const cross = Cesium.Cartesian3.cross( + north3D, direction3D, new Cesium.Cartesian3()); + + // 6. 返回带符号角度(Z轴分量为负则取反) + return cross.z < 0 ? angle : -angle; + } + calculatePolygonOrientation(positions) { + + // 假设 position 是 Cesium.Cartesian3 对象,表示地球上的某个点 + var position = positions[0] + // 获取东、北、上坐标系 + var eastNorthUp = Cesium.Transforms.eastNorthUpToFixedFrame(position); + // northAxis 是北方向向量 + var northAxis = eastNorthUp.getColumn(1, new Cesium.Cartesian3()); + Cesium.Cartesian3.normalize(northAxis, northAxis); + + const direction = Cesium.Cartesian3.subtract(positions[0], positions[1], new Cesium.Cartesian3()); + Cesium.Cartesian3.normalize(direction, direction); + + + const dot = Cesium.Cartesian3.dot(northAxis, direction); + const magA = Cesium.Cartesian3.magnitude(northAxis); + const magB = Cesium.Cartesian3.magnitude(direction); + return Math.acos(dot / (magA * magB)); + } + calculateTextureRepeat(polygonPositions, textureSize, meterPerPixel = 0.01) { + // 验证纹理尺寸 + if (!textureSize.width || !textureSize.height) { + throw new Error('Texture size must contain width and height in pixels'); + } + + let widthDis = Cesium.Cartesian3.distance(polygonPositions[0], polygonPositions[1]) + let heightDis = Cesium.Cartesian3.distance(polygonPositions[0], polygonPositions[3]) + + // 计算纹理实际尺寸(米) + // const textureWidthMeters = textureSize.width * meterPerPixel; + // const textureHeightMeters = textureSize.height * meterPerPixel; + const textureWidthMeters = textureSize.width; + const textureHeightMeters = textureSize.height; + console.log(textureWidthMeters, textureHeightMeters, widthDis, heightDis, '宽度') + + // 保持宽高比计算重复次数 + const repeatX = widthDis / textureWidthMeters; + const repeatY = heightDis / textureHeightMeters; + const aspectRatio = textureWidthMeters / textureHeightMeters; + console.log(aspectRatio, 'aspectRatio') + + // 选择主导轴并保持比例 + const dominantRepeat = Math.max(repeatX, repeatY); + let x = Math.max(1, Math.ceil(widthDis / (aspectRatio * heightDis))) + console.log(x, '10') + return new Cesium.Cartesian2( + x, + 1 + ); + } + calculateTextureRepeat2(polygonPositions, textureSize, meterPerPixel = 0.01) { + // 验证纹理尺寸 + if (!textureSize.width || !textureSize.height) { + throw new Error('Texture size must contain width and height in pixels'); + } + + // 创建多边形几何体 + const geometry = Cesium.PolygonGeometry.createGeometry( + new Cesium.PolygonGeometry({ + polygonHierarchy: new Cesium.PolygonHierarchy(polygonPositions), + vertexFormat: Cesium.VertexFormat.POSITION_ONLY + }) + ); + + // 计算多边形面积(平方米) + let area = 0; + const indices = geometry.indices; + const positions = geometry.attributes.position.values; + for (let i = 0; i < indices.length; i += 3) { + const i0 = indices[i] * 3; + const i1 = indices[i + 1] * 3; + const i2 = indices[i + 2] * 3; + + const p0 = new Cesium.Cartesian3(positions[i0], positions[i0 + 1], positions[i0 + 2]); + const p1 = new Cesium.Cartesian3(positions[i1], positions[i1 + 1], positions[i1 + 2]); + const p2 = new Cesium.Cartesian3(positions[i2], positions[i2 + 1], positions[i2 + 2]); + + const cross = Cesium.Cartesian3.cross( + Cesium.Cartesian3.subtract(p1, p0, new Cesium.Cartesian3()), + Cesium.Cartesian3.subtract(p2, p0, new Cesium.Cartesian3()), + new Cesium.Cartesian3() + ); + area += Cesium.Cartesian3.magnitude(cross) * 0.5; + } + + // 将像素尺寸转换为实际尺寸(平方米) + const textureWidthMeters = textureSize.width * meterPerPixel; + const textureHeightMeters = textureSize.height * meterPerPixel; + const textureArea = textureWidthMeters * textureHeightMeters; + + // 计算各轴向重复次数 + const repeatX = Math.sqrt(area) / textureWidthMeters; + const repeatY = Math.sqrt(area) / textureHeightMeters; + + return new Cesium.Cartesian2(Math.max(1, Math.ceil(repeatX)), 1.0); + } + swapLastElements(arr1, arr2) { + const last = arr1[arr1.length - 1] + const first = arr2[0] + arr1[arr1.length - 1] = first + arr2[0] = last + + return [arr1, arr2]; + } + createLineBufferPolygonSide(positions, width) { + let area = [] + for (let i = 0; i < positions.length; i++) { + const posi = positions[i]; + + const dir = Cesium.Cartesian3.subtract(posi[1], posi[0], new Cesium.Cartesian3()); + Cesium.Cartesian3.normalize(dir, dir); + + // 获取垂直向量(基于Z轴) + const perp = Cesium.Cartesian3.cross(dir, Cesium.Cartesian3.UNIT_Z, new Cesium.Cartesian3()); + Cesium.Cartesian3.normalize(perp, perp); + + // 生成偏移向量 + const offset = Cesium.Cartesian3.multiplyByScalar(perp, width, new Cesium.Cartesian3()); + let point1 = Cesium.Cartesian3.add(posi[0], offset, new Cesium.Cartesian3()) + let point3 = Cesium.Cartesian3.add(posi[1], offset, new Cesium.Cartesian3()) + + // i == positions.length - 2 ? area.push(start, point1, end, point3) : area.push(start, point1) + area.push([posi[0], point1, point3, posi[1]]) + } + // let arr = [] + // for (let i = 0; i < area.length - 2; i += 2) { + // arr.push([area[i], area[i + 1], area[i + 3], area[i + 2]]) + // } + return area + } + createLineBufferPolygon2(positions, width) { + let area = [] + let leftPositions = []; + let rightPositions = []; + + for (let i = 0; i < positions.length - 1; i++) { + const start = positions[i]; + // const end = positions[i + 1] || positions[i - 1]; + const end = positions[i + 1]; + + const dir = Cesium.Cartesian3.subtract(end, start, new Cesium.Cartesian3()); + Cesium.Cartesian3.normalize(dir, dir); + + // 获取垂直向量(基于Z轴) + const perp = Cesium.Cartesian3.cross(dir, Cesium.Cartesian3.UNIT_Z, new Cesium.Cartesian3()); + Cesium.Cartesian3.normalize(perp, perp); + + const dir2 = Cesium.Cartesian3.subtract(start, end, new Cesium.Cartesian3()); + Cesium.Cartesian3.normalize(dir2, dir2); + + // 获取垂直向量(基于Z轴) + const perp2 = Cesium.Cartesian3.cross(dir2, Cesium.Cartesian3.UNIT_Z, new Cesium.Cartesian3()); + Cesium.Cartesian3.normalize(perp2, perp2); + + + + + // 生成偏移向量 + const offset = Cesium.Cartesian3.multiplyByScalar(perp, width, new Cesium.Cartesian3()); + const offset2 = Cesium.Cartesian3.multiplyByScalar(perp, -width, new Cesium.Cartesian3()); + + const offsetEnd = Cesium.Cartesian3.multiplyByScalar(perp2, -width, new Cesium.Cartesian3()); + const offsetEnd2 = Cesium.Cartesian3.multiplyByScalar(perp2, width, new Cesium.Cartesian3()); + + let point1 = Cesium.Cartesian3.add(start, offset, new Cesium.Cartesian3()) + let point2 = Cesium.Cartesian3.add(start, offset2, new Cesium.Cartesian3()) + let point3 = Cesium.Cartesian3.add(end, offsetEnd, new Cesium.Cartesian3()) + let point4 = Cesium.Cartesian3.add(end, offsetEnd2, new Cesium.Cartesian3()) + + area.push([point1, point3, point4, point2]) + + rightPositions.push([point1, point3]) + leftPositions.push([point2, point4]) + + // if (i == positions.length - 2) { + // area.push(point1, point2, point3, point4) + // rightPositions.push(point1) + // leftPositions.push(point2) + // leftPositions.push(point4) + // rightPositions.push(point3) + // } else { + // area.push(point1, point2) + // rightPositions.push(point1) + // leftPositions.push(point2) + // } + } + // let arr = [] + // for (let i = 0; i < area.length - 2; i += 2) { + // arr.push([area[i], area[i + 1], area[i + 3], area[i + 2]]) + // } + let that = this + // return [arr, rightPositions, leftPositions] + return [area, rightPositions, leftPositions] + } + getIntersects2(segment1Start, segment1End, segment2Start, segment2End) { + // 构造平面(使用线段2的起点和方向向量) + let direction = Cesium.Cartesian3.subtract(segment2End, segment2Start, new Cesium.Cartesian3()); + let plane = Cesium.Plane.fromPointNormal(segment2Start, direction); + + // 计算线段1与平面的交点(增加容差参数) + let intersection = Cesium.IntersectionTests.lineSegmentPlane( + segment1Start, segment1End, plane, 1e-10 + ); + if (!intersection) return null; + + // 使用参数方程验证交点有效性 + let vecToIntersect = Cesium.Cartesian3.subtract(intersection, segment2Start, new Cesium.Cartesian3()); + let t = Cesium.Cartesian3.dot(vecToIntersect, direction) / + Cesium.Cartesian3.magnitudeSquared(direction); + + let result = (t >= -1e-10 && t <= 1.0 + 1e-10) ? intersection : null; + return result; + } + getIntersects(point1, point2, point3, point4) { + let carPoint1 = this.getLonLat(point1) + let carPoint2 = this.getLonLat(point2) + let carPoint3 = this.getLonLat(point3) + let carPoint4 = this.getLonLat(point4) + var line1 = turf.lineString([ + [carPoint1.lon, carPoint1.lat], + [carPoint2.lon, carPoint2.lat] + ]); + var line2 = turf.lineString([ + [carPoint3.lon, carPoint3.lat], + [carPoint4.lon, carPoint4.lat] + ]); + var intersects = turf.lineIntersect(line1, line2); + if (intersects.features.length > 0) { + return Cesium.Cartesian3.fromDegrees(intersects.features[0].geometry.coordinates[0], intersects.features[0].geometry.coordinates[1]) + } + } + getLonLat(point) { + let pointDe = Cesium.Cartographic.fromCartesian(point) + const longitude = Cesium.Math.toDegrees(pointDe.longitude); + const latitude = Cesium.Math.toDegrees(pointDe.latitude); + return { lon: longitude, lat: latitude } + + } + createLineBufferPolygon(viewer, positions, width) { + // 计算每个线段的左右偏移点 + const leftPositions = []; + const rightPositions = []; + + for (let i = 0; i < positions.length; i++) { + const start = positions[i]; + const end = positions[i + 1] || positions[i - 1]; + + // 计算线段方向向量 + const direction = Cesium.Cartesian3.subtract(end, start, new Cesium.Cartesian3()); + // const direction = Cesium.Cartesian3.subtract(start, end, new Cesium.Cartesian3()); + Cesium.Cartesian3.normalize(direction, direction); + + // 计算垂直向量(使用上向量叉积) + const up = Cesium.Cartesian3.UNIT_Z; + const perpendicular = Cesium.Cartesian3.cross(direction, up, new Cesium.Cartesian3()); + Cesium.Cartesian3.normalize(perpendicular, perpendicular); + + // 计算左右偏移点 + const leftOffset = Cesium.Cartesian3.multiplyByScalar( + perpendicular, + width, + new Cesium.Cartesian3() + ); + + if (width > 0) { + rightPositions.unshift(Cesium.Cartesian3.add(start, leftOffset, new Cesium.Cartesian3())); + } else if (width < 0) { + rightPositions.push(Cesium.Cartesian3.add(start, leftOffset, new Cesium.Cartesian3())); + } + + } + return rightPositions + } + //计算角度 + calculateAangle(arr) { + // let fromDegreesArray = that.calSector(that.options.center, that.options.radius, that.options.startAngle, that.options.endAngle, 360, true) + + function getAangle(start, end) { + let rad = Math.PI / 180, + lat1 = start.y * rad, + lat2 = end.y * rad, + lon1 = start.x * rad, + lon2 = end.x * rad; + const a = Math.sin(lon2 - lon1) * Math.cos(lat2); + const b = + Math.cos(lat1) * Math.sin(lat2) - + Math.sin(lat1) * Math.cos(lat2) * Math.cos(lon2 - lon1); + const radians = Math.atan2(a, b) + const degrees = radians % (2 * Math.PI); + let bearing = 450 - ((degrees * 180) / Math.PI < 0 + ? 360 + (degrees * 180) / Math.PI + : (degrees * 180) / Math.PI) - 90; + return 360 - (bearing % 360) + } + + let center = arr[0] + let pos84_1 = arr[1] + let pos84_2 = arr[2] + + let start = { x: center.lng, y: center.lat } + let end1 = { x: pos84_1.lng, y: pos84_1.lat } + let end2 = { x: pos84_2.lng, y: pos84_2.lat } + + let angle1 = getAangle(start, end1) + let angle2 = getAangle(start, end2) + + return { + angle1, + angle2 + } + } + + get carRoadWidth() { + return this.options.carRoadWidth + } + + set carRoadWidth(v) { + this.options.carRoadWidth = v + Road.create(this) + + } + get sideWidth() { + return this.options.sideWidth + } + set sideWidth(v) { + this.options.sideWidth = v + Road.create(this) + } + /** + * @description 编辑框 + * @param state=false {boolean} 状态: true打开, false关闭 + */ + async edit(state = false) { + let _this = this + this.originalOptions = this.deepCopyObj(this.options) + + if (this._DialogObject && this._DialogObject.close) { + this._DialogObject.close() + this._DialogObject = null + } + + if (state) { + this._DialogObject = await new Dialog(this.sdk, this.originalOptions, { + title: '道路属性', left: '180px', top: '100px', + confirmCallBack: (options) => { + this.name = this.name.trim() + if (!this.name) { + this.name = '道路' + } + this.originalOptions = this.deepCopyObj(this.options) + this._DialogObject.close() + this.Dialog.confirmCallBack && this.Dialog.confirmCallBack(this.originalOptions) + syncData(this.sdk, this.options.id) + syncSplitData(this.sdk, this.options.id) + }, + resetCallBack: () => { + this.reset() + this.Dialog.resetCallBack && this.Dialog.resetCallBack() + }, + closeCallBack: () => { + this.reset() + this.Dialog.closeCallBack && this.Dialog.closeCallBack() + }, + showCallBack: (show) => { + this.show = show + this.Dialog.showCallBack && this.Dialog.showCallBack() + } + }, true) + this._DialogObject._element.body.className = this._DialogObject._element.body.className + ' road-surface' + let contentElm = document.createElement('div'); + contentElm.innerHTML = html() + this._DialogObject.contentAppChild(contentElm) + + + // 下拉选项 + // let heightModeData = [ + // { + // name: '海拔高度', + // value: '海拔高度', + // key: '0', + // }, + // { + // name: '相对地表', + // value: '相对地表', + // key: '1', + // }, + // { + // name: '依附模型', + // value: '依附模型', + // key: '2', + // } + // ] + // let heightModeObject = legp( + // this._DialogObject._element.content.getElementsByClassName( + // 'road-box' + // )[0], + // '.road-type' + // ) + // if (heightModeObject) { + // heightModeObject.legp_search(heightModeData) + // let heightModeDataLegpElm = this._DialogObject._element.content + // .getElementsByClassName('road-type')[0] + // .getElementsByTagName('input')[0] + // for (let i = 0; i < heightModeData.length; i++) { + // if (heightModeData[i].key == this.heightMode) { + // heightModeDataLegpElm.value = heightModeData[i].value + // heightModeObject.legp_searchActive( + // heightModeData[i].value + // ) + // break + // } + // } + // heightModeDataLegpElm.addEventListener('input', () => { + // for (let i = 0; i < heightModeData.length; i++) { + // if (heightModeData[i].value === heightModeDataLegpElm.value) { + // this.heightMode = heightModeData[i].key + // break + // } + // } + // }) + + + // this._elms.height = heightElm + // this._elms.heightBox = heightBoxElm + // this._elms.heightMode = heightModeDataLegpElm + // this._elms.heightConfirm = heightConfirmElm + // this._elms.heightModeObject = heightModeObject + + // heightConfirmElm.addEventListener('click', () => { + // this.positionEditing = false + // for (let i = 0; i < this.options.positions.length; i++) { + // this.options.positions[i].alt = Number((this.options.positions[i].alt + Number(heightElm.value)).toFixed(2)) + // this._elms.alt[i].innerHTML = this.options.positions[i].alt + // } + // let fromDegreesArray = this.renewPositions(this.options.positions) + // this.entity.polyline.positions = Cesium.Cartesian3.fromDegreesArrayHeights( + // fromDegreesArray + // ) + + // this.positionEditing = false + // PolylineObject.closeNodeEdit(this) + // }) + // } + + + + + + let all_elm = contentElm.getElementsByTagName("*") + this._EventBinding.on(this, all_elm) + this._elms = this._EventBinding.element + } else { + // if (this._element_style) { + // document.getElementsByTagName('head')[0].removeChild(this._element_style) + // this._element_style = null + // } + // if (this._DialogObject && this._DialogObject.remove) { + // this._DialogObject.remove() + // this._DialogObject = null + // } + } + } + + reset() { + if (!this.viewer.entities.getById(this.options.id)) { + return + } + this.name = this.originalOptions.name + this.carRoadWidth = this.originalOptions.carRoadWidth + this.sideWidth = this.originalOptions.sideWidth + this.positions = this.originalOptions.positions + this.roadImage = this.originalOptions.roadImage + this.sideImage = this.originalOptions.sideImage + } + + /** + * 飞到对应实体 + */ + async flyTo(options = {}) { + setActiveViewer(0) + closeRotateAround(this.sdk) + closeViewFollow(this.sdk) + + if (this.options.customView && this.options.customView.relativePosition && this.options.customView.orientation) { + let orientation = { + heading: Cesium.Math.toRadians(this.options.customView.orientation.heading || 0.0), + pitch: Cesium.Math.toRadians(this.options.customView.orientation.pitch || -60.0), + roll: Cesium.Math.toRadians(this.options.customView.orientation.roll || 0.0) + } + + let lng = this.options.customView.relativePosition.lng + let lat = this.options.customView.relativePosition.lat + let alt = this.options.customView.relativePosition.alt + let destination = Cesium.Cartesian3.fromDegrees(lng, lat, alt) + + let position = { lng: 0, lat: 0 } + if (this.options.position) { + position = { ...this.options.position } + } + else if (this.options.positions) { + position = { ...this.options.positions[0] } + } + else if (this.options.center) { + position = { ...this.options.center } + } + else if (this.options.start) { + position = { ...this.options.start } + } + else { + if (this.options.hasOwnProperty('lng')) { + position.lng = this.options.lng + } + if (this.options.hasOwnProperty('lat')) { + position.lat = this.options.lat + } + if (this.options.hasOwnProperty('alt')) { + position.alt = this.options.alt + } + } + // 如果没有高度值,则获取紧贴高度计算 + // if (!position.hasOwnProperty('alt')) { + // position.alt = await this.getClampToHeight(position) + // } + lng = this.options.customView.relativePosition.lng + position.lng + lat = this.options.customView.relativePosition.lat + position.lat + alt = this.options.customView.relativePosition.alt + position.alt + destination = Cesium.Cartesian3.fromDegrees(lng, lat, alt) + this.sdk.viewer.camera.flyTo({ + destination: destination, + orientation: orientation + }) + } + else { + let positionArray = [] + for (let i = 0; i < this.positions.length; i++) { + let a = Cesium.Cartesian3.fromDegrees( + this.positions[i][0], + this.positions[i][1], + this.options.height + this.options.heightDifference / 2 + ) + positionArray.push(a.x, a.y, a.z) + } + let BoundingSphere = Cesium.BoundingSphere.fromVertices(positionArray) + this.viewer.camera.flyToBoundingSphere(BoundingSphere, { + offset: { + heading: Cesium.Math.toRadians(0.0), + pitch: Cesium.Math.toRadians(-20.0), + roll: Cesium.Math.toRadians(0.0) + } + }) + + } + } + + getSphere() { + return new Promise((resolve) => { + // entity没有加载完成时 state 不会等于0 所以设置定时器直到获取到为止 + const interval = setInterval(() => { + const sphere = new Cesium.BoundingSphere() + const state = this.sdk.viewer._dataSourceDisplay.getBoundingSphere( + this.viewer.entities.getById(this.options.id), + false, + sphere + ) + if (state === Cesium.BoundingSphereState.DONE) { + clearInterval(interval) + } + }, 1000) + }) + } + + /** + * 删除 + */ + async remove() { + this.positions = [] + this.lineEntity = null + + if (this.viewer.entities.getById(this.options.id)) { + this.viewer.entities.getById(this.options.id)._children.forEach((item) => { + this.viewer.entities.remove(item); + }); + this.viewer.entities.remove(this.viewer.entities.getById(this.options.id)) + } + + if (this._DialogObject && !this._DialogObject.isDestroy) { + this._DialogObject.close() + this._DialogObject = null + } + await this.sdk.removeIncetance(this.options.id) + await syncData(this.sdk, this.options.id) + } + + flicker() { } +} + +export default Road diff --git a/src/Obj/Base/RoadObject/index-拐角连接.js b/src/Obj/Base/RoadObject/index-拐角连接.js new file mode 100644 index 0000000..9fb0c6c --- /dev/null +++ b/src/Obj/Base/RoadObject/index-拐角连接.js @@ -0,0 +1,889 @@ +/** + * @description 道路 + */ +import Dialog from '../../Element/Dialog'; +import { html } from "./_element"; +import EventBinding from '../../Element/Dialog/eventBinding'; +import Base from "../index"; +import { syncData } from '../../../Global/MultiViewportMode' +import { setSplitDirection, syncSplitData, setActiveId } from '../../../Global/SplitScreen' +import { setActiveViewer, closeRotateAround, closeViewFollow } from '../../../Global/global' + +class Road extends Base { + /** + * @constructor + * @param sdk + * @description 道路 + * @param options {object} 道路属性 + * @param options.name=未命名对象 {string} 名称 + * @param options.carRoadWidth=2 {number} 车道宽度 + * @param options.sideWidth=2 {number} 人行道宽度 + * @param options.positions=[] {array} 道路positions + * @param options.roadImage='' {string} 车道贴图 + * @param options.sideImage='' {string} 人行道贴图 + * @param Dialog {object} 弹框对象 + * @param Dialog.confirmCallBack {function} 弹框确认时的回调 + * */ + constructor(sdk, options = {}, _Dialog = {}) { + super(sdk, options); + this.viewer = this.sdk.viewer + this.options.name = options.name || '道路' + this.options.carRoadWidth = options.carRoadWidth || 10 + this.options.sideWidth = options.sideWidth || 5 + this.options.positions = options.positions || [] + this.options.roadImage = options.roadImage || (this.getSourceRootPath() + '/img/roadPhoto.png') + this.options.sideImage = options.sideImage || (this.getSourceRootPath() + '/img/sidePhoto.png') + this.options.show = (options.show || options.show === false) ? options.show : true + this.Dialog = _Dialog + this._EventBinding = new EventBinding() + this._elms = {}; + this.positionArea = [] + this.positions = [] + this.lineEntity = '' + + this.sdk.addIncetance(this.options.id, this) + Road.create(this) + } + + // 创建道路 + static create(that) { + let positions = [] + that.options.positions.forEach(v => { + positions.push(new Cesium.Cartesian3.fromDegrees(v.lng, v.lat, v.alt)) + }) + + let area = [[], [], []] + area[1] = that.createLineBufferPolygon2(positions, that.options.carRoadWidth / 2) + area[0] = that.createLineBufferPolygonSide(area[1][2], -that.options.sideWidth) + area[2] = that.createLineBufferPolygonSide(area[1][1], that.options.sideWidth) + + //判断道路边是否相交 + for (let i = 0; i < area[0].length - 1; i++) { + + let leftItem = area[0][i] + let leftItem2 = area[0][i + 1] + let rightItem = area[2][i] + let rightItem2 = area[2][i + 1] + let carItem = area[1][0][i] + let carItem2 = area[1][0][i + 1] + let leftLine = that.getIntersects(leftItem[1], leftItem[2], leftItem2[1], leftItem2[2]) + let rightLine = that.getIntersects(rightItem[1], rightItem[2], rightItem2[1], rightItem2[2]) + + console.log(leftLine, 'leftLine') + if (leftLine) {//左侧相交 + //获取右侧延长交点 + let point1 = that.getExtendPoint(rightItem[1], rightItem[2], 1000) + let point2 = that.getExtendPoint(rightItem2[2], rightItem2[1], 1000) + let rightIntersection = that.getIntersects(rightItem[2], point1, rightItem2[1], point2) + //将其他几条边都延长 + let leftLineNeiPoint = that.getExtendPoint(leftItem[0], leftItem[3], 1000) + let carLeftPoint = that.getExtendPoint(carItem[3], carItem[2], 1000) + let carRightPoint = that.getExtendPoint(carItem[0], carItem[1], 1000) + let rightLineNeiPoint = that.getExtendPoint(rightItem[0], rightItem[3], 1000) + + + + + //跟左侧里相交点 + let leftLineNei = that.getIntersects(leftLine, rightIntersection, leftItem[0], leftLineNeiPoint) + //跟车道左侧相交点 + let carLeft = that.getIntersects(leftLine, rightIntersection, carItem[3], carLeftPoint) + //跟车道右侧相交点 + let carRight = that.getIntersects(leftLine, rightIntersection, carItem[0], carRightPoint) + let rightLineNei = that.getIntersects(leftLine, rightIntersection, rightItem[0], rightLineNeiPoint) + + // let leftLineNei = that.getIntersects(leftLine, rightItem[2], leftItem[0], leftItem[3]) + // let carLeft = that.getIntersects(leftLine, rightItem[2], carItem[3], carItem[2]) + // let carRight = that.getIntersects(leftLine, rightItem[2], carItem[0], carItem[1]) + // let rightLineNei = that.getIntersects(leftLine, rightItem[2], rightItem[0], rightItem[3]) + + // let leftLineNei = that.getIntersects(leftLine, intersection, leftItem[0], leftItem[3]) + // //跟车道左侧相交点 + // let carLeft = that.getIntersects(leftLine, intersection, carItem[3], carItem[2]) + // //跟车道右侧相交点 + // let carRight = that.getIntersects(leftLine, intersection, carItem[0], carItem[1]) + // let rightLineNei = that.getIntersects(leftLine, intersection, rightItem[0], rightItem[3]) + + leftItem[2] = leftLine + leftItem[3] = leftLineNei + carItem[2] = carLeft + carItem[1] = carRight + rightItem[3] = rightLineNei + rightItem[2] = rightIntersection + console.log(leftItem, carItem, rightItem, 'leftItemleft') + + + //将其他几条边都延长 + let leftLineNeiPoint2 = that.getExtendPoint(leftItem2[3], leftItem2[0], 1000) + let carLeftPoint2 = that.getExtendPoint(carItem2[2], carItem2[3], 1000) + let carRightPoint2 = that.getExtendPoint(carItem2[1], carItem2[0], 1000) + let rightLineNeiPoint2 = that.getExtendPoint(rightItem2[3], rightItem2[0], 1000) + + // let leftLineNei2 = that.getIntersects(leftLine, rightItem2[1], leftItem2[0], leftItem2[3]) + // let carLeft2 = that.getIntersects(leftLine, rightItem2[1], carItem2[3], carItem2[2]) + // let carRight2 = that.getIntersects(leftLine, rightItem2[1], carItem2[0], carItem2[1]) + // let rightLineNei2 = that.getIntersects(leftLine, rightItem2[1], rightItem2[0], rightItem2[3]) + + + + let leftLineNei2 = that.getIntersects(leftLine, rightIntersection, leftItem2[3], leftLineNeiPoint2) + let carLeft2 = that.getIntersects(leftLine, rightIntersection, carItem2[2], carLeftPoint2) + let carRight2 = that.getIntersects(leftLine, rightIntersection, carItem2[1], carRightPoint2) + let rightLineNei2 = that.getIntersects(leftLine, rightIntersection, rightItem2[3], rightLineNeiPoint2) + + // let arr = [leftLine, rightIntersection, leftItem2[3], leftLineNeiPoint2] + // arr.forEach((item, index) => { + // that.sdk.viewer.entities.add({ + // name: 'node-secondary-edit-point', + // index: i, + // position: item, + // billboard: { + // image: that.getSourceRootPath() + '/img/point.png', + // width: 15, + // height: 15, + // disableDepthTestDistance: Number.POSITIVE_INFINITY, + // color: Cesium.Color.WHITE.withAlpha(0.99) + // }, + // label: { + // text: '' + index, + // pixelOffset: { x: 0, y: -20 }, + // }, + // }) + // }) + + // let leftLineNei2 = that.getIntersects(leftLine, intersection, leftItem2[0], leftItem2[3]) + // //跟车道左侧相交点 + // let carLeft2 = that.getIntersects(leftLine, intersection, carItem2[3], carItem2[2]) + // //跟车道右侧相交点 + // let carRight2 = that.getIntersects(leftLine, intersection, carItem2[0], carItem2[1]) + // let rightLineNei2 = that.getIntersects(leftLine, intersection, rightItem2[0], rightItem2[3]) + + leftItem2[1] = leftLine + leftItem2[0] = leftLineNei2 + carItem2[3] = carLeft2 + carItem2[0] = carRight2 + rightItem2[0] = rightLineNei2 + rightItem2[1] = rightIntersection + console.log(leftItem2, carItem2, rightItem2, 'leftItem2left') + + } else if (rightLine) {//右侧相交 + + //获取左侧延长交点 + let point1 = that.getExtendPoint(leftItem[1], leftItem[2], 1000) + let point2 = that.getExtendPoint(leftItem2[2], leftItem2[1], 1000) + let rightIntersection = that.getIntersects(leftItem[2], point1, leftItem2[1], point2) + //将其他几条边都延长 + let leftLineNeiPoint = that.getExtendPoint(leftItem[0], leftItem[3], 1000) + let carLeftPoint = that.getExtendPoint(carItem[3], carItem[2], 1000) + let carRightPoint = that.getExtendPoint(carItem[0], carItem[1], 1000) + let rightLineNeiPoint = that.getExtendPoint(rightItem[0], rightItem[3], 1000) + + // //跟左侧里相交点 + let leftLineNei = that.getIntersects(rightLine, rightIntersection, leftItem[0], leftLineNeiPoint) + //跟车道左侧相交点 + let carLeft = that.getIntersects(rightLine, rightIntersection, carItem[3], carLeftPoint) + //跟车道右侧相交点 + let carRight = that.getIntersects(rightLine, rightIntersection, carItem[0], carRightPoint) + let rightLineNei = that.getIntersects(rightLine, rightIntersection, rightItem[0], rightLineNeiPoint) + // //跟左侧里相交点 + // let leftLineNei = that.getIntersects(rightLine, leftItem[2], leftItem[0], leftItem[3]) + // //跟车道左侧相交点 + // let carLeft = that.getIntersects(rightLine, leftItem[2], carItem[3], carItem[2]) + // //跟车道右侧相交点 + // let carRight = that.getIntersects(rightLine, leftItem[2], carItem[0], carItem[1]) + // let rightLineNei = that.getIntersects(rightLine, leftItem[2], rightItem[0], rightItem[3]) + + + leftItem[2] = rightIntersection + leftItem[3] = leftLineNei + carItem[2] = carLeft + carItem[1] = carRight + rightItem[3] = rightLineNei + rightItem[2] = rightLine + console.log(leftItem, carItem, rightItem, 'leftItemright') + + //将其他几条边都延长 + let leftLineNeiPoint2 = that.getExtendPoint(leftItem2[3], leftItem2[0], 1000) + let carLeftPoint2 = that.getExtendPoint(carItem2[2], carItem2[3], 1000) + let carRightPoint2 = that.getExtendPoint(carItem2[1], carItem2[0], 1000) + let rightLineNeiPoint2 = that.getExtendPoint(rightItem2[3], rightItem2[0], 1000) + + let leftLineNei2 = that.getIntersects(rightLine, rightIntersection, leftItem2[3], leftLineNeiPoint2) + //跟车道左侧相交点 + let carLeft2 = that.getIntersects(rightLine, rightIntersection, carItem2[2], carLeftPoint2) + //跟车道右侧相交点 + let carRight2 = that.getIntersects(rightLine, rightIntersection, carItem2[1], carRightPoint2) + let rightLineNei2 = that.getIntersects(rightLine, rightIntersection, rightItem2[3], rightLineNeiPoint2) + // let leftLineNei2 = that.getIntersects(rightLine, leftItem2[1], leftItem2[0], leftItem2[3]) + // //跟车道左侧相交点 + // let carLeft2 = that.getIntersects(rightLine, leftItem2[1], carItem2[3], carItem2[2]) + // //跟车道右侧相交点 + // let carRight2 = that.getIntersects(rightLine, leftItem2[1], carItem2[0], carItem2[1]) + // let rightLineNei2 = that.getIntersects(rightLine, leftItem2[1], rightItem2[0], rightItem2[3]) + + leftItem2[1] = rightIntersection + leftItem2[0] = leftLineNei2 + carItem2[3] = carLeft2 + carItem2[0] = carRight2 + rightItem2[0] = rightLineNei2 + rightItem2[1] = rightLine + console.log(leftItem2, carItem2, rightItem2, 'leftItem2right') + + } + } + + + console.log(area[0], 'area') + + if (that.viewer.entities.getById(that.options.id)) { + that.viewer.entities.getById(that.options.id)._children.forEach((item) => { + that.viewer.entities.remove(item); + }); + that.viewer.entities.remove(that.viewer.entities.getById(that.options.id)) + } + that.lineEntity = that.viewer.entities.add(new Cesium.Entity({ id: that.options.id, show: that.options.show })) + + const myImg = new Image() + myImg.src = that.options.roadImage + myImg.onload = function () { + console.log(area[1][0], 'arr') + area[1][0].forEach((item, index) => { + that.viewer.entities.add({ + // id: that.options.id, + parent: that.lineEntity, + polygon: { + hierarchy: new Cesium.PolygonHierarchy(item), + material: new Cesium.ImageMaterialProperty({ + image: that.options.roadImage, + transparent: true,// 如果图片有透明部分,需要设置为 true + repeat: that.calculateTextureRepeat(item, myImg) + }), + stRotation: that.calculateRoadAngle(positions[index], positions[index + 1]) + } + }); + }) + } + + const myImg2 = new Image() + myImg2.src = that.options.sideImage + myImg2.onload = function () { + area[0].forEach((item, index) => { + that.viewer.entities.add({ + parent: that.lineEntity, + polygon: { + hierarchy: new Cesium.PolygonHierarchy(item), + material: new Cesium.ImageMaterialProperty({ + image: that.options.sideImage, + transparent: true,// 如果图片有透明部分,需要设置为 true + repeat: that.calculateTextureRepeat(item, myImg2) + }), + stRotation: that.calculateRoadAngle(positions[index], positions[index + 1]) + } + }); + }) + + area[2].forEach((item, index) => { + that.viewer.entities.add({ + polygon: { + hierarchy: new Cesium.PolygonHierarchy(item), + material: new Cesium.ImageMaterialProperty({ + image: that.options.sideImage, + transparent: true,// 如果图片有透明部分,需要设置为 true + repeat: that.calculateTextureRepeat(item, myImg2) + }), + stRotation: that.calculateRoadAngle(positions[index], positions[index + 1]) + } + }); + }) + + } + + } + getExtendPoint(position1, position2, distance) { + // let position1 = Cesium.Cartesian3.fromDegrees(p1[0], p1[1], 0); + // let position2 = Cesium.Cartesian3.fromDegrees(p2[0], p2[1], 0); + let pot = Cesium.Cartesian3.subtract(position2, position1, new Cesium.Cartesian3());//方向 + var dir = Cesium.Cartesian3.normalize(pot, new Cesium.Cartesian3());//向量归一化 + + var ray = new Cesium.Ray(position1, dir); + let np = Cesium.Ray.getPoint(ray, distance * 10);//计算延长点 + return np + } + getArr(arr1, arr2) { + arr2 = arr2.reverse() + let polygon = [] + for (let index = 0; index < arr1.length - 1; index++) { + polygon.push([arr1[index], arr1[index + 1], arr2[index + 1], arr2[index]]) + } + return polygon + } + + calculateRoadAngle(startPoint, endPoint) { + // 1. 获取地表法向量 + const normal = Cesium.Ellipsoid.WGS84.geodeticSurfaceNormal(startPoint); + + // 2. 构建精确ENU坐标系 + const enuMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(startPoint, undefined, normal); + const inverseMatrix = Cesium.Matrix4.inverse(enuMatrix, new Cesium.Matrix4()); + + // 3. 转换终点并计算水平向量 + const localEnd = Cesium.Matrix4.multiplyByPoint(inverseMatrix, endPoint, new Cesium.Cartesian3()); + const horizontalVec = new Cesium.Cartesian2(localEnd.x, localEnd.y); + Cesium.Cartesian2.normalize(horizontalVec, horizontalVec); + + const north = new Cesium.Cartesian2(1, 0); + + const angle = Cesium.Cartesian2.angleBetween(north, horizontalVec); + const cross = Cesium.Cartesian2.cross(north, horizontalVec, new Cesium.Cartesian2()); + return cross < 0 ? angle : -angle; + } + + calculatePolygonOrientation(positions) { + + // 假设 position 是 Cesium.Cartesian3 对象,表示地球上的某个点 + var position = positions[0] + // 获取东、北、上坐标系 + var eastNorthUp = Cesium.Transforms.eastNorthUpToFixedFrame(position); + // northAxis 是北方向向量 + var northAxis = eastNorthUp.getColumn(1, new Cesium.Cartesian3()); + Cesium.Cartesian3.normalize(northAxis, northAxis); + + const direction = Cesium.Cartesian3.subtract(positions[0], positions[1], new Cesium.Cartesian3()); + Cesium.Cartesian3.normalize(direction, direction); + + + const dot = Cesium.Cartesian3.dot(northAxis, direction); + const magA = Cesium.Cartesian3.magnitude(northAxis); + const magB = Cesium.Cartesian3.magnitude(direction); + return Math.acos(dot / (magA * magB)); + } + calculateTextureRepeat(polygonPositions, textureSize, meterPerPixel = 0.01) { + // 验证纹理尺寸 + if (!textureSize.width || !textureSize.height) { + throw new Error('Texture size must contain width and height in pixels'); + } + + // 创建多边形几何体 + const geometry = Cesium.PolygonGeometry.createGeometry( + new Cesium.PolygonGeometry({ + polygonHierarchy: new Cesium.PolygonHierarchy(polygonPositions), + vertexFormat: Cesium.VertexFormat.POSITION_ONLY + }) + ); + + // 计算多边形面积(平方米) + let area = 0; + const indices = geometry.indices; + const positions = geometry.attributes.position.values; + for (let i = 0; i < indices.length; i += 3) { + const i0 = indices[i] * 3; + const i1 = indices[i + 1] * 3; + const i2 = indices[i + 2] * 3; + + const p0 = new Cesium.Cartesian3(positions[i0], positions[i0 + 1], positions[i0 + 2]); + const p1 = new Cesium.Cartesian3(positions[i1], positions[i1 + 1], positions[i1 + 2]); + const p2 = new Cesium.Cartesian3(positions[i2], positions[i2 + 1], positions[i2 + 2]); + + const cross = Cesium.Cartesian3.cross( + Cesium.Cartesian3.subtract(p1, p0, new Cesium.Cartesian3()), + Cesium.Cartesian3.subtract(p2, p0, new Cesium.Cartesian3()), + new Cesium.Cartesian3() + ); + area += Cesium.Cartesian3.magnitude(cross) * 0.5; + } + + // 将像素尺寸转换为实际尺寸(平方米) + const textureWidthMeters = textureSize.width * meterPerPixel; + const textureHeightMeters = textureSize.height * meterPerPixel; + const textureArea = textureWidthMeters * textureHeightMeters; + + // 计算各轴向重复次数 + const repeatX = Math.sqrt(area) / textureWidthMeters; + const repeatY = Math.sqrt(area) / textureHeightMeters; + + return new Cesium.Cartesian2(Math.max(1, Math.ceil(repeatX)), 1.0); + } + swapLastElements(arr1, arr2) { + const last = arr1[arr1.length - 1] + const first = arr2[0] + arr1[arr1.length - 1] = first + arr2[0] = last + + return [arr1, arr2]; + } + createLineBufferPolygonSide(positions, width) { + let area = [] + for (let i = 0; i < positions.length; i++) { + const posi = positions[i]; + + const dir = Cesium.Cartesian3.subtract(posi[1], posi[0], new Cesium.Cartesian3()); + Cesium.Cartesian3.normalize(dir, dir); + + // 获取垂直向量(基于Z轴) + const perp = Cesium.Cartesian3.cross(dir, Cesium.Cartesian3.UNIT_Z, new Cesium.Cartesian3()); + Cesium.Cartesian3.normalize(perp, perp); + + // 生成偏移向量 + const offset = Cesium.Cartesian3.multiplyByScalar(perp, width, new Cesium.Cartesian3()); + let point1 = Cesium.Cartesian3.add(posi[0], offset, new Cesium.Cartesian3()) + let point3 = Cesium.Cartesian3.add(posi[1], offset, new Cesium.Cartesian3()) + + // i == positions.length - 2 ? area.push(start, point1, end, point3) : area.push(start, point1) + area.push([posi[0], point1, point3, posi[1]]) + } + // let arr = [] + // for (let i = 0; i < area.length - 2; i += 2) { + // arr.push([area[i], area[i + 1], area[i + 3], area[i + 2]]) + // } + return area + } + createLineBufferPolygon2(positions, width) { + let area = [] + let leftPositions = []; + let rightPositions = []; + + for (let i = 0; i < positions.length - 1; i++) { + const start = positions[i]; + // const end = positions[i + 1] || positions[i - 1]; + const end = positions[i + 1]; + + const dir = Cesium.Cartesian3.subtract(end, start, new Cesium.Cartesian3()); + Cesium.Cartesian3.normalize(dir, dir); + + // 获取垂直向量(基于Z轴) + const perp = Cesium.Cartesian3.cross(dir, Cesium.Cartesian3.UNIT_Z, new Cesium.Cartesian3()); + Cesium.Cartesian3.normalize(perp, perp); + + const dir2 = Cesium.Cartesian3.subtract(start, end, new Cesium.Cartesian3()); + Cesium.Cartesian3.normalize(dir2, dir2); + + // 获取垂直向量(基于Z轴) + const perp2 = Cesium.Cartesian3.cross(dir2, Cesium.Cartesian3.UNIT_Z, new Cesium.Cartesian3()); + Cesium.Cartesian3.normalize(perp2, perp2); + + + + + // 生成偏移向量 + const offset = Cesium.Cartesian3.multiplyByScalar(perp, width, new Cesium.Cartesian3()); + const offset2 = Cesium.Cartesian3.multiplyByScalar(perp, -width, new Cesium.Cartesian3()); + + const offsetEnd = Cesium.Cartesian3.multiplyByScalar(perp2, -width, new Cesium.Cartesian3()); + const offsetEnd2 = Cesium.Cartesian3.multiplyByScalar(perp2, width, new Cesium.Cartesian3()); + + let point1 = Cesium.Cartesian3.add(start, offset, new Cesium.Cartesian3()) + let point2 = Cesium.Cartesian3.add(start, offset2, new Cesium.Cartesian3()) + let point3 = Cesium.Cartesian3.add(end, offsetEnd, new Cesium.Cartesian3()) + let point4 = Cesium.Cartesian3.add(end, offsetEnd2, new Cesium.Cartesian3()) + + area.push([point1, point3, point4, point2]) + + rightPositions.push([point1, point3]) + leftPositions.push([point2, point4]) + + // if (i == positions.length - 2) { + // area.push(point1, point2, point3, point4) + // rightPositions.push(point1) + // leftPositions.push(point2) + // leftPositions.push(point4) + // rightPositions.push(point3) + // } else { + // area.push(point1, point2) + // rightPositions.push(point1) + // leftPositions.push(point2) + // } + } + // let arr = [] + // for (let i = 0; i < area.length - 2; i += 2) { + // arr.push([area[i], area[i + 1], area[i + 3], area[i + 2]]) + // } + console.log(area, rightPositions, 'rightPositions') + let that = this + // return [arr, rightPositions, leftPositions] + return [area, rightPositions, leftPositions] + } + getIntersects(point1, point2, point3, point4) { + let carPoint1 = this.getLonLat(point1) + let carPoint2 = this.getLonLat(point2) + let carPoint3 = this.getLonLat(point3) + let carPoint4 = this.getLonLat(point4) + var line1 = turf.lineString([ + [carPoint1.lon, carPoint1.lat], + [carPoint2.lon, carPoint2.lat] + ]); + var line2 = turf.lineString([ + [carPoint3.lon, carPoint3.lat], + [carPoint4.lon, carPoint4.lat] + ]); + var intersects = turf.lineIntersect(line1, line2); + if (intersects.features.length > 0) { + console.log(intersects.features, 'ooooo') + return Cesium.Cartesian3.fromDegrees(intersects.features[0].geometry.coordinates[0], intersects.features[0].geometry.coordinates[1]) + } + } + getLonLat(point) { + let pointDe = Cesium.Cartographic.fromCartesian(point) + const longitude = Cesium.Math.toDegrees(pointDe.longitude); + const latitude = Cesium.Math.toDegrees(pointDe.latitude); + return { lon: longitude, lat: latitude } + + } + createLineBufferPolygon(viewer, positions, width) { + // 计算每个线段的左右偏移点 + const leftPositions = []; + const rightPositions = []; + + for (let i = 0; i < positions.length; i++) { + const start = positions[i]; + const end = positions[i + 1] || positions[i - 1]; + + // 计算线段方向向量 + const direction = Cesium.Cartesian3.subtract(end, start, new Cesium.Cartesian3()); + // const direction = Cesium.Cartesian3.subtract(start, end, new Cesium.Cartesian3()); + Cesium.Cartesian3.normalize(direction, direction); + + // 计算垂直向量(使用上向量叉积) + const up = Cesium.Cartesian3.UNIT_Z; + const perpendicular = Cesium.Cartesian3.cross(direction, up, new Cesium.Cartesian3()); + Cesium.Cartesian3.normalize(perpendicular, perpendicular); + + // 计算左右偏移点 + const leftOffset = Cesium.Cartesian3.multiplyByScalar( + perpendicular, + width, + new Cesium.Cartesian3() + ); + + if (width > 0) { + rightPositions.unshift(Cesium.Cartesian3.add(start, leftOffset, new Cesium.Cartesian3())); + } else if (width < 0) { + rightPositions.push(Cesium.Cartesian3.add(start, leftOffset, new Cesium.Cartesian3())); + } + + } + return rightPositions + } + //计算角度 + calculateAangle(arr) { + // let fromDegreesArray = that.calSector(that.options.center, that.options.radius, that.options.startAngle, that.options.endAngle, 360, true) + + function getAangle(start, end) { + let rad = Math.PI / 180, + lat1 = start.y * rad, + lat2 = end.y * rad, + lon1 = start.x * rad, + lon2 = end.x * rad; + const a = Math.sin(lon2 - lon1) * Math.cos(lat2); + const b = + Math.cos(lat1) * Math.sin(lat2) - + Math.sin(lat1) * Math.cos(lat2) * Math.cos(lon2 - lon1); + const radians = Math.atan2(a, b) + const degrees = radians % (2 * Math.PI); + let bearing = 450 - ((degrees * 180) / Math.PI < 0 + ? 360 + (degrees * 180) / Math.PI + : (degrees * 180) / Math.PI) - 90; + return 360 - (bearing % 360) + } + + let center = arr[0] + let pos84_1 = arr[1] + let pos84_2 = arr[2] + + let start = { x: center.lng, y: center.lat } + let end1 = { x: pos84_1.lng, y: pos84_1.lat } + let end2 = { x: pos84_2.lng, y: pos84_2.lat } + + let angle1 = getAangle(start, end1) + let angle2 = getAangle(start, end2) + + return { + angle1, + angle2 + } + } + + get carRoadWidth() { + return this.options.carRoadWidth + } + + set carRoadWidth(v) { + this.options.carRoadWidth = v + Road.create(this) + + } + get sideWidth() { + return this.options.sideWidth + } + set sideWidth(v) { + this.options.sideWidth = v + Road.create(this) + } + /** + * @description 编辑框 + * @param state=false {boolean} 状态: true打开, false关闭 + */ + async edit(state = false) { + let _this = this + this.originalOptions = this.deepCopyObj(this.options) + + if (this._DialogObject && this._DialogObject.close) { + this._DialogObject.close() + this._DialogObject = null + } + + if (state) { + this._DialogObject = await new Dialog(this.sdk, this.originalOptions, { + title: '道路属性', left: '180px', top: '100px', + confirmCallBack: (options) => { + this.name = this.name.trim() + if (!this.name) { + this.name = '道路' + } + this.originalOptions = this.deepCopyObj(this.options) + this._DialogObject.close() + this.Dialog.confirmCallBack && this.Dialog.confirmCallBack(this.originalOptions) + syncData(this.sdk, this.options.id) + syncSplitData(this.sdk, this.options.id) + }, + resetCallBack: () => { + this.reset() + this.Dialog.resetCallBack && this.Dialog.resetCallBack() + }, + closeCallBack: () => { + this.reset() + this.Dialog.closeCallBack && this.Dialog.closeCallBack() + }, + showCallBack: (show) => { + this.show = show + this.Dialog.showCallBack && this.Dialog.showCallBack() + } + }, true) + this._DialogObject._element.body.className = this._DialogObject._element.body.className + ' road-surface' + let contentElm = document.createElement('div'); + contentElm.innerHTML = html() + this._DialogObject.contentAppChild(contentElm) + + + // 下拉选项 + // let heightModeData = [ + // { + // name: '海拔高度', + // value: '海拔高度', + // key: '0', + // }, + // { + // name: '相对地表', + // value: '相对地表', + // key: '1', + // }, + // { + // name: '依附模型', + // value: '依附模型', + // key: '2', + // } + // ] + // let heightModeObject = legp( + // this._DialogObject._element.content.getElementsByClassName( + // 'road-box' + // )[0], + // '.road-type' + // ) + // if (heightModeObject) { + // heightModeObject.legp_search(heightModeData) + // let heightModeDataLegpElm = this._DialogObject._element.content + // .getElementsByClassName('road-type')[0] + // .getElementsByTagName('input')[0] + // for (let i = 0; i < heightModeData.length; i++) { + // if (heightModeData[i].key == this.heightMode) { + // heightModeDataLegpElm.value = heightModeData[i].value + // heightModeObject.legp_searchActive( + // heightModeData[i].value + // ) + // break + // } + // } + // heightModeDataLegpElm.addEventListener('input', () => { + // for (let i = 0; i < heightModeData.length; i++) { + // if (heightModeData[i].value === heightModeDataLegpElm.value) { + // this.heightMode = heightModeData[i].key + // break + // } + // } + // }) + + + // this._elms.height = heightElm + // this._elms.heightBox = heightBoxElm + // this._elms.heightMode = heightModeDataLegpElm + // this._elms.heightConfirm = heightConfirmElm + // this._elms.heightModeObject = heightModeObject + + // heightConfirmElm.addEventListener('click', () => { + // this.positionEditing = false + // for (let i = 0; i < this.options.positions.length; i++) { + // this.options.positions[i].alt = Number((this.options.positions[i].alt + Number(heightElm.value)).toFixed(2)) + // this._elms.alt[i].innerHTML = this.options.positions[i].alt + // } + // let fromDegreesArray = this.renewPositions(this.options.positions) + // this.entity.polyline.positions = Cesium.Cartesian3.fromDegreesArrayHeights( + // fromDegreesArray + // ) + + // this.positionEditing = false + // PolylineObject.closeNodeEdit(this) + // }) + // } + + + + + + let all_elm = contentElm.getElementsByTagName("*") + this._EventBinding.on(this, all_elm) + this._elms = this._EventBinding.element + } else { + // if (this._element_style) { + // document.getElementsByTagName('head')[0].removeChild(this._element_style) + // this._element_style = null + // } + // if (this._DialogObject && this._DialogObject.remove) { + // this._DialogObject.remove() + // this._DialogObject = null + // } + } + } + + reset() { + if (!this.viewer.entities.getById(this.options.id)) { + return + } + this.name = this.originalOptions.name + this.carRoadWidth = this.originalOptions.carRoadWidth + this.sideWidth = this.originalOptions.sideWidth + this.positions = this.originalOptions.positions + this.roadImage = this.originalOptions.roadImage + this.sideImage = this.originalOptions.sideImage + } + + /** + * 飞到对应实体 + */ + async flyTo(options = {}) { + setActiveViewer(0) + closeRotateAround(this.sdk) + closeViewFollow(this.sdk) + + if (this.options.customView && this.options.customView.relativePosition && this.options.customView.orientation) { + let orientation = { + heading: Cesium.Math.toRadians(this.options.customView.orientation.heading || 0.0), + pitch: Cesium.Math.toRadians(this.options.customView.orientation.pitch || -60.0), + roll: Cesium.Math.toRadians(this.options.customView.orientation.roll || 0.0) + } + + let lng = this.options.customView.relativePosition.lng + let lat = this.options.customView.relativePosition.lat + let alt = this.options.customView.relativePosition.alt + let destination = Cesium.Cartesian3.fromDegrees(lng, lat, alt) + + let position = { lng: 0, lat: 0 } + if (this.options.position) { + position = { ...this.options.position } + } + else if (this.options.positions) { + position = { ...this.options.positions[0] } + } + else if (this.options.center) { + position = { ...this.options.center } + } + else if (this.options.start) { + position = { ...this.options.start } + } + else { + if (this.options.hasOwnProperty('lng')) { + position.lng = this.options.lng + } + if (this.options.hasOwnProperty('lat')) { + position.lat = this.options.lat + } + if (this.options.hasOwnProperty('alt')) { + position.alt = this.options.alt + } + } + // 如果没有高度值,则获取紧贴高度计算 + // if (!position.hasOwnProperty('alt')) { + // position.alt = await this.getClampToHeight(position) + // } + lng = this.options.customView.relativePosition.lng + position.lng + lat = this.options.customView.relativePosition.lat + position.lat + alt = this.options.customView.relativePosition.alt + position.alt + destination = Cesium.Cartesian3.fromDegrees(lng, lat, alt) + this.sdk.viewer.camera.flyTo({ + destination: destination, + orientation: orientation + }) + } + else { + let positionArray = [] + for (let i = 0; i < this.positions.length; i++) { + let a = Cesium.Cartesian3.fromDegrees( + this.positions[i][0], + this.positions[i][1], + this.options.height + this.options.heightDifference / 2 + ) + positionArray.push(a.x, a.y, a.z) + } + let BoundingSphere = Cesium.BoundingSphere.fromVertices(positionArray) + this.viewer.camera.flyToBoundingSphere(BoundingSphere, { + offset: { + heading: Cesium.Math.toRadians(0.0), + pitch: Cesium.Math.toRadians(-20.0), + roll: Cesium.Math.toRadians(0.0) + } + }) + + } + } + + getSphere() { + return new Promise((resolve) => { + // entity没有加载完成时 state 不会等于0 所以设置定时器直到获取到为止 + const interval = setInterval(() => { + const sphere = new Cesium.BoundingSphere() + const state = this.sdk.viewer._dataSourceDisplay.getBoundingSphere( + this.viewer.entities.getById(this.options.id), + false, + sphere + ) + if (state === Cesium.BoundingSphereState.DONE) { + clearInterval(interval) + } + }, 1000) + }) + } + + /** + * 删除 + */ + async remove() { + this.positions = [] + this.lineEntity = null + + if (this.viewer.entities.getById(this.options.id)) { + this.viewer.entities.getById(this.options.id)._children.forEach((item) => { + this.viewer.entities.remove(item); + }); + this.viewer.entities.remove(this.viewer.entities.getById(this.options.id)) + } + + if (this._DialogObject && !this._DialogObject.isDestroy) { + this._DialogObject.close() + this._DialogObject = null + } + await this.sdk.removeIncetance(this.options.id) + await syncData(this.sdk, this.options.id) + } + + flicker() { } +} + +export default Road diff --git a/src/Obj/Base/RoadObject/index-直接连接.js b/src/Obj/Base/RoadObject/index-直接连接.js new file mode 100644 index 0000000..e31297a --- /dev/null +++ b/src/Obj/Base/RoadObject/index-直接连接.js @@ -0,0 +1,615 @@ +/** + * @description 道路 + */ +import Dialog from '../../Element/Dialog'; +import { html } from "./_element"; +import EventBinding from '../../Element/Dialog/eventBinding'; +import Base from "../index"; +import { syncData } from '../../../Global/MultiViewportMode' +import { setSplitDirection, syncSplitData, setActiveId } from '../../../Global/SplitScreen' +import { setActiveViewer, closeRotateAround, closeViewFollow } from '../../../Global/global' + +class Road extends Base { + /** + * @constructor + * @param sdk + * @description 道路 + * @param options {object} 道路属性 + * @param options.name=未命名对象 {string} 名称 + * @param options.carRoadWidth=2 {number} 车道宽度 + * @param options.sideWidth=2 {number} 人行道宽度 + * @param options.positions=[] {array} 道路positions + * @param options.roadImage='' {string} 车道贴图 + * @param options.sideImage='' {string} 人行道贴图 + * @param Dialog {object} 弹框对象 + * @param Dialog.confirmCallBack {function} 弹框确认时的回调 + * */ + constructor(sdk, options = {}, _Dialog = {}) { + super(sdk, options); + this.viewer = this.sdk.viewer + this.options.name = options.name || '道路' + this.options.carRoadWidth = options.carRoadWidth || 10 + this.options.sideWidth = options.sideWidth || 5 + this.options.positions = options.positions || [] + this.options.roadImage = options.roadImage || (this.getSourceRootPath() + '/img/roadPhoto.png') + this.options.sideImage = options.sideImage || (this.getSourceRootPath() + '/img/sidePhoto.png') + this.options.show = (options.show || options.show === false) ? options.show : true + this.Dialog = _Dialog + this._EventBinding = new EventBinding() + this._elms = {}; + this.positionArea = [] + this.positions = [] + this.lineEntity = '' + + this.sdk.addIncetance(this.options.id, this) + Road.create(this) + } + + // 创建道路 + static create(that) { + let positions = [] + that.options.positions.forEach(v => { + positions.push(new Cesium.Cartesian3.fromDegrees(v.lng, v.lat, v.alt)) + }) + + let area = [[], [], []] + area[1] = that.createLineBufferPolygon2(positions, that.options.carRoadWidth / 2) + area[0] = that.createLineBufferPolygonSide(area[1][2], -that.options.sideWidth) + area[2] = that.createLineBufferPolygonSide(area[1][1], that.options.sideWidth) + + if (that.viewer.entities.getById(that.options.id)) { + that.viewer.entities.getById(that.options.id)._children.forEach((item) => { + that.viewer.entities.remove(item); + }); + that.viewer.entities.remove(that.viewer.entities.getById(that.options.id)) + } + that.lineEntity = that.viewer.entities.add(new Cesium.Entity({ id: that.options.id, show: that.options.show })) + + const myImg = new Image() + myImg.src = that.options.roadImage + myImg.onload = function () { + area[1][0].forEach((item, index) => { + that.viewer.entities.add({ + // id: that.options.id, + parent: that.lineEntity, + polygon: { + hierarchy: new Cesium.PolygonHierarchy(item), + material: new Cesium.ImageMaterialProperty({ + image: that.options.roadImage, + transparent: true,// 如果图片有透明部分,需要设置为 true + repeat: that.calculateTextureRepeat(item, myImg) + }), + stRotation: that.calculateRoadAngle(positions[index], positions[index + 1]) + } + }); + }) + } + + const myImg2 = new Image() + myImg2.src = that.options.sideImage + myImg2.onload = function () { + area[0].forEach((item, index) => { + that.viewer.entities.add({ + parent: that.lineEntity, + polygon: { + hierarchy: new Cesium.PolygonHierarchy(item), + material: new Cesium.ImageMaterialProperty({ + image: that.options.sideImage, + transparent: true,// 如果图片有透明部分,需要设置为 true + repeat: that.calculateTextureRepeat(item, myImg2) + }), + stRotation: that.calculateRoadAngle(positions[index], positions[index + 1]) + } + }); + }) + + area[2].forEach((item, index) => { + that.viewer.entities.add({ + polygon: { + hierarchy: new Cesium.PolygonHierarchy(item), + material: new Cesium.ImageMaterialProperty({ + image: that.options.sideImage, + transparent: true,// 如果图片有透明部分,需要设置为 true + repeat: that.calculateTextureRepeat(item, myImg2) + }), + stRotation: that.calculateRoadAngle(positions[index], positions[index + 1]) + } + }); + }) + + } + + } + getArr(arr1, arr2) { + arr2 = arr2.reverse() + let polygon = [] + for (let index = 0; index < arr1.length - 1; index++) { + polygon.push([arr1[index], arr1[index + 1], arr2[index + 1], arr2[index]]) + } + return polygon + } + + calculateRoadAngle(startPoint, endPoint) { + // 1. 获取地表法向量 + const normal = Cesium.Ellipsoid.WGS84.geodeticSurfaceNormal(startPoint); + + // 2. 构建精确ENU坐标系 + const enuMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(startPoint, undefined, normal); + const inverseMatrix = Cesium.Matrix4.inverse(enuMatrix, new Cesium.Matrix4()); + + // 3. 转换终点并计算水平向量 + const localEnd = Cesium.Matrix4.multiplyByPoint(inverseMatrix, endPoint, new Cesium.Cartesian3()); + const horizontalVec = new Cesium.Cartesian2(localEnd.x, localEnd.y); + Cesium.Cartesian2.normalize(horizontalVec, horizontalVec); + + const north = new Cesium.Cartesian2(1, 0); + + const angle = Cesium.Cartesian2.angleBetween(north, horizontalVec); + const cross = Cesium.Cartesian2.cross(north, horizontalVec, new Cesium.Cartesian2()); + return cross < 0 ? angle : -angle; + } + + calculatePolygonOrientation(positions) { + + // 假设 position 是 Cesium.Cartesian3 对象,表示地球上的某个点 + var position = positions[0] + // 获取东、北、上坐标系 + var eastNorthUp = Cesium.Transforms.eastNorthUpToFixedFrame(position); + // northAxis 是北方向向量 + var northAxis = eastNorthUp.getColumn(1, new Cesium.Cartesian3()); + Cesium.Cartesian3.normalize(northAxis, northAxis); + + const direction = Cesium.Cartesian3.subtract(positions[0], positions[1], new Cesium.Cartesian3()); + Cesium.Cartesian3.normalize(direction, direction); + + + const dot = Cesium.Cartesian3.dot(northAxis, direction); + const magA = Cesium.Cartesian3.magnitude(northAxis); + const magB = Cesium.Cartesian3.magnitude(direction); + return Math.acos(dot / (magA * magB)); + } + calculateTextureRepeat(polygonPositions, textureSize, meterPerPixel = 0.01) { + // 验证纹理尺寸 + if (!textureSize.width || !textureSize.height) { + throw new Error('Texture size must contain width and height in pixels'); + } + + // 创建多边形几何体 + const geometry = Cesium.PolygonGeometry.createGeometry( + new Cesium.PolygonGeometry({ + polygonHierarchy: new Cesium.PolygonHierarchy(polygonPositions), + vertexFormat: Cesium.VertexFormat.POSITION_ONLY + }) + ); + + // 计算多边形面积(平方米) + let area = 0; + const indices = geometry.indices; + const positions = geometry.attributes.position.values; + for (let i = 0; i < indices.length; i += 3) { + const i0 = indices[i] * 3; + const i1 = indices[i + 1] * 3; + const i2 = indices[i + 2] * 3; + + const p0 = new Cesium.Cartesian3(positions[i0], positions[i0 + 1], positions[i0 + 2]); + const p1 = new Cesium.Cartesian3(positions[i1], positions[i1 + 1], positions[i1 + 2]); + const p2 = new Cesium.Cartesian3(positions[i2], positions[i2 + 1], positions[i2 + 2]); + + const cross = Cesium.Cartesian3.cross( + Cesium.Cartesian3.subtract(p1, p0, new Cesium.Cartesian3()), + Cesium.Cartesian3.subtract(p2, p0, new Cesium.Cartesian3()), + new Cesium.Cartesian3() + ); + area += Cesium.Cartesian3.magnitude(cross) * 0.5; + } + + // 将像素尺寸转换为实际尺寸(平方米) + const textureWidthMeters = textureSize.width * meterPerPixel; + const textureHeightMeters = textureSize.height * meterPerPixel; + const textureArea = textureWidthMeters * textureHeightMeters; + + // 计算各轴向重复次数 + const repeatX = Math.sqrt(area) / textureWidthMeters; + const repeatY = Math.sqrt(area) / textureHeightMeters; + + return new Cesium.Cartesian2(Math.max(1, Math.ceil(repeatX)), 1.0); + } + swapLastElements(arr1, arr2) { + const last = arr1[arr1.length - 1] + const first = arr2[0] + arr1[arr1.length - 1] = first + arr2[0] = last + + return [arr1, arr2]; + } + createLineBufferPolygonSide(positions, width) { + let area = [] + for (let i = 0; i < positions.length - 1; i++) { + const start = positions[i]; + const end = positions[i + 1]; + + const dir = Cesium.Cartesian3.subtract(end, start, new Cesium.Cartesian3()); + Cesium.Cartesian3.normalize(dir, dir); + + // 获取垂直向量(基于Z轴) + const perp = Cesium.Cartesian3.cross(dir, Cesium.Cartesian3.UNIT_Z, new Cesium.Cartesian3()); + Cesium.Cartesian3.normalize(perp, perp); + + // 生成偏移向量 + const offset = Cesium.Cartesian3.multiplyByScalar(perp, width, new Cesium.Cartesian3()); + let point1 = Cesium.Cartesian3.add(start, offset, new Cesium.Cartesian3()) + let point3 = Cesium.Cartesian3.add(end, offset, new Cesium.Cartesian3()) + + i == positions.length - 2 ? area.push(start, point1, end, point3) : area.push(start, point1) + } + let arr = [] + for (let i = 0; i < area.length - 2; i += 2) { + arr.push([area[i], area[i + 1], area[i + 3], area[i + 2]]) + } + return arr + } + createLineBufferPolygon2(positions, width) { + let area = [] + let leftPositions = []; + let rightPositions = []; + + for (let i = 0; i < positions.length - 1; i++) { + const start = positions[i]; + // const end = positions[i + 1] || positions[i - 1]; + const end = positions[i + 1]; + + const dir = Cesium.Cartesian3.subtract(end, start, new Cesium.Cartesian3()); + Cesium.Cartesian3.normalize(dir, dir); + + // 获取垂直向量(基于Z轴) + const perp = Cesium.Cartesian3.cross(dir, Cesium.Cartesian3.UNIT_Z, new Cesium.Cartesian3()); + Cesium.Cartesian3.normalize(perp, perp); + + + // 生成偏移向量 + const offset = Cesium.Cartesian3.multiplyByScalar(perp, width, new Cesium.Cartesian3()); + const offset2 = Cesium.Cartesian3.multiplyByScalar(perp, -width, new Cesium.Cartesian3()); + let point1 = Cesium.Cartesian3.add(start, offset, new Cesium.Cartesian3()) + let point2 = Cesium.Cartesian3.add(start, offset2, new Cesium.Cartesian3()) + let point3 = Cesium.Cartesian3.add(end, offset, new Cesium.Cartesian3()) + let point4 = Cesium.Cartesian3.add(end, offset2, new Cesium.Cartesian3()) + + if (i == positions.length - 2) { + area.push(point1, point2, point3, point4) + rightPositions.push(point1) + leftPositions.push(point2) + leftPositions.push(point4) + rightPositions.push(point3) + } else { + area.push(point1, point2) + rightPositions.push(point1) + leftPositions.push(point2) + } + } + let arr = [] + for (let i = 0; i < area.length - 2; i += 2) { + arr.push([area[i], area[i + 1], area[i + 3], area[i + 2]]) + } + return [arr, rightPositions, leftPositions] + } + createLineBufferPolygon(viewer, positions, width) { + // 计算每个线段的左右偏移点 + const leftPositions = []; + const rightPositions = []; + + for (let i = 0; i < positions.length; i++) { + const start = positions[i]; + const end = positions[i + 1] || positions[i - 1]; + + // 计算线段方向向量 + const direction = Cesium.Cartesian3.subtract(end, start, new Cesium.Cartesian3()); + // const direction = Cesium.Cartesian3.subtract(start, end, new Cesium.Cartesian3()); + Cesium.Cartesian3.normalize(direction, direction); + + // 计算垂直向量(使用上向量叉积) + const up = Cesium.Cartesian3.UNIT_Z; + const perpendicular = Cesium.Cartesian3.cross(direction, up, new Cesium.Cartesian3()); + Cesium.Cartesian3.normalize(perpendicular, perpendicular); + + // 计算左右偏移点 + const leftOffset = Cesium.Cartesian3.multiplyByScalar( + perpendicular, + width, + new Cesium.Cartesian3() + ); + + if (width > 0) { + rightPositions.unshift(Cesium.Cartesian3.add(start, leftOffset, new Cesium.Cartesian3())); + } else if (width < 0) { + rightPositions.push(Cesium.Cartesian3.add(start, leftOffset, new Cesium.Cartesian3())); + } + + } + return rightPositions + } + + get carRoadWidth() { + return this.options.carRoadWidth + } + + set carRoadWidth(v) { + this.options.carRoadWidth = v + Road.create(this) + + } + get sideWidth() { + return this.options.sideWidth + } + set sideWidth(v) { + this.options.sideWidth = v + Road.create(this) + } + /** + * @description 编辑框 + * @param state=false {boolean} 状态: true打开, false关闭 + */ + async edit(state = false) { + let _this = this + this.originalOptions = this.deepCopyObj(this.options) + + if (this._DialogObject && this._DialogObject.close) { + this._DialogObject.close() + this._DialogObject = null + } + + if (state) { + this._DialogObject = await new Dialog(this.sdk, this.originalOptions, { + title: '道路属性', left: '180px', top: '100px', + confirmCallBack: (options) => { + this.name = this.name.trim() + if (!this.name) { + this.name = '道路' + } + this.originalOptions = this.deepCopyObj(this.options) + this._DialogObject.close() + this.Dialog.confirmCallBack && this.Dialog.confirmCallBack(this.originalOptions) + syncData(this.sdk, this.options.id) + syncSplitData(this.sdk, this.options.id) + }, + resetCallBack: () => { + this.reset() + this.Dialog.resetCallBack && this.Dialog.resetCallBack() + }, + closeCallBack: () => { + this.reset() + this.Dialog.closeCallBack && this.Dialog.closeCallBack() + }, + showCallBack: (show) => { + this.show = show + this.Dialog.showCallBack && this.Dialog.showCallBack() + } + }, true) + this._DialogObject._element.body.className = this._DialogObject._element.body.className + ' road-surface' + let contentElm = document.createElement('div'); + contentElm.innerHTML = html() + this._DialogObject.contentAppChild(contentElm) + + + // 下拉选项 + // let heightModeData = [ + // { + // name: '海拔高度', + // value: '海拔高度', + // key: '0', + // }, + // { + // name: '相对地表', + // value: '相对地表', + // key: '1', + // }, + // { + // name: '依附模型', + // value: '依附模型', + // key: '2', + // } + // ] + // let heightModeObject = legp( + // this._DialogObject._element.content.getElementsByClassName( + // 'road-box' + // )[0], + // '.road-type' + // ) + // if (heightModeObject) { + // heightModeObject.legp_search(heightModeData) + // let heightModeDataLegpElm = this._DialogObject._element.content + // .getElementsByClassName('road-type')[0] + // .getElementsByTagName('input')[0] + // for (let i = 0; i < heightModeData.length; i++) { + // if (heightModeData[i].key == this.heightMode) { + // heightModeDataLegpElm.value = heightModeData[i].value + // heightModeObject.legp_searchActive( + // heightModeData[i].value + // ) + // break + // } + // } + // heightModeDataLegpElm.addEventListener('input', () => { + // for (let i = 0; i < heightModeData.length; i++) { + // if (heightModeData[i].value === heightModeDataLegpElm.value) { + // this.heightMode = heightModeData[i].key + // break + // } + // } + // }) + + + // this._elms.height = heightElm + // this._elms.heightBox = heightBoxElm + // this._elms.heightMode = heightModeDataLegpElm + // this._elms.heightConfirm = heightConfirmElm + // this._elms.heightModeObject = heightModeObject + + // heightConfirmElm.addEventListener('click', () => { + // this.positionEditing = false + // for (let i = 0; i < this.options.positions.length; i++) { + // this.options.positions[i].alt = Number((this.options.positions[i].alt + Number(heightElm.value)).toFixed(2)) + // this._elms.alt[i].innerHTML = this.options.positions[i].alt + // } + // let fromDegreesArray = this.renewPositions(this.options.positions) + // this.entity.polyline.positions = Cesium.Cartesian3.fromDegreesArrayHeights( + // fromDegreesArray + // ) + + // this.positionEditing = false + // PolylineObject.closeNodeEdit(this) + // }) + // } + + + + + + let all_elm = contentElm.getElementsByTagName("*") + this._EventBinding.on(this, all_elm) + this._elms = this._EventBinding.element + } else { + // if (this._element_style) { + // document.getElementsByTagName('head')[0].removeChild(this._element_style) + // this._element_style = null + // } + // if (this._DialogObject && this._DialogObject.remove) { + // this._DialogObject.remove() + // this._DialogObject = null + // } + } + } + + reset() { + if (!this.viewer.entities.getById(this.options.id)) { + return + } + this.name = this.originalOptions.name + this.carRoadWidth = this.originalOptions.carRoadWidth + this.sideWidth = this.originalOptions.sideWidth + this.positions = this.originalOptions.positions + this.roadImage = this.originalOptions.roadImage + this.sideImage = this.originalOptions.sideImage + } + + /** + * 飞到对应实体 + */ + async flyTo(options = {}) { + setActiveViewer(0) + closeRotateAround(this.sdk) + closeViewFollow(this.sdk) + + if (this.options.customView && this.options.customView.relativePosition && this.options.customView.orientation) { + let orientation = { + heading: Cesium.Math.toRadians(this.options.customView.orientation.heading || 0.0), + pitch: Cesium.Math.toRadians(this.options.customView.orientation.pitch || -60.0), + roll: Cesium.Math.toRadians(this.options.customView.orientation.roll || 0.0) + } + + let lng = this.options.customView.relativePosition.lng + let lat = this.options.customView.relativePosition.lat + let alt = this.options.customView.relativePosition.alt + let destination = Cesium.Cartesian3.fromDegrees(lng, lat, alt) + + let position = { lng: 0, lat: 0 } + if (this.options.position) { + position = { ...this.options.position } + } + else if (this.options.positions) { + position = { ...this.options.positions[0] } + } + else if (this.options.center) { + position = { ...this.options.center } + } + else if (this.options.start) { + position = { ...this.options.start } + } + else { + if (this.options.hasOwnProperty('lng')) { + position.lng = this.options.lng + } + if (this.options.hasOwnProperty('lat')) { + position.lat = this.options.lat + } + if (this.options.hasOwnProperty('alt')) { + position.alt = this.options.alt + } + } + // 如果没有高度值,则获取紧贴高度计算 + // if (!position.hasOwnProperty('alt')) { + // position.alt = await this.getClampToHeight(position) + // } + lng = this.options.customView.relativePosition.lng + position.lng + lat = this.options.customView.relativePosition.lat + position.lat + alt = this.options.customView.relativePosition.alt + position.alt + destination = Cesium.Cartesian3.fromDegrees(lng, lat, alt) + this.sdk.viewer.camera.flyTo({ + destination: destination, + orientation: orientation + }) + } + else { + let positionArray = [] + for (let i = 0; i < this.positions.length; i++) { + let a = Cesium.Cartesian3.fromDegrees( + this.positions[i][0], + this.positions[i][1], + this.options.height + this.options.heightDifference / 2 + ) + positionArray.push(a.x, a.y, a.z) + } + let BoundingSphere = Cesium.BoundingSphere.fromVertices(positionArray) + this.viewer.camera.flyToBoundingSphere(BoundingSphere, { + offset: { + heading: Cesium.Math.toRadians(0.0), + pitch: Cesium.Math.toRadians(-20.0), + roll: Cesium.Math.toRadians(0.0) + } + }) + + } + } + + getSphere() { + return new Promise((resolve) => { + // entity没有加载完成时 state 不会等于0 所以设置定时器直到获取到为止 + const interval = setInterval(() => { + const sphere = new Cesium.BoundingSphere() + const state = this.sdk.viewer._dataSourceDisplay.getBoundingSphere( + this.viewer.entities.getById(this.options.id), + false, + sphere + ) + if (state === Cesium.BoundingSphereState.DONE) { + clearInterval(interval) + } + }, 1000) + }) + } + + /** + * 删除 + */ + async remove() { + this.positions = [] + this.lineEntity = null + + if (this.viewer.entities.getById(this.options.id)) { + this.viewer.entities.getById(this.options.id)._children.forEach((item) => { + this.viewer.entities.remove(item); + }); + this.viewer.entities.remove(this.viewer.entities.getById(this.options.id)) + } + + if (this._DialogObject && !this._DialogObject.isDestroy) { + this._DialogObject.close() + this._DialogObject = null + } + await this.sdk.removeIncetance(this.options.id) + await syncData(this.sdk, this.options.id) + } + + flicker() { } +} + +export default Road diff --git a/src/Obj/Base/RoadObject/index-面大小一致.js b/src/Obj/Base/RoadObject/index-面大小一致.js new file mode 100644 index 0000000..620efa6 --- /dev/null +++ b/src/Obj/Base/RoadObject/index-面大小一致.js @@ -0,0 +1,1023 @@ +/** + * @description 道路 + */ +import Dialog from '../../Element/Dialog'; +import { html } from "./_element"; +import EventBinding from '../../Element/Dialog/eventBinding'; +import Base from "../index"; +import { syncData } from '../../../Global/MultiViewportMode' +import { setSplitDirection, syncSplitData, setActiveId } from '../../../Global/SplitScreen' +import { setActiveViewer, closeRotateAround, closeViewFollow } from '../../../Global/global' + +class Road extends Base { + /** + * @constructor + * @param sdk + * @description 道路 + * @param options {object} 道路属性 + * @param options.name=未命名对象 {string} 名称 + * @param options.carRoadWidth=2 {number} 车道宽度 + * @param options.sideWidth=2 {number} 人行道宽度 + * @param options.positions=[] {array} 道路positions + * @param options.roadImage='' {string} 车道贴图 + * @param options.sideImage='' {string} 人行道贴图 + * @param Dialog {object} 弹框对象 + * @param Dialog.confirmCallBack {function} 弹框确认时的回调 + * */ + constructor(sdk, options = {}, _Dialog = {}) { + super(sdk, options); + this.viewer = this.sdk.viewer + this.options.name = options.name || '道路' + this.options.carRoadWidth = options.carRoadWidth || 10 + this.options.sideWidth = options.sideWidth || 5 + this.options.positions = options.positions || [] + this.options.roadImage = options.roadImage || (this.getSourceRootPath() + '/img/roadPhoto.png') + this.options.sideImage = options.sideImage || (this.getSourceRootPath() + '/img/sidePhoto.png') + this.options.show = (options.show || options.show === false) ? options.show : true + this.Dialog = _Dialog + this._EventBinding = new EventBinding() + this._elms = {}; + this.positionArea = [] + this.positions = [] + this.lineEntity = '' + this.crossArr = [] + this.pointArr = [] + + this.sdk.addIncetance(this.options.id, this) + Road.create(this) + } + // 创建道路 + static create(that) { + let positions = [] + that.options.positions.forEach(v => { + positions.push(new Cesium.Cartesian3.fromDegrees(v.lng, v.lat, v.alt)) + }) + let newPosi = [] + for (let i = 0; i < positions.length - 1; i++) { + const start = positions[i]; + const end = positions[i + 1]; + newPosi.push([start, end]) + that.pointArr = newPosi + } + + let area = [[], [], []] + + // area[1] = that.getRectangle(positions, that.options.carRoadWidth) + area[1][0] = that.getRectangle(newPosi, that.options.carRoadWidth) + let sideArr = that.getSideRectangle(area[1][0], that.options.sideWidth) + area[0] = sideArr.left + area[2] = sideArr.right + // area[1] = that.createLineBufferPolygon2(positions, that.options.carRoadWidth / 2) + // area[1] = newPositions + // area[0] = that.createLineBufferPolygonSide(area[1][2], -that.options.sideWidth) + // area[2] = that.createLineBufferPolygonSide(area[1][1], that.options.sideWidth) + + //判断道路边是否相交 + for (let i = 0; i < area[0].length - 1; i++) { + + let leftItem = area[0][i] + let leftItem2 = area[0][i + 1] + let rightItem = area[2][i] + let rightItem2 = area[2][i + 1] + let carItem = area[1][0][i] + let carItem2 = area[1][0][i + 1] + let leftLine = that.getIntersects(leftItem[2], leftItem[3], leftItem2[2], leftItem2[3]) + let rightLine = that.getIntersects(rightItem[0], rightItem[1], rightItem2[0], rightItem2[1]) + + console.log(leftLine, rightLine, 'leftLine') + if (leftLine) {//左侧相交 + //获取右侧延长交点 + let point1 = that.getExtendPoint(rightItem[0], rightItem[1], 1000) + let point2 = that.getExtendPoint(rightItem2[1], rightItem2[0], 1000) + console.log('aaaa') + let rightIntersection = that.getIntersects(rightItem[0], point1, rightItem2[1], point2) + //将其他几条边都延长 + let leftLineNeiPoint = that.getExtendPoint(leftItem[0], leftItem[1], 1000) + let carLeftPoint = that.getExtendPoint(carItem[3], carItem[2], 1000) + let carRightPoint = that.getExtendPoint(carItem[0], carItem[1], 1000) + + let rightLineNeiPoint = that.getExtendPoint(rightItem[3], rightItem[2], 1000) + + + + console.log(leftLine, rightIntersection, leftItem[0], leftLineNeiPoint, 'bbbb') + //跟左侧里相交点 + let leftLineNei = that.getIntersects(leftLine, rightIntersection, leftItem[0], leftLineNeiPoint) + console.log(leftLineNei, 'leftLineNei') + //跟车道左侧相交点 + let carLeft = that.getIntersects(leftLine, rightIntersection, carItem[3], carLeftPoint) + + //跟车道右侧相交点 + let carRight = that.getIntersects(leftLine, rightIntersection, carItem[0], carRightPoint) + let rightLineNei = that.getIntersects(leftLine, rightIntersection, rightItem[3], rightLineNeiPoint) + console.log('ccc') + // let leftLineNei = that.getIntersects(leftLine, rightItem[2], leftItem[0], leftItem[3]) + // let carLeft = that.getIntersects(leftLine, rightItem[2], carItem[3], carItem[2]) + // let carRight = that.getIntersects(leftLine, rightItem[2], carItem[0], carItem[1]) + // let rightLineNei = that.getIntersects(leftLine, rightItem[2], rightItem[0], rightItem[3]) + + // let leftLineNei = that.getIntersects(leftLine, intersection, leftItem[0], leftItem[3]) + // //跟车道左侧相交点 + // let carLeft = that.getIntersects(leftLine, intersection, carItem[3], carItem[2]) + // //跟车道右侧相交点 + // let carRight = that.getIntersects(leftLine, intersection, carItem[0], carItem[1]) + // let rightLineNei = that.getIntersects(leftLine, intersection, rightItem[0], rightItem[3]) + + leftItem[2] = leftLine + leftItem[1] = leftLineNei + carItem[2] = carLeft + carItem[1] = carRight + rightItem[2] = rightLineNei + rightItem[1] = rightIntersection + console.log(leftItem, carItem, rightItem, 'leftItemleft') + + + //将其他几条边都延长 + let leftLineNeiPoint2 = that.getExtendPoint(leftItem2[1], leftItem2[0], 1000) + let carLeftPoint2 = that.getExtendPoint(carItem2[2], carItem2[3], 1000) + let carRightPoint2 = that.getExtendPoint(carItem2[1], carItem2[0], 1000) + let rightLineNeiPoint2 = that.getExtendPoint(rightItem2[2], rightItem2[3], 1000) + + // let leftLineNei2 = that.getIntersects(leftLine, rightItem2[1], leftItem2[0], leftItem2[3]) + // let carLeft2 = that.getIntersects(leftLine, rightItem2[1], carItem2[3], carItem2[2]) + // let carRight2 = that.getIntersects(leftLine, rightItem2[1], carItem2[0], carItem2[1]) + // let rightLineNei2 = that.getIntersects(leftLine, rightItem2[1], rightItem2[0], rightItem2[3]) + + + + let leftLineNei2 = that.getIntersects(leftLine, rightIntersection, leftItem2[1], leftLineNeiPoint2) + let carLeft2 = that.getIntersects(leftLine, rightIntersection, carItem2[2], carLeftPoint2) + let carRight2 = that.getIntersects(leftLine, rightIntersection, carItem2[1], carRightPoint2) + let rightLineNei2 = that.getIntersects(leftLine, rightIntersection, rightItem2[2], rightLineNeiPoint2) + + // let arr = [leftLine, rightIntersection, leftItem2[3], leftLineNeiPoint2] + // arr.forEach((item, index) => { + // that.sdk.viewer.entities.add({ + // name: 'node-secondary-edit-point', + // index: i, + // position: item, + // billboard: { + // image: that.getSourceRootPath() + '/img/point.png', + // width: 15, + // height: 15, + // disableDepthTestDistance: Number.POSITIVE_INFINITY, + // color: Cesium.Color.WHITE.withAlpha(0.99) + // }, + // label: { + // text: '' + index, + // pixelOffset: { x: 0, y: -20 }, + // }, + // }) + // }) + + // let leftLineNei2 = that.getIntersects(leftLine, intersection, leftItem2[0], leftItem2[3]) + // //跟车道左侧相交点 + // let carLeft2 = that.getIntersects(leftLine, intersection, carItem2[3], carItem2[2]) + // //跟车道右侧相交点 + // let carRight2 = that.getIntersects(leftLine, intersection, carItem2[0], carItem2[1]) + // let rightLineNei2 = that.getIntersects(leftLine, intersection, rightItem2[0], rightItem2[3]) + + leftItem2[3] = leftLine + leftItem2[0] = leftLineNei + carItem2[3] = carLeft + carItem2[0] = carRight + rightItem2[3] = rightLineNei + rightItem2[0] = rightIntersection + console.log(leftItem2, carItem2, rightItem2, 'leftItem2left') + + } else if (rightLine) {//右侧相交 + + //获取左侧延长交点 + let point1 = that.getExtendPoint(leftItem[3], leftItem[2], 1000) + let point2 = that.getExtendPoint(leftItem2[2], leftItem2[3], 1000) + let rightIntersection = that.getIntersects(leftItem[3], point1, leftItem2[2], point2) + if (!rightIntersection) { + return + } + //将其他几条边都延长 + let leftLineNeiPoint = that.getExtendPoint(leftItem[0], leftItem[1], 1000) + let carLeftPoint = that.getExtendPoint(carItem[3], carItem[2], 1000) + let carRightPoint = that.getExtendPoint(carItem[0], carItem[1], 1000) + let rightLineNeiPoint = that.getExtendPoint(rightItem[3], rightItem[2], 1000) + + // //跟左侧里相交点 + let leftLineNei = that.getIntersects(rightLine, rightIntersection, leftItem[0], leftLineNeiPoint) + //跟车道左侧相交点 + let carLeft = that.getIntersects(rightLine, rightIntersection, carItem[3], carLeftPoint) + //跟车道右侧相交点 + let carRight = that.getIntersects(rightLine, rightIntersection, carItem[0], carRightPoint) + let rightLineNei = that.getIntersects(rightLine, rightIntersection, rightItem[3], rightLineNeiPoint) + // //跟左侧里相交点 + // let leftLineNei = that.getIntersects(rightLine, leftItem[2], leftItem[0], leftItem[3]) + // //跟车道左侧相交点 + // let carLeft = that.getIntersects(rightLine, leftItem[2], carItem[3], carItem[2]) + // //跟车道右侧相交点 + // let carRight = that.getIntersects(rightLine, leftItem[2], carItem[0], carItem[1]) + // let rightLineNei = that.getIntersects(rightLine, leftItem[2], rightItem[0], rightItem[3]) + + + leftItem[2] = rightIntersection + leftItem[1] = leftLineNei + carItem[2] = carLeft + carItem[1] = carRight + rightItem[2] = rightLineNei + rightItem[1] = rightLine + console.log(leftItem, carItem, rightItem, 'leftItemright') + + //将其他几条边都延长 + let leftLineNeiPoint2 = that.getExtendPoint(leftItem2[2], leftItem2[3], 1000) + let carLeftPoint2 = that.getExtendPoint(carItem2[2], carItem2[3], 1000) + let carRightPoint2 = that.getExtendPoint(carItem2[1], carItem2[0], 1000) + let rightLineNeiPoint2 = that.getExtendPoint(rightItem2[2], rightItem2[3], 1000) + + let leftLineNei2 = that.getIntersects(rightLine, rightIntersection, leftItem2[1], leftLineNeiPoint2) + //跟车道左侧相交点 + let carLeft2 = that.getIntersects(rightLine, rightIntersection, carItem2[2], carLeftPoint2) + //跟车道右侧相交点 + let carRight2 = that.getIntersects(rightLine, rightIntersection, carItem2[1], carRightPoint2) + let rightLineNei2 = that.getIntersects(rightLine, rightIntersection, rightItem2[2], rightLineNeiPoint2) + // let leftLineNei2 = that.getIntersects(rightLine, leftItem2[1], leftItem2[0], leftItem2[3]) + // //跟车道左侧相交点 + // let carLeft2 = that.getIntersects(rightLine, leftItem2[1], carItem2[3], carItem2[2]) + // //跟车道右侧相交点 + // let carRight2 = that.getIntersects(rightLine, leftItem2[1], carItem2[0], carItem2[1]) + // let rightLineNei2 = that.getIntersects(rightLine, leftItem2[1], rightItem2[0], rightItem2[3]) + + leftItem2[3] = rightIntersection + leftItem2[0] = leftLineNei + carItem2[3] = carLeft + carItem2[0] = carRight + rightItem2[3] = rightLineNei + rightItem2[0] = rightLine + console.log(leftItem2, carItem2, rightItem2, 'leftItem2right') + + } + } + + if (that.viewer.entities.getById(that.options.id)) { + that.viewer.entities.getById(that.options.id)._children.forEach((item) => { + that.viewer.entities.remove(item); + }); + that.viewer.entities.remove(that.viewer.entities.getById(that.options.id)) + } + that.lineEntity = that.viewer.entities.add(new Cesium.Entity({ id: that.options.id, show: that.options.show })) + + const myImg = new Image() + myImg.src = that.options.roadImage + myImg.onload = function () { + console.log(area[1][0], that.options.roadImage, 'llll') + area[1][0].forEach((item, index) => { + that.viewer.entities.add({ + // id: that.options.id, + parent: that.lineEntity, + polygon: { + hierarchy: new Cesium.PolygonHierarchy(item), + material: new Cesium.ImageMaterialProperty({ + image: that.options.roadImage, + transparent: true,// 如果图片有透明部分,需要设置为 true + repeat: that.calculateTextureRepeat(item, myImg) + }), + stRotation: that.calculateRoadAngle(positions[index], positions[index + 1]) + } + }); + }) + } + + const myImg2 = new Image() + myImg2.src = that.options.sideImage + myImg2.onload = function () { + // area[0].forEach((item, index) => { + area[0].forEach((item, index) => { + that.viewer.entities.add({ + parent: that.lineEntity, + polygon: { + hierarchy: new Cesium.PolygonHierarchy(item), + material: new Cesium.ImageMaterialProperty({ + image: that.options.sideImage, + transparent: true,// 如果图片有透明部分,需要设置为 true + repeat: that.calculateTextureRepeat(item, myImg2) + }), + stRotation: that.calculateRoadAngle(positions[index], positions[index + 1]) + } + }); + }) + + // area[2].forEach((item, index) => { + area[2].forEach((item, index) => { + that.viewer.entities.add({ + polygon: { + hierarchy: new Cesium.PolygonHierarchy(item), + material: new Cesium.ImageMaterialProperty({ + image: that.options.sideImage, + transparent: true,// 如果图片有透明部分,需要设置为 true + repeat: that.calculateTextureRepeat(item, myImg2) + }), + stRotation: that.calculateRoadAngle(positions[index], positions[index + 1]) + } + }); + }) + + } + + } + getSideRectangle(positions, width) { + let right = [] + let left = [] + let that = this + positions.forEach(item => { + right.push([item[0], item[1]]) + left.push([item[2], item[3]]) + }) + let rightPosi = that.getRectangle(right, width, 'side') + + let leftPosi = this.getRectangle(left, width, 'side') + return { left: leftPosi, right: rightPosi } + } + getRectangle(positions, width, type) { + let areaArr = [] + let newPositions = [] + let that = this + // for (let i = 0; i < positions.length - 1; i++) { + for (let i = 0; i < positions.length; i++) { + const start = positions[i][0]; + const end = positions[i][1]; + + areaArr[i] = [] + let posi = [] + let Outlinegeometry = new Cesium.CorridorGeometry({ + positions: [start, end], + width: width, + cornerType: Cesium.CornerType.MITERED, + vertexFormat: Cesium.MaterialAppearance.MaterialSupport.ALL.vertexFormat + }) + let geometry = Cesium.CorridorGeometry.createGeometry(Outlinegeometry) + for (let j = 0; j < geometry.attributes.position.values.length; j += 3) { + let val = that.cartesian3Towgs84(new Cesium.Cartesian3(geometry.attributes.position.values[j], geometry.attributes.position.values[j + 1], geometry.attributes.position.values[j + 2]), that.sdk.viewer) + posi.push([val.lng, val.lat]) + } + + for (let x = 0; x < geometry.indices.length; x += 3) { + areaArr[i].push(turf.polygon([[posi[geometry.indices[x]], posi[geometry.indices[x + 1]], posi[geometry.indices[x + 2]], posi[geometry.indices[x]]]])) + } + + let geojson = turf.union(areaArr[i][0], areaArr[i][1]); + let arr = [] + geojson.geometry.coordinates[0].pop() + geojson.geometry.coordinates[0].forEach(item => { + arr.push(new Cesium.Cartesian3.fromDegrees(item[0], item[1])) + }) + let dotResult, angle + const tempVec = new Cesium.Cartesian3(); + + // 计算并归一化第一个向量 + const vector1 = Cesium.Cartesian3.normalize( + Cesium.Cartesian3.subtract(that.pointArr[i][1], that.pointArr[i][0], tempVec), + tempVec + ); + + // 计算并归一化第二个向量 + const vector2 = Cesium.Cartesian3.normalize( + Cesium.Cartesian3.subtract(arr[1], arr[0], new Cesium.Cartesian3()), + new Cesium.Cartesian3() + ); + + dotResult = Cesium.Cartesian3.dot(vector1, vector2); + if ((0.999 < dotResult && dotResult < 1.001) || (-0.999 > dotResult && dotResult > -1.001)) {//调整方向 + newPositions.push(arr) + } else { + let newArr = [] + newArr[0] = arr[1] + newArr[1] = arr[2] + newArr[2] = arr[3] + newArr[3] = arr[0] + newPositions.push(newArr) + } + // } + + // if (!type) { + // // if (cross > 0 && !type) {//调整方向 + // if ((0.999 < dotResult && dotResult < 1.001) || (-0.999 > dotResult && dotResult > -1.001)) {//调整方向 + // newPositions.push(arr) + // } else { + // let newArr = [] + // newArr[0] = arr[1] + // newArr[1] = arr[2] + // newArr[2] = arr[3] + // newArr[3] = arr[0] + // newPositions.push(newArr) + // } + // } else { + // newPositions.push(arr) + // } + } + return newPositions + } + getExtendPoint(position1, position2, distance) { + // let position1 = Cesium.Cartesian3.fromDegrees(p1[0], p1[1], 0); + // let position2 = Cesium.Cartesian3.fromDegrees(p2[0], p2[1], 0); + let pot = Cesium.Cartesian3.subtract(position2, position1, new Cesium.Cartesian3());//方向 + var dir = Cesium.Cartesian3.normalize(pot, new Cesium.Cartesian3());//向量归一化 + + var ray = new Cesium.Ray(position1, dir); + let np = Cesium.Ray.getPoint(ray, distance * 10);//计算延长点 + return np + } + getArr(arr1, arr2) { + arr2 = arr2.reverse() + let polygon = [] + for (let index = 0; index < arr1.length - 1; index++) { + polygon.push([arr1[index], arr1[index + 1], arr2[index + 1], arr2[index]]) + } + return polygon + } + calculateRoadAngle2(startPoint, endPoint) { + // 1. 获取地表法向量 + const normal = this.sdk.viewer.scene.globe.ellipsoid.geodeticSurfaceNormal( + startPoint, new Cesium.Cartesian3()); + + // 2. 构建带椭球参数的ENU矩阵 + const enuMatrix = Cesium.Transforms.eastNorthUpToFixedFrame( + startPoint, this.sdk.viewer.scene.globe.ellipsoid, normal); + const inverseMatrix = Cesium.Matrix4.inverse( + enuMatrix, new Cesium.Matrix4()); + + // 3. 转换坐标并计算相对向量 + const localEnd = Cesium.Matrix4.multiplyByPoint( + inverseMatrix, endPoint, new Cesium.Cartesian3()); + const heightFactor = Math.abs(localEnd.z) / 1000; // 高度差补偿 + + // 4. 使用四象限反正切计算角度 + const angle = Math.atan2(localEnd.y, localEnd.x); + const adjustedAngle = angle - (heightFactor * 0.01); // 高度补偿 + let result = Cesium.Math.toDegrees(adjustedAngle) + console.log(result, 'result') + return result; + } + calculateRoadAngle(startPoint, endPoint) { + // 1. 获取地表法向量 + const normal = Cesium.Ellipsoid.WGS84.geodeticSurfaceNormal(startPoint); + + // 2. 构建精确ENU坐标系 + const enuMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(startPoint, undefined, normal); + const inverseMatrix = Cesium.Matrix4.inverse(enuMatrix, new Cesium.Matrix4()); + + // 3. 转换终点并计算水平向量 + const localEnd = Cesium.Matrix4.multiplyByPoint(inverseMatrix, endPoint, new Cesium.Cartesian3()); + const horizontalVec = new Cesium.Cartesian2(localEnd.x, localEnd.y); + Cesium.Cartesian2.normalize(horizontalVec, horizontalVec); + + const north = new Cesium.Cartesian2(1, 0); + + const angle = Cesium.Cartesian2.angleBetween(north, horizontalVec); + const cross = Cesium.Cartesian2.cross(north, horizontalVec, new Cesium.Cartesian2()); + return cross < 0 ? angle : -angle; + } + + calculatePolygonOrientation(positions) { + + // 假设 position 是 Cesium.Cartesian3 对象,表示地球上的某个点 + var position = positions[0] + // 获取东、北、上坐标系 + var eastNorthUp = Cesium.Transforms.eastNorthUpToFixedFrame(position); + // northAxis 是北方向向量 + var northAxis = eastNorthUp.getColumn(1, new Cesium.Cartesian3()); + Cesium.Cartesian3.normalize(northAxis, northAxis); + + const direction = Cesium.Cartesian3.subtract(positions[0], positions[1], new Cesium.Cartesian3()); + Cesium.Cartesian3.normalize(direction, direction); + + + const dot = Cesium.Cartesian3.dot(northAxis, direction); + const magA = Cesium.Cartesian3.magnitude(northAxis); + const magB = Cesium.Cartesian3.magnitude(direction); + return Math.acos(dot / (magA * magB)); + } + calculateTextureRepeat(polygonPositions, textureSize, meterPerPixel = 0.01) { + // 验证纹理尺寸 + if (!textureSize.width || !textureSize.height) { + throw new Error('Texture size must contain width and height in pixels'); + } + + // 创建多边形几何体 + const geometry = Cesium.PolygonGeometry.createGeometry( + new Cesium.PolygonGeometry({ + polygonHierarchy: new Cesium.PolygonHierarchy(polygonPositions), + vertexFormat: Cesium.VertexFormat.POSITION_ONLY + }) + ); + + // 计算多边形面积(平方米) + let area = 0; + const indices = geometry.indices; + const positions = geometry.attributes.position.values; + for (let i = 0; i < indices.length; i += 3) { + const i0 = indices[i] * 3; + const i1 = indices[i + 1] * 3; + const i2 = indices[i + 2] * 3; + + const p0 = new Cesium.Cartesian3(positions[i0], positions[i0 + 1], positions[i0 + 2]); + const p1 = new Cesium.Cartesian3(positions[i1], positions[i1 + 1], positions[i1 + 2]); + const p2 = new Cesium.Cartesian3(positions[i2], positions[i2 + 1], positions[i2 + 2]); + + const cross = Cesium.Cartesian3.cross( + Cesium.Cartesian3.subtract(p1, p0, new Cesium.Cartesian3()), + Cesium.Cartesian3.subtract(p2, p0, new Cesium.Cartesian3()), + new Cesium.Cartesian3() + ); + area += Cesium.Cartesian3.magnitude(cross) * 0.5; + } + + // 将像素尺寸转换为实际尺寸(平方米) + const textureWidthMeters = textureSize.width * meterPerPixel; + const textureHeightMeters = textureSize.height * meterPerPixel; + const textureArea = textureWidthMeters * textureHeightMeters; + + // 计算各轴向重复次数 + const repeatX = Math.sqrt(area) / textureWidthMeters; + const repeatY = Math.sqrt(area) / textureHeightMeters; + + return new Cesium.Cartesian2(Math.max(1, Math.ceil(repeatX)), 1.0); + } + swapLastElements(arr1, arr2) { + const last = arr1[arr1.length - 1] + const first = arr2[0] + arr1[arr1.length - 1] = first + arr2[0] = last + + return [arr1, arr2]; + } + createLineBufferPolygonSide(positions, width) { + let area = [] + for (let i = 0; i < positions.length; i++) { + const posi = positions[i]; + + const dir = Cesium.Cartesian3.subtract(posi[1], posi[0], new Cesium.Cartesian3()); + Cesium.Cartesian3.normalize(dir, dir); + + // 获取垂直向量(基于Z轴) + const perp = Cesium.Cartesian3.cross(dir, Cesium.Cartesian3.UNIT_Z, new Cesium.Cartesian3()); + Cesium.Cartesian3.normalize(perp, perp); + + // 生成偏移向量 + const offset = Cesium.Cartesian3.multiplyByScalar(perp, width, new Cesium.Cartesian3()); + let point1 = Cesium.Cartesian3.add(posi[0], offset, new Cesium.Cartesian3()) + let point3 = Cesium.Cartesian3.add(posi[1], offset, new Cesium.Cartesian3()) + + // i == positions.length - 2 ? area.push(start, point1, end, point3) : area.push(start, point1) + area.push([posi[0], point1, point3, posi[1]]) + } + // let arr = [] + // for (let i = 0; i < area.length - 2; i += 2) { + // arr.push([area[i], area[i + 1], area[i + 3], area[i + 2]]) + // } + return area + } + createLineBufferPolygon2(positions, width) { + let area = [] + let leftPositions = []; + let rightPositions = []; + + for (let i = 0; i < positions.length - 1; i++) { + const start = positions[i]; + // const end = positions[i + 1] || positions[i - 1]; + const end = positions[i + 1]; + + const dir = Cesium.Cartesian3.subtract(end, start, new Cesium.Cartesian3()); + Cesium.Cartesian3.normalize(dir, dir); + + // 获取垂直向量(基于Z轴) + const perp = Cesium.Cartesian3.cross(dir, Cesium.Cartesian3.UNIT_Z, new Cesium.Cartesian3()); + Cesium.Cartesian3.normalize(perp, perp); + + const dir2 = Cesium.Cartesian3.subtract(start, end, new Cesium.Cartesian3()); + Cesium.Cartesian3.normalize(dir2, dir2); + + // 获取垂直向量(基于Z轴) + const perp2 = Cesium.Cartesian3.cross(dir2, Cesium.Cartesian3.UNIT_Z, new Cesium.Cartesian3()); + Cesium.Cartesian3.normalize(perp2, perp2); + + + + + // 生成偏移向量 + const offset = Cesium.Cartesian3.multiplyByScalar(perp, width, new Cesium.Cartesian3()); + const offset2 = Cesium.Cartesian3.multiplyByScalar(perp, -width, new Cesium.Cartesian3()); + + const offsetEnd = Cesium.Cartesian3.multiplyByScalar(perp2, -width, new Cesium.Cartesian3()); + const offsetEnd2 = Cesium.Cartesian3.multiplyByScalar(perp2, width, new Cesium.Cartesian3()); + + let point1 = Cesium.Cartesian3.add(start, offset, new Cesium.Cartesian3()) + let point2 = Cesium.Cartesian3.add(start, offset2, new Cesium.Cartesian3()) + let point3 = Cesium.Cartesian3.add(end, offsetEnd, new Cesium.Cartesian3()) + let point4 = Cesium.Cartesian3.add(end, offsetEnd2, new Cesium.Cartesian3()) + + area.push([point1, point3, point4, point2]) + + rightPositions.push([point1, point3]) + leftPositions.push([point2, point4]) + + // if (i == positions.length - 2) { + // area.push(point1, point2, point3, point4) + // rightPositions.push(point1) + // leftPositions.push(point2) + // leftPositions.push(point4) + // rightPositions.push(point3) + // } else { + // area.push(point1, point2) + // rightPositions.push(point1) + // leftPositions.push(point2) + // } + } + // let arr = [] + // for (let i = 0; i < area.length - 2; i += 2) { + // arr.push([area[i], area[i + 1], area[i + 3], area[i + 2]]) + // } + console.log(area, rightPositions, 'rightPositions') + let that = this + // return [arr, rightPositions, leftPositions] + return [area, rightPositions, leftPositions] + } + getIntersects(point1, point2, point3, point4) { + let carPoint1 = this.getLonLat(point1) + let carPoint2 = this.getLonLat(point2) + let carPoint3 = this.getLonLat(point3) + let carPoint4 = this.getLonLat(point4) + var line1 = turf.lineString([ + [carPoint1.lon, carPoint1.lat], + [carPoint2.lon, carPoint2.lat] + ]); + var line2 = turf.lineString([ + [carPoint3.lon, carPoint3.lat], + [carPoint4.lon, carPoint4.lat] + ]); + var intersects = turf.lineIntersect(line1, line2); + if (intersects.features.length > 0) { + return Cesium.Cartesian3.fromDegrees(intersects.features[0].geometry.coordinates[0], intersects.features[0].geometry.coordinates[1]) + } + } + getLonLat(point) { + let pointDe = Cesium.Cartographic.fromCartesian(point) + const longitude = Cesium.Math.toDegrees(pointDe.longitude); + const latitude = Cesium.Math.toDegrees(pointDe.latitude); + return { lon: longitude, lat: latitude } + + } + createLineBufferPolygon(viewer, positions, width) { + // 计算每个线段的左右偏移点 + const leftPositions = []; + const rightPositions = []; + + for (let i = 0; i < positions.length; i++) { + const start = positions[i]; + const end = positions[i + 1] || positions[i - 1]; + + // 计算线段方向向量 + const direction = Cesium.Cartesian3.subtract(end, start, new Cesium.Cartesian3()); + // const direction = Cesium.Cartesian3.subtract(start, end, new Cesium.Cartesian3()); + Cesium.Cartesian3.normalize(direction, direction); + + // 计算垂直向量(使用上向量叉积) + const up = Cesium.Cartesian3.UNIT_Z; + const perpendicular = Cesium.Cartesian3.cross(direction, up, new Cesium.Cartesian3()); + Cesium.Cartesian3.normalize(perpendicular, perpendicular); + + // 计算左右偏移点 + const leftOffset = Cesium.Cartesian3.multiplyByScalar( + perpendicular, + width, + new Cesium.Cartesian3() + ); + + if (width > 0) { + rightPositions.unshift(Cesium.Cartesian3.add(start, leftOffset, new Cesium.Cartesian3())); + } else if (width < 0) { + rightPositions.push(Cesium.Cartesian3.add(start, leftOffset, new Cesium.Cartesian3())); + } + + } + return rightPositions + } + //计算角度 + calculateAangle(arr) { + // let fromDegreesArray = that.calSector(that.options.center, that.options.radius, that.options.startAngle, that.options.endAngle, 360, true) + + function getAangle(start, end) { + let rad = Math.PI / 180, + lat1 = start.y * rad, + lat2 = end.y * rad, + lon1 = start.x * rad, + lon2 = end.x * rad; + const a = Math.sin(lon2 - lon1) * Math.cos(lat2); + const b = + Math.cos(lat1) * Math.sin(lat2) - + Math.sin(lat1) * Math.cos(lat2) * Math.cos(lon2 - lon1); + const radians = Math.atan2(a, b) + const degrees = radians % (2 * Math.PI); + let bearing = 450 - ((degrees * 180) / Math.PI < 0 + ? 360 + (degrees * 180) / Math.PI + : (degrees * 180) / Math.PI) - 90; + return 360 - (bearing % 360) + } + + let center = arr[0] + let pos84_1 = arr[1] + let pos84_2 = arr[2] + + let start = { x: center.lng, y: center.lat } + let end1 = { x: pos84_1.lng, y: pos84_1.lat } + let end2 = { x: pos84_2.lng, y: pos84_2.lat } + + let angle1 = getAangle(start, end1) + let angle2 = getAangle(start, end2) + + return { + angle1, + angle2 + } + } + + get carRoadWidth() { + return this.options.carRoadWidth + } + + set carRoadWidth(v) { + this.options.carRoadWidth = v + Road.create(this) + + } + get sideWidth() { + return this.options.sideWidth + } + set sideWidth(v) { + this.options.sideWidth = v + Road.create(this) + } + /** + * @description 编辑框 + * @param state=false {boolean} 状态: true打开, false关闭 + */ + async edit(state = false) { + let _this = this + this.originalOptions = this.deepCopyObj(this.options) + + if (this._DialogObject && this._DialogObject.close) { + this._DialogObject.close() + this._DialogObject = null + } + + if (state) { + this._DialogObject = await new Dialog(this.sdk, this.originalOptions, { + title: '道路属性', left: '180px', top: '100px', + confirmCallBack: (options) => { + this.name = this.name.trim() + if (!this.name) { + this.name = '道路' + } + this.originalOptions = this.deepCopyObj(this.options) + this._DialogObject.close() + this.Dialog.confirmCallBack && this.Dialog.confirmCallBack(this.originalOptions) + syncData(this.sdk, this.options.id) + syncSplitData(this.sdk, this.options.id) + }, + resetCallBack: () => { + this.reset() + this.Dialog.resetCallBack && this.Dialog.resetCallBack() + }, + closeCallBack: () => { + this.reset() + this.Dialog.closeCallBack && this.Dialog.closeCallBack() + }, + showCallBack: (show) => { + this.show = show + this.Dialog.showCallBack && this.Dialog.showCallBack() + } + }, true) + this._DialogObject._element.body.className = this._DialogObject._element.body.className + ' road-surface' + let contentElm = document.createElement('div'); + contentElm.innerHTML = html() + this._DialogObject.contentAppChild(contentElm) + + + // 下拉选项 + // let heightModeData = [ + // { + // name: '海拔高度', + // value: '海拔高度', + // key: '0', + // }, + // { + // name: '相对地表', + // value: '相对地表', + // key: '1', + // }, + // { + // name: '依附模型', + // value: '依附模型', + // key: '2', + // } + // ] + // let heightModeObject = legp( + // this._DialogObject._element.content.getElementsByClassName( + // 'road-box' + // )[0], + // '.road-type' + // ) + // if (heightModeObject) { + // heightModeObject.legp_search(heightModeData) + // let heightModeDataLegpElm = this._DialogObject._element.content + // .getElementsByClassName('road-type')[0] + // .getElementsByTagName('input')[0] + // for (let i = 0; i < heightModeData.length; i++) { + // if (heightModeData[i].key == this.heightMode) { + // heightModeDataLegpElm.value = heightModeData[i].value + // heightModeObject.legp_searchActive( + // heightModeData[i].value + // ) + // break + // } + // } + // heightModeDataLegpElm.addEventListener('input', () => { + // for (let i = 0; i < heightModeData.length; i++) { + // if (heightModeData[i].value === heightModeDataLegpElm.value) { + // this.heightMode = heightModeData[i].key + // break + // } + // } + // }) + + + // this._elms.height = heightElm + // this._elms.heightBox = heightBoxElm + // this._elms.heightMode = heightModeDataLegpElm + // this._elms.heightConfirm = heightConfirmElm + // this._elms.heightModeObject = heightModeObject + + // heightConfirmElm.addEventListener('click', () => { + // this.positionEditing = false + // for (let i = 0; i < this.options.positions.length; i++) { + // this.options.positions[i].alt = Number((this.options.positions[i].alt + Number(heightElm.value)).toFixed(2)) + // this._elms.alt[i].innerHTML = this.options.positions[i].alt + // } + // let fromDegreesArray = this.renewPositions(this.options.positions) + // this.entity.polyline.positions = Cesium.Cartesian3.fromDegreesArrayHeights( + // fromDegreesArray + // ) + + // this.positionEditing = false + // PolylineObject.closeNodeEdit(this) + // }) + // } + + + + + + let all_elm = contentElm.getElementsByTagName("*") + this._EventBinding.on(this, all_elm) + this._elms = this._EventBinding.element + } else { + // if (this._element_style) { + // document.getElementsByTagName('head')[0].removeChild(this._element_style) + // this._element_style = null + // } + // if (this._DialogObject && this._DialogObject.remove) { + // this._DialogObject.remove() + // this._DialogObject = null + // } + } + } + + reset() { + if (!this.viewer.entities.getById(this.options.id)) { + return + } + this.name = this.originalOptions.name + this.carRoadWidth = this.originalOptions.carRoadWidth + this.sideWidth = this.originalOptions.sideWidth + this.positions = this.originalOptions.positions + this.roadImage = this.originalOptions.roadImage + this.sideImage = this.originalOptions.sideImage + } + + /** + * 飞到对应实体 + */ + async flyTo(options = {}) { + setActiveViewer(0) + closeRotateAround(this.sdk) + closeViewFollow(this.sdk) + + if (this.options.customView && this.options.customView.relativePosition && this.options.customView.orientation) { + let orientation = { + heading: Cesium.Math.toRadians(this.options.customView.orientation.heading || 0.0), + pitch: Cesium.Math.toRadians(this.options.customView.orientation.pitch || -60.0), + roll: Cesium.Math.toRadians(this.options.customView.orientation.roll || 0.0) + } + + let lng = this.options.customView.relativePosition.lng + let lat = this.options.customView.relativePosition.lat + let alt = this.options.customView.relativePosition.alt + let destination = Cesium.Cartesian3.fromDegrees(lng, lat, alt) + + let position = { lng: 0, lat: 0 } + if (this.options.position) { + position = { ...this.options.position } + } + else if (this.options.positions) { + position = { ...this.options.positions[0] } + } + else if (this.options.center) { + position = { ...this.options.center } + } + else if (this.options.start) { + position = { ...this.options.start } + } + else { + if (this.options.hasOwnProperty('lng')) { + position.lng = this.options.lng + } + if (this.options.hasOwnProperty('lat')) { + position.lat = this.options.lat + } + if (this.options.hasOwnProperty('alt')) { + position.alt = this.options.alt + } + } + // 如果没有高度值,则获取紧贴高度计算 + // if (!position.hasOwnProperty('alt')) { + // position.alt = await this.getClampToHeight(position) + // } + lng = this.options.customView.relativePosition.lng + position.lng + lat = this.options.customView.relativePosition.lat + position.lat + alt = this.options.customView.relativePosition.alt + position.alt + destination = Cesium.Cartesian3.fromDegrees(lng, lat, alt) + this.sdk.viewer.camera.flyTo({ + destination: destination, + orientation: orientation + }) + } + else { + let positionArray = [] + for (let i = 0; i < this.positions.length; i++) { + let a = Cesium.Cartesian3.fromDegrees( + this.positions[i][0], + this.positions[i][1], + this.options.height + this.options.heightDifference / 2 + ) + positionArray.push(a.x, a.y, a.z) + } + let BoundingSphere = Cesium.BoundingSphere.fromVertices(positionArray) + this.viewer.camera.flyToBoundingSphere(BoundingSphere, { + offset: { + heading: Cesium.Math.toRadians(0.0), + pitch: Cesium.Math.toRadians(-20.0), + roll: Cesium.Math.toRadians(0.0) + } + }) + + } + } + + getSphere() { + return new Promise((resolve) => { + // entity没有加载完成时 state 不会等于0 所以设置定时器直到获取到为止 + const interval = setInterval(() => { + const sphere = new Cesium.BoundingSphere() + const state = this.sdk.viewer._dataSourceDisplay.getBoundingSphere( + this.viewer.entities.getById(this.options.id), + false, + sphere + ) + if (state === Cesium.BoundingSphereState.DONE) { + clearInterval(interval) + } + }, 1000) + }) + } + + /** + * 删除 + */ + async remove() { + this.positions = [] + this.lineEntity = null + + if (this.viewer.entities.getById(this.options.id)) { + this.viewer.entities.getById(this.options.id)._children.forEach((item) => { + this.viewer.entities.remove(item); + }); + this.viewer.entities.remove(this.viewer.entities.getById(this.options.id)) + } + + if (this._DialogObject && !this._DialogObject.isDestroy) { + this._DialogObject.close() + this._DialogObject = null + } + await this.sdk.removeIncetance(this.options.id) + await syncData(this.sdk, this.options.id) + } + + flicker() { } +} + +export default Road diff --git a/src/Obj/Base/RoadObject/index.js b/src/Obj/Base/RoadObject/index.js new file mode 100644 index 0000000..8c58a0b --- /dev/null +++ b/src/Obj/Base/RoadObject/index.js @@ -0,0 +1,1207 @@ +/** + * @description 道路 + */ +import Dialog from '../../Element/Dialog'; +import { html } from "./_element"; +import EventBinding from '../../Element/Dialog/eventBinding'; +import Base from "../index"; +import { syncData } from '../../../Global/MultiViewportMode' +import { setSplitDirection, syncSplitData, setActiveId } from '../../../Global/SplitScreen' +import { setActiveViewer, closeRotateAround, closeViewFollow } from '../../../Global/global' + +class Road extends Base { + /** + * @constructor + * @param sdk + * @description 道路 + * @param options {object} 道路属性 + * @param options.name=未命名对象 {string} 名称 + * @param options.carRoadWidth=2 {number} 车道宽度 + * @param options.sideWidth=2 {number} 人行道宽度 + * @param options.positions=[] {array} 道路positions + * @param options.roadImage='' {string} 车道贴图 + * @param options.sideImage='' {string} 人行道贴图 + * @param Dialog {object} 弹框对象 + * @param Dialog.confirmCallBack {function} 弹框确认时的回调 + * */ + constructor(sdk, options = {}, _Dialog = {}) { + super(sdk, options); + this.viewer = this.sdk.viewer + this.options.name = options.name || '道路' + this.options.carRoadWidth = options.carRoadWidth || 10 + this.options.sideWidth = options.sideWidth || 5 + this.options.positions = options.positions || [] + this.options.roadImage = options.roadImage || (this.getSourceRootPath() + '/img/roadPhoto.png') + this.options.sideImage = options.sideImage || (this.getSourceRootPath() + '/img/sidePhoto.png') + this.options.show = (options.show || options.show === false) ? options.show : true + this.Dialog = _Dialog + this._EventBinding = new EventBinding() + this._elms = {}; + this.positionArea = [] + this.positions = [] + this.lineEntity = '' + this.crossArr = [] + this.pointArr = [] + + this.sdk.addIncetance(this.options.id, this) + // Road.create(this) + // console.log('1212') + function createCustomCorridor(viewer, positions, width) { + // 计算两侧顶点位置 + const leftPositions = []; + const rightPositions = []; + const topPositions = []; + + // 生成两侧和顶部顶点 + for (let i = 0; i < positions.length; i++) { + const position = positions[i]; + const nextPosition = positions[i + 1]; + + if (!nextPosition) continue; + + // 计算方向向量 + const direction = Cesium.Cartesian3.subtract( + nextPosition, + position, + new Cesium.Cartesian3() + ); + Cesium.Cartesian3.normalize(direction, direction); + + // 计算垂直向量 + const normal = Cesium.Cartesian3.cross( + direction, + Cesium.Cartesian3.UNIT_Z, + new Cesium.Cartesian3() + ); + Cesium.Cartesian3.normalize(normal, normal); + + // 计算两侧偏移量 + const offset = Cesium.Cartesian3.multiplyByScalar( + normal, + width / 2, + new Cesium.Cartesian3() + ); + + // 左侧点 + const left = Cesium.Cartesian3.add(position, offset, new Cesium.Cartesian3()); + leftPositions.push(left.x, left.y, left.z); + + // 右侧点 + const right = Cesium.Cartesian3.subtract(position, offset, new Cesium.Cartesian3()); + rightPositions.push(right.x, right.y, right.z); + + // 顶部点(高度增加) + const top = Cesium.Cartesian3.clone(position); + top.z += 100; // 设置顶部高度 + topPositions.push(top.x, top.y, top.z); + } + + // 合并所有顶点 + const allPositions = new Float64Array([ + ...leftPositions, + ...rightPositions, + ...topPositions + ]); + + // 创建几何体 + const geometry = new Cesium.Geometry({ + attributes: { + position: new Cesium.GeometryAttribute({ + componentDatatype: Cesium.ComponentDatatype.DOUBLE, + componentsPerAttribute: 3, + values: allPositions + }) + }, + indices: new Uint16Array([ + // 左侧面索引 + 0, 1, 3, + 1, 4, 3, + // 右侧面索引 + 2, 5, 6, + 2, 6, 7, + // 顶面索引 + 8, 9, 10, + 8, 10, 11 + ]), + primitiveType: Cesium.PrimitiveType.TRIANGLES, + boundingSphere: Cesium.BoundingSphere.fromVertices(allPositions) + }); + + // 创建图元 + const primitive = new Cesium.Primitive({ + geometryInstances: new Cesium.GeometryInstance({ + geometry: geometry + }), + appearance: new Cesium.PerInstanceColorAppearance({ + flat: true, + translucent: false + }), + asynchronous: false + }); + + viewer.scene.primitives.add(primitive); + return primitive; + } + + // 使用示例 + const positions = Cesium.Cartesian3.fromDegreesArray([ + -75.0, 39.0, + -74.5, 39.5, + -74.0, 40.0 + ]); + createCustomCorridor(this.sdk.viewer, positions, 1000); + this.sdk.viewer.camera.flyTo({ + destination: Cesium.Cartesian3.fromDegrees(-75.0, 39.0, 100), + // orientation: { + // heading: Cesium.Math.toRadians(0), + // pitch: Cesium.Math.toRadians(-30), + // roll: 0.0 + // } + }); + + + } + // 创建走廊几何体 + createCorridor(myImg) { + const positions = Cesium.Cartesian3.fromDegreesArray([ + 116.391, 39.907, // 北京 + 116.404, 39.914, + 116.417, 39.921, + 116.430, 39.928 + ]); + + const corridorInstance = new Cesium.GeometryInstance({ + geometry: new Cesium.CorridorGeometry({ + positions: positions, + width: 200.0, + vertexFormat: Cesium.VertexFormat.POSITION_AND_ST, + cornerType: Cesium.CornerType.ROUNDED + }), + attributes: { + color: Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.WHITE) + } + }); + + const primitive = new Cesium.Primitive({ + geometryInstances: corridorInstance, + appearance: new Cesium.MaterialAppearance({ + material: new Cesium.Material({ + fabric: { + type: 'Image', + uniforms: { + image: myImg + } + } + }), + translucent: false + }), + asynchronous: false + }); + + this.sdk.viewer.scene.primitives.add(primitive); + + // 定位到走廊 + this.sdk.viewer.camera.flyTo({ + destination: Cesium.Cartesian3.fromDegrees(116.404, 39.914, 2000), + orientation: { + heading: Cesium.Math.toRadians(0), + pitch: Cesium.Math.toRadians(-30), + roll: 0.0 + } + }); + + return primitive; + } + // 创建道路 + static create(that) { + let positions = [] + that.options.positions.forEach(v => { + positions.push(new Cesium.Cartesian3.fromDegrees(v.lng, v.lat, v.alt)) + }) + let newPosi = [] + for (let i = 0; i < positions.length - 1; i++) { + const start = positions[i]; + const end = positions[i + 1]; + newPosi.push([start, end]) + that.pointArr = newPosi + } + + let area = [[], [], []] + + // area[1] = that.getRectangle(positions, that.options.carRoadWidth) + area[1][0] = that.getRectangle(newPosi, that.options.carRoadWidth) + let sideArr = that.getSideRectangle(area[1][0], that.options.sideWidth) + area[0] = sideArr.left + area[2] = sideArr.right + // area[1] = that.createLineBufferPolygon2(positions, that.options.carRoadWidth / 2) + // area[1] = newPositions + // area[0] = that.createLineBufferPolygonSide(area[1][2], -that.options.sideWidth) + // area[2] = that.createLineBufferPolygonSide(area[1][1], that.options.sideWidth) + + //判断道路边是否相交 + for (let i = 0; i < area[0].length - 1; i++) { + + let leftItem = area[0][i] + let leftItem2 = area[0][i + 1] + let rightItem = area[2][i] + let rightItem2 = area[2][i + 1] + let carItem = area[1][0][i] + let carItem2 = area[1][0][i + 1] + let leftLine = that.getIntersects(leftItem[2], leftItem[3], leftItem2[2], leftItem2[3]) + let rightLine = that.getIntersects(rightItem[0], rightItem[1], rightItem2[0], rightItem2[1]) + if (!leftLine && !rightLine) { + for (let index = 0; index < 4; index++) { + let positions = [] + index === 0 ? positions.push(leftItem[2], leftItem[3]) : index === 1 ? positions.push(leftItem2[2], leftItem2[3]) : index === 2 ? positions.push(rightItem[0], rightItem[1]) : positions.push(rightItem2[0], rightItem2[1]) + that.sdk.viewer.entities.add({ + polyline: { + positions: positions, + width: 10.0, + material: new Cesium.PolylineGlowMaterialProperty({ + color: index === 0 ? Cesium.Color.RED : index === 1 ? Cesium.Color.BLUE : index === 2 ? Cesium.Color.YELLOW : Cesium.Color.GREEN, + glowPower: 0.25, + }), + }, + }); + + } + + } + + console.log(leftLine, rightLine, 'leftLine') + if (leftLine) {//左侧相交 + //获取右侧延长交点 + let point1 = that.getExtendPoint(rightItem[0], rightItem[1], 1000) + let point2 = that.getExtendPoint(rightItem2[1], rightItem2[0], 1000) + console.log('aaaa') + let rightIntersection = that.getIntersects(rightItem[0], point1, rightItem2[1], point2) + //将其他几条边都延长 + let leftLineNeiPoint = that.getExtendPoint(leftItem[0], leftItem[1], 1000) + let carLeftPoint = that.getExtendPoint(carItem[3], carItem[2], 1000) + let carRightPoint = that.getExtendPoint(carItem[0], carItem[1], 1000) + + let rightLineNeiPoint = that.getExtendPoint(rightItem[3], rightItem[2], 1000) + + + + console.log(leftLine, rightIntersection, leftItem[0], leftLineNeiPoint, 'bbbb') + //跟左侧里相交点 + let leftLineNei = that.getIntersects(leftLine, rightIntersection, leftItem[0], leftLineNeiPoint) + console.log(leftLineNei, 'leftLineNei') + //跟车道左侧相交点 + let carLeft = that.getIntersects(leftLine, rightIntersection, carItem[3], carLeftPoint) + + //跟车道右侧相交点 + let carRight = that.getIntersects(leftLine, rightIntersection, carItem[0], carRightPoint) + let rightLineNei = that.getIntersects(leftLine, rightIntersection, rightItem[3], rightLineNeiPoint) + console.log('ccc') + // let leftLineNei = that.getIntersects(leftLine, rightItem[2], leftItem[0], leftItem[3]) + // let carLeft = that.getIntersects(leftLine, rightItem[2], carItem[3], carItem[2]) + // let carRight = that.getIntersects(leftLine, rightItem[2], carItem[0], carItem[1]) + // let rightLineNei = that.getIntersects(leftLine, rightItem[2], rightItem[0], rightItem[3]) + + // let leftLineNei = that.getIntersects(leftLine, intersection, leftItem[0], leftItem[3]) + // //跟车道左侧相交点 + // let carLeft = that.getIntersects(leftLine, intersection, carItem[3], carItem[2]) + // //跟车道右侧相交点 + // let carRight = that.getIntersects(leftLine, intersection, carItem[0], carItem[1]) + // let rightLineNei = that.getIntersects(leftLine, intersection, rightItem[0], rightItem[3]) + + leftItem[2] = leftLine + leftItem[1] = leftLineNei + carItem[2] = carLeft + carItem[1] = carRight + rightItem[2] = rightLineNei + rightItem[1] = rightIntersection + console.log(leftItem, carItem, rightItem, 'leftItemleft') + + + //将其他几条边都延长 + let leftLineNeiPoint2 = that.getExtendPoint(leftItem2[1], leftItem2[0], 1000) + let carLeftPoint2 = that.getExtendPoint(carItem2[2], carItem2[3], 1000) + let carRightPoint2 = that.getExtendPoint(carItem2[1], carItem2[0], 1000) + let rightLineNeiPoint2 = that.getExtendPoint(rightItem2[2], rightItem2[3], 1000) + + // let leftLineNei2 = that.getIntersects(leftLine, rightItem2[1], leftItem2[0], leftItem2[3]) + // let carLeft2 = that.getIntersects(leftLine, rightItem2[1], carItem2[3], carItem2[2]) + // let carRight2 = that.getIntersects(leftLine, rightItem2[1], carItem2[0], carItem2[1]) + // let rightLineNei2 = that.getIntersects(leftLine, rightItem2[1], rightItem2[0], rightItem2[3]) + + + + let leftLineNei2 = that.getIntersects(leftLine, rightIntersection, leftItem2[1], leftLineNeiPoint2) + let carLeft2 = that.getIntersects(leftLine, rightIntersection, carItem2[2], carLeftPoint2) + let carRight2 = that.getIntersects(leftLine, rightIntersection, carItem2[1], carRightPoint2) + let rightLineNei2 = that.getIntersects(leftLine, rightIntersection, rightItem2[2], rightLineNeiPoint2) + + // let arr = [leftLine, rightIntersection, leftItem2[3], leftLineNeiPoint2] + // arr.forEach((item, index) => { + // that.sdk.viewer.entities.add({ + // name: 'node-secondary-edit-point', + // index: i, + // position: item, + // billboard: { + // image: that.getSourceRootPath() + '/img/point.png', + // width: 15, + // height: 15, + // disableDepthTestDistance: Number.POSITIVE_INFINITY, + // color: Cesium.Color.WHITE.withAlpha(0.99) + // }, + // label: { + // text: '' + index, + // pixelOffset: { x: 0, y: -20 }, + // }, + // }) + // }) + + // let leftLineNei2 = that.getIntersects(leftLine, intersection, leftItem2[0], leftItem2[3]) + // //跟车道左侧相交点 + // let carLeft2 = that.getIntersects(leftLine, intersection, carItem2[3], carItem2[2]) + // //跟车道右侧相交点 + // let carRight2 = that.getIntersects(leftLine, intersection, carItem2[0], carItem2[1]) + // let rightLineNei2 = that.getIntersects(leftLine, intersection, rightItem2[0], rightItem2[3]) + + leftItem2[3] = leftLine + leftItem2[0] = leftLineNei + carItem2[3] = carLeft + carItem2[0] = carRight + rightItem2[3] = rightLineNei + rightItem2[0] = rightIntersection + console.log(leftItem2, carItem2, rightItem2, 'leftItem2left') + + } else if (rightLine) {//右侧相交 + + //获取左侧延长交点 + let point1 = that.getExtendPoint(leftItem[3], leftItem[2], 1000) + let point2 = that.getExtendPoint(leftItem2[2], leftItem2[3], 1000) + let rightIntersection = that.getIntersects(leftItem[3], point1, leftItem2[2], point2) + if (!rightIntersection) { + return + } + //将其他几条边都延长 + let leftLineNeiPoint = that.getExtendPoint(leftItem[0], leftItem[1], 1000) + let carLeftPoint = that.getExtendPoint(carItem[3], carItem[2], 1000) + let carRightPoint = that.getExtendPoint(carItem[0], carItem[1], 1000) + let rightLineNeiPoint = that.getExtendPoint(rightItem[3], rightItem[2], 1000) + + // //跟左侧里相交点 + let leftLineNei = that.getIntersects(rightLine, rightIntersection, leftItem[0], leftLineNeiPoint) + //跟车道左侧相交点 + let carLeft = that.getIntersects(rightLine, rightIntersection, carItem[3], carLeftPoint) + //跟车道右侧相交点 + let carRight = that.getIntersects(rightLine, rightIntersection, carItem[0], carRightPoint) + let rightLineNei = that.getIntersects(rightLine, rightIntersection, rightItem[3], rightLineNeiPoint) + // //跟左侧里相交点 + // let leftLineNei = that.getIntersects(rightLine, leftItem[2], leftItem[0], leftItem[3]) + // //跟车道左侧相交点 + // let carLeft = that.getIntersects(rightLine, leftItem[2], carItem[3], carItem[2]) + // //跟车道右侧相交点 + // let carRight = that.getIntersects(rightLine, leftItem[2], carItem[0], carItem[1]) + // let rightLineNei = that.getIntersects(rightLine, leftItem[2], rightItem[0], rightItem[3]) + + + leftItem[2] = rightIntersection + leftItem[1] = leftLineNei + carItem[2] = carLeft + carItem[1] = carRight + rightItem[2] = rightLineNei + rightItem[1] = rightLine + console.log(leftItem, carItem, rightItem, 'leftItemright') + + //将其他几条边都延长 + let leftLineNeiPoint2 = that.getExtendPoint(leftItem2[2], leftItem2[3], 1000) + let carLeftPoint2 = that.getExtendPoint(carItem2[2], carItem2[3], 1000) + let carRightPoint2 = that.getExtendPoint(carItem2[1], carItem2[0], 1000) + let rightLineNeiPoint2 = that.getExtendPoint(rightItem2[2], rightItem2[3], 1000) + + let leftLineNei2 = that.getIntersects(rightLine, rightIntersection, leftItem2[1], leftLineNeiPoint2) + //跟车道左侧相交点 + let carLeft2 = that.getIntersects(rightLine, rightIntersection, carItem2[2], carLeftPoint2) + //跟车道右侧相交点 + let carRight2 = that.getIntersects(rightLine, rightIntersection, carItem2[1], carRightPoint2) + let rightLineNei2 = that.getIntersects(rightLine, rightIntersection, rightItem2[2], rightLineNeiPoint2) + // let leftLineNei2 = that.getIntersects(rightLine, leftItem2[1], leftItem2[0], leftItem2[3]) + // //跟车道左侧相交点 + // let carLeft2 = that.getIntersects(rightLine, leftItem2[1], carItem2[3], carItem2[2]) + // //跟车道右侧相交点 + // let carRight2 = that.getIntersects(rightLine, leftItem2[1], carItem2[0], carItem2[1]) + // let rightLineNei2 = that.getIntersects(rightLine, leftItem2[1], rightItem2[0], rightItem2[3]) + + leftItem2[3] = rightIntersection + leftItem2[0] = leftLineNei + carItem2[3] = carLeft + carItem2[0] = carRight + rightItem2[3] = rightLineNei + rightItem2[0] = rightLine + console.log(leftItem2, carItem2, rightItem2, 'leftItem2right') + + } + } + + if (that.viewer.entities.getById(that.options.id)) { + that.viewer.entities.getById(that.options.id)._children.forEach((item) => { + that.viewer.entities.remove(item); + }); + that.viewer.entities.remove(that.viewer.entities.getById(that.options.id)) + } + that.lineEntity = that.viewer.entities.add(new Cesium.Entity({ id: that.options.id, show: that.options.show })) + + const myImg = new Image() + myImg.src = that.options.roadImage + myImg.onload = function () { + console.log(area[1][0], that.options.roadImage, 'llll') + area[1][0].forEach((item, index) => { + that.viewer.entities.add({ + // id: that.options.id, + parent: that.lineEntity, + polygon: { + hierarchy: new Cesium.PolygonHierarchy(item), + material: new Cesium.ImageMaterialProperty({ + image: that.options.roadImage, + transparent: true,// 如果图片有透明部分,需要设置为 true + repeat: that.calculateTextureRepeat(item, myImg) + }), + stRotation: that.calculateRoadAngle(positions[index], positions[index + 1]) + } + }); + }) + } + + const myImg2 = new Image() + myImg2.src = that.options.sideImage + myImg2.onload = function () { + // area[0].forEach((item, index) => { + area[0].forEach((item, index) => { + that.viewer.entities.add({ + parent: that.lineEntity, + polygon: { + hierarchy: new Cesium.PolygonHierarchy(item), + material: new Cesium.ImageMaterialProperty({ + image: that.options.sideImage, + transparent: true,// 如果图片有透明部分,需要设置为 true + repeat: that.calculateTextureRepeat(item, myImg2) + }), + stRotation: that.calculateRoadAngle(positions[index], positions[index + 1]) + } + }); + }) + + // area[2].forEach((item, index) => { + area[2].forEach((item, index) => { + that.viewer.entities.add({ + polygon: { + hierarchy: new Cesium.PolygonHierarchy(item), + material: new Cesium.ImageMaterialProperty({ + image: that.options.sideImage, + transparent: true,// 如果图片有透明部分,需要设置为 true + repeat: that.calculateTextureRepeat(item, myImg2) + }), + stRotation: that.calculateRoadAngle(positions[index], positions[index + 1]) + } + }); + }) + + } + + } + getSideRectangle(positions, width) { + let right = [] + let left = [] + let that = this + positions.forEach(item => { + right.push([item[0], item[1]]) + left.push([item[2], item[3]]) + }) + let rightPosi = that.getRectangle(right, width, 'side') + + let leftPosi = this.getRectangle(left, width, 'side') + return { left: leftPosi, right: rightPosi } + } + getRectangle(positions, width, type) { + let areaArr = [] + let newPositions = [] + let that = this + // for (let i = 0; i < positions.length - 1; i++) { + for (let i = 0; i < positions.length; i++) { + const start = positions[i][0]; + const end = positions[i][1]; + + areaArr[i] = [] + let posi = [] + let Outlinegeometry = new Cesium.CorridorGeometry({ + positions: [start, end], + width: width, + cornerType: Cesium.CornerType.MITERED, + vertexFormat: Cesium.MaterialAppearance.MaterialSupport.ALL.vertexFormat + }) + let geometry = Cesium.CorridorGeometry.createGeometry(Outlinegeometry) + for (let j = 0; j < geometry.attributes.position.values.length; j += 3) { + let val = that.cartesian3Towgs84(new Cesium.Cartesian3(geometry.attributes.position.values[j], geometry.attributes.position.values[j + 1], geometry.attributes.position.values[j + 2]), that.sdk.viewer) + posi.push([val.lng, val.lat]) + } + + for (let x = 0; x < geometry.indices.length; x += 3) { + areaArr[i].push(turf.polygon([[posi[geometry.indices[x]], posi[geometry.indices[x + 1]], posi[geometry.indices[x + 2]], posi[geometry.indices[x]]]])) + } + + let geojson = turf.union(areaArr[i][0], areaArr[i][1]); + let arr = [] + geojson.geometry.coordinates[0].pop() + geojson.geometry.coordinates[0].forEach(item => { + arr.push(new Cesium.Cartesian3.fromDegrees(item[0], item[1])) + }) + let dotResult, angle + const tempVec = new Cesium.Cartesian3(); + + // 计算并归一化第一个向量 + const vector1 = Cesium.Cartesian3.normalize( + Cesium.Cartesian3.subtract(that.pointArr[i][1], that.pointArr[i][0], tempVec), + tempVec + ); + + // 计算并归一化第二个向量 + const vector2 = Cesium.Cartesian3.normalize( + Cesium.Cartesian3.subtract(arr[1], arr[0], new Cesium.Cartesian3()), + new Cesium.Cartesian3() + ); + + dotResult = Cesium.Cartesian3.dot(vector1, vector2); + if ((0.999 < dotResult && dotResult < 1.001) || (-0.999 > dotResult && dotResult > -1.001)) {//调整方向 + newPositions.push(arr) + } else { + let newArr = [] + newArr[0] = arr[1] + newArr[1] = arr[2] + newArr[2] = arr[3] + newArr[3] = arr[0] + newPositions.push(newArr) + } + // } + + // if (!type) { + // // if (cross > 0 && !type) {//调整方向 + // if ((0.999 < dotResult && dotResult < 1.001) || (-0.999 > dotResult && dotResult > -1.001)) {//调整方向 + // newPositions.push(arr) + // } else { + // let newArr = [] + // newArr[0] = arr[1] + // newArr[1] = arr[2] + // newArr[2] = arr[3] + // newArr[3] = arr[0] + // newPositions.push(newArr) + // } + // } else { + // newPositions.push(arr) + // } + } + return newPositions + } + getExtendPoint(position1, position2, distance) { + // let position1 = Cesium.Cartesian3.fromDegrees(p1[0], p1[1], 0); + // let position2 = Cesium.Cartesian3.fromDegrees(p2[0], p2[1], 0); + let pot = Cesium.Cartesian3.subtract(position2, position1, new Cesium.Cartesian3());//方向 + var dir = Cesium.Cartesian3.normalize(pot, new Cesium.Cartesian3());//向量归一化 + + var ray = new Cesium.Ray(position1, dir); + let np = Cesium.Ray.getPoint(ray, distance * 10);//计算延长点 + return np + } + getArr(arr1, arr2) { + arr2 = arr2.reverse() + let polygon = [] + for (let index = 0; index < arr1.length - 1; index++) { + polygon.push([arr1[index], arr1[index + 1], arr2[index + 1], arr2[index]]) + } + return polygon + } + calculateRoadAngle2(startPoint, endPoint) { + // 1. 获取地表法向量 + const normal = this.sdk.viewer.scene.globe.ellipsoid.geodeticSurfaceNormal( + startPoint, new Cesium.Cartesian3()); + + // 2. 构建带椭球参数的ENU矩阵 + const enuMatrix = Cesium.Transforms.eastNorthUpToFixedFrame( + startPoint, this.sdk.viewer.scene.globe.ellipsoid, normal); + const inverseMatrix = Cesium.Matrix4.inverse( + enuMatrix, new Cesium.Matrix4()); + + // 3. 转换坐标并计算相对向量 + const localEnd = Cesium.Matrix4.multiplyByPoint( + inverseMatrix, endPoint, new Cesium.Cartesian3()); + const heightFactor = Math.abs(localEnd.z) / 1000; // 高度差补偿 + + // 4. 使用四象限反正切计算角度 + const angle = Math.atan2(localEnd.y, localEnd.x); + const adjustedAngle = angle - (heightFactor * 0.01); // 高度补偿 + let result = Cesium.Math.toDegrees(adjustedAngle) + console.log(result, 'result') + return result; + } + calculateRoadAngle(startPoint, endPoint) { + // 1. 获取地表法向量 + const normal = Cesium.Ellipsoid.WGS84.geodeticSurfaceNormal(startPoint); + + // 2. 构建精确ENU坐标系 + const enuMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(startPoint, undefined, normal); + const inverseMatrix = Cesium.Matrix4.inverse(enuMatrix, new Cesium.Matrix4()); + + // 3. 转换终点并计算水平向量 + const localEnd = Cesium.Matrix4.multiplyByPoint(inverseMatrix, endPoint, new Cesium.Cartesian3()); + const horizontalVec = new Cesium.Cartesian2(localEnd.x, localEnd.y); + Cesium.Cartesian2.normalize(horizontalVec, horizontalVec); + + const north = new Cesium.Cartesian2(1, 0); + + const angle = Cesium.Cartesian2.angleBetween(north, horizontalVec); + const cross = Cesium.Cartesian2.cross(north, horizontalVec, new Cesium.Cartesian2()); + return cross < 0 ? angle : -angle; + } + + calculatePolygonOrientation(positions) { + + // 假设 position 是 Cesium.Cartesian3 对象,表示地球上的某个点 + var position = positions[0] + // 获取东、北、上坐标系 + var eastNorthUp = Cesium.Transforms.eastNorthUpToFixedFrame(position); + // northAxis 是北方向向量 + var northAxis = eastNorthUp.getColumn(1, new Cesium.Cartesian3()); + Cesium.Cartesian3.normalize(northAxis, northAxis); + + const direction = Cesium.Cartesian3.subtract(positions[0], positions[1], new Cesium.Cartesian3()); + Cesium.Cartesian3.normalize(direction, direction); + + + const dot = Cesium.Cartesian3.dot(northAxis, direction); + const magA = Cesium.Cartesian3.magnitude(northAxis); + const magB = Cesium.Cartesian3.magnitude(direction); + return Math.acos(dot / (magA * magB)); + } + calculateTextureRepeat(polygonPositions, textureSize, meterPerPixel = 0.01) { + // 验证纹理尺寸 + if (!textureSize.width || !textureSize.height) { + throw new Error('Texture size must contain width and height in pixels'); + } + + // 创建多边形几何体 + const geometry = Cesium.PolygonGeometry.createGeometry( + new Cesium.PolygonGeometry({ + polygonHierarchy: new Cesium.PolygonHierarchy(polygonPositions), + vertexFormat: Cesium.VertexFormat.POSITION_ONLY + }) + ); + + // 计算多边形面积(平方米) + let area = 0; + const indices = geometry.indices; + const positions = geometry.attributes.position.values; + for (let i = 0; i < indices.length; i += 3) { + const i0 = indices[i] * 3; + const i1 = indices[i + 1] * 3; + const i2 = indices[i + 2] * 3; + + const p0 = new Cesium.Cartesian3(positions[i0], positions[i0 + 1], positions[i0 + 2]); + const p1 = new Cesium.Cartesian3(positions[i1], positions[i1 + 1], positions[i1 + 2]); + const p2 = new Cesium.Cartesian3(positions[i2], positions[i2 + 1], positions[i2 + 2]); + + const cross = Cesium.Cartesian3.cross( + Cesium.Cartesian3.subtract(p1, p0, new Cesium.Cartesian3()), + Cesium.Cartesian3.subtract(p2, p0, new Cesium.Cartesian3()), + new Cesium.Cartesian3() + ); + area += Cesium.Cartesian3.magnitude(cross) * 0.5; + } + + // 将像素尺寸转换为实际尺寸(平方米) + const textureWidthMeters = textureSize.width * meterPerPixel; + const textureHeightMeters = textureSize.height * meterPerPixel; + const textureArea = textureWidthMeters * textureHeightMeters; + + // 计算各轴向重复次数 + const repeatX = Math.sqrt(area) / textureWidthMeters; + const repeatY = Math.sqrt(area) / textureHeightMeters; + + return new Cesium.Cartesian2(Math.max(1, Math.ceil(repeatX)), 1.0); + } + swapLastElements(arr1, arr2) { + const last = arr1[arr1.length - 1] + const first = arr2[0] + arr1[arr1.length - 1] = first + arr2[0] = last + + return [arr1, arr2]; + } + createLineBufferPolygonSide(positions, width) { + let area = [] + for (let i = 0; i < positions.length; i++) { + const posi = positions[i]; + + const dir = Cesium.Cartesian3.subtract(posi[1], posi[0], new Cesium.Cartesian3()); + Cesium.Cartesian3.normalize(dir, dir); + + // 获取垂直向量(基于Z轴) + const perp = Cesium.Cartesian3.cross(dir, Cesium.Cartesian3.UNIT_Z, new Cesium.Cartesian3()); + Cesium.Cartesian3.normalize(perp, perp); + + // 生成偏移向量 + const offset = Cesium.Cartesian3.multiplyByScalar(perp, width, new Cesium.Cartesian3()); + let point1 = Cesium.Cartesian3.add(posi[0], offset, new Cesium.Cartesian3()) + let point3 = Cesium.Cartesian3.add(posi[1], offset, new Cesium.Cartesian3()) + + // i == positions.length - 2 ? area.push(start, point1, end, point3) : area.push(start, point1) + area.push([posi[0], point1, point3, posi[1]]) + } + // let arr = [] + // for (let i = 0; i < area.length - 2; i += 2) { + // arr.push([area[i], area[i + 1], area[i + 3], area[i + 2]]) + // } + return area + } + createLineBufferPolygon2(positions, width) { + let area = [] + let leftPositions = []; + let rightPositions = []; + + for (let i = 0; i < positions.length - 1; i++) { + const start = positions[i]; + // const end = positions[i + 1] || positions[i - 1]; + const end = positions[i + 1]; + + const dir = Cesium.Cartesian3.subtract(end, start, new Cesium.Cartesian3()); + Cesium.Cartesian3.normalize(dir, dir); + + // 获取垂直向量(基于Z轴) + const perp = Cesium.Cartesian3.cross(dir, Cesium.Cartesian3.UNIT_Z, new Cesium.Cartesian3()); + Cesium.Cartesian3.normalize(perp, perp); + + const dir2 = Cesium.Cartesian3.subtract(start, end, new Cesium.Cartesian3()); + Cesium.Cartesian3.normalize(dir2, dir2); + + // 获取垂直向量(基于Z轴) + const perp2 = Cesium.Cartesian3.cross(dir2, Cesium.Cartesian3.UNIT_Z, new Cesium.Cartesian3()); + Cesium.Cartesian3.normalize(perp2, perp2); + + + + + // 生成偏移向量 + const offset = Cesium.Cartesian3.multiplyByScalar(perp, width, new Cesium.Cartesian3()); + const offset2 = Cesium.Cartesian3.multiplyByScalar(perp, -width, new Cesium.Cartesian3()); + + const offsetEnd = Cesium.Cartesian3.multiplyByScalar(perp2, -width, new Cesium.Cartesian3()); + const offsetEnd2 = Cesium.Cartesian3.multiplyByScalar(perp2, width, new Cesium.Cartesian3()); + + let point1 = Cesium.Cartesian3.add(start, offset, new Cesium.Cartesian3()) + let point2 = Cesium.Cartesian3.add(start, offset2, new Cesium.Cartesian3()) + let point3 = Cesium.Cartesian3.add(end, offsetEnd, new Cesium.Cartesian3()) + let point4 = Cesium.Cartesian3.add(end, offsetEnd2, new Cesium.Cartesian3()) + + area.push([point1, point3, point4, point2]) + + rightPositions.push([point1, point3]) + leftPositions.push([point2, point4]) + + // if (i == positions.length - 2) { + // area.push(point1, point2, point3, point4) + // rightPositions.push(point1) + // leftPositions.push(point2) + // leftPositions.push(point4) + // rightPositions.push(point3) + // } else { + // area.push(point1, point2) + // rightPositions.push(point1) + // leftPositions.push(point2) + // } + } + // let arr = [] + // for (let i = 0; i < area.length - 2; i += 2) { + // arr.push([area[i], area[i + 1], area[i + 3], area[i + 2]]) + // } + console.log(area, rightPositions, 'rightPositions') + let that = this + // return [arr, rightPositions, leftPositions] + return [area, rightPositions, leftPositions] + } + getIntersects(point1, point2, point3, point4) { + let carPoint1 = this.getLonLat(point1) + let carPoint2 = this.getLonLat(point2) + let carPoint3 = this.getLonLat(point3) + let carPoint4 = this.getLonLat(point4) + var line1 = turf.lineString([ + [carPoint1.lon, carPoint1.lat], + [carPoint2.lon, carPoint2.lat] + ]); + var line2 = turf.lineString([ + [carPoint3.lon, carPoint3.lat], + [carPoint4.lon, carPoint4.lat] + ]); + var intersects = turf.lineIntersect(line1, line2); + if (intersects.features.length > 0) { + return Cesium.Cartesian3.fromDegrees(intersects.features[0].geometry.coordinates[0], intersects.features[0].geometry.coordinates[1]) + } + } + getLonLat(point) { + let pointDe = Cesium.Cartographic.fromCartesian(point) + const longitude = Cesium.Math.toDegrees(pointDe.longitude); + const latitude = Cesium.Math.toDegrees(pointDe.latitude); + return { lon: longitude, lat: latitude } + + } + createLineBufferPolygon(viewer, positions, width) { + // 计算每个线段的左右偏移点 + const leftPositions = []; + const rightPositions = []; + + for (let i = 0; i < positions.length; i++) { + const start = positions[i]; + const end = positions[i + 1] || positions[i - 1]; + + // 计算线段方向向量 + const direction = Cesium.Cartesian3.subtract(end, start, new Cesium.Cartesian3()); + // const direction = Cesium.Cartesian3.subtract(start, end, new Cesium.Cartesian3()); + Cesium.Cartesian3.normalize(direction, direction); + + // 计算垂直向量(使用上向量叉积) + const up = Cesium.Cartesian3.UNIT_Z; + const perpendicular = Cesium.Cartesian3.cross(direction, up, new Cesium.Cartesian3()); + Cesium.Cartesian3.normalize(perpendicular, perpendicular); + + // 计算左右偏移点 + const leftOffset = Cesium.Cartesian3.multiplyByScalar( + perpendicular, + width, + new Cesium.Cartesian3() + ); + + if (width > 0) { + rightPositions.unshift(Cesium.Cartesian3.add(start, leftOffset, new Cesium.Cartesian3())); + } else if (width < 0) { + rightPositions.push(Cesium.Cartesian3.add(start, leftOffset, new Cesium.Cartesian3())); + } + + } + return rightPositions + } + //计算角度 + calculateAangle(arr) { + // let fromDegreesArray = that.calSector(that.options.center, that.options.radius, that.options.startAngle, that.options.endAngle, 360, true) + + function getAangle(start, end) { + let rad = Math.PI / 180, + lat1 = start.y * rad, + lat2 = end.y * rad, + lon1 = start.x * rad, + lon2 = end.x * rad; + const a = Math.sin(lon2 - lon1) * Math.cos(lat2); + const b = + Math.cos(lat1) * Math.sin(lat2) - + Math.sin(lat1) * Math.cos(lat2) * Math.cos(lon2 - lon1); + const radians = Math.atan2(a, b) + const degrees = radians % (2 * Math.PI); + let bearing = 450 - ((degrees * 180) / Math.PI < 0 + ? 360 + (degrees * 180) / Math.PI + : (degrees * 180) / Math.PI) - 90; + return 360 - (bearing % 360) + } + + let center = arr[0] + let pos84_1 = arr[1] + let pos84_2 = arr[2] + + let start = { x: center.lng, y: center.lat } + let end1 = { x: pos84_1.lng, y: pos84_1.lat } + let end2 = { x: pos84_2.lng, y: pos84_2.lat } + + let angle1 = getAangle(start, end1) + let angle2 = getAangle(start, end2) + + return { + angle1, + angle2 + } + } + + get carRoadWidth() { + return this.options.carRoadWidth + } + + set carRoadWidth(v) { + this.options.carRoadWidth = v + Road.create(this) + + } + get sideWidth() { + return this.options.sideWidth + } + set sideWidth(v) { + this.options.sideWidth = v + Road.create(this) + } + /** + * @description 编辑框 + * @param state=false {boolean} 状态: true打开, false关闭 + */ + async edit(state = false) { + let _this = this + this.originalOptions = this.deepCopyObj(this.options) + + if (this._DialogObject && this._DialogObject.close) { + this._DialogObject.close() + this._DialogObject = null + } + + if (state) { + this._DialogObject = await new Dialog(this.sdk, this.originalOptions, { + title: '道路属性', left: '180px', top: '100px', + confirmCallBack: (options) => { + this.name = this.name.trim() + if (!this.name) { + this.name = '道路' + } + this.originalOptions = this.deepCopyObj(this.options) + this._DialogObject.close() + this.Dialog.confirmCallBack && this.Dialog.confirmCallBack(this.originalOptions) + syncData(this.sdk, this.options.id) + syncSplitData(this.sdk, this.options.id) + }, + resetCallBack: () => { + this.reset() + this.Dialog.resetCallBack && this.Dialog.resetCallBack() + }, + closeCallBack: () => { + this.reset() + this.Dialog.closeCallBack && this.Dialog.closeCallBack() + }, + showCallBack: (show) => { + this.show = show + this.Dialog.showCallBack && this.Dialog.showCallBack() + } + }, true) + this._DialogObject._element.body.className = this._DialogObject._element.body.className + ' road-surface' + let contentElm = document.createElement('div'); + contentElm.innerHTML = html() + this._DialogObject.contentAppChild(contentElm) + + + // 下拉选项 + // let heightModeData = [ + // { + // name: '海拔高度', + // value: '海拔高度', + // key: '0', + // }, + // { + // name: '相对地表', + // value: '相对地表', + // key: '1', + // }, + // { + // name: '依附模型', + // value: '依附模型', + // key: '2', + // } + // ] + // let heightModeObject = legp( + // this._DialogObject._element.content.getElementsByClassName( + // 'road-box' + // )[0], + // '.road-type' + // ) + // if (heightModeObject) { + // heightModeObject.legp_search(heightModeData) + // let heightModeDataLegpElm = this._DialogObject._element.content + // .getElementsByClassName('road-type')[0] + // .getElementsByTagName('input')[0] + // for (let i = 0; i < heightModeData.length; i++) { + // if (heightModeData[i].key == this.heightMode) { + // heightModeDataLegpElm.value = heightModeData[i].value + // heightModeObject.legp_searchActive( + // heightModeData[i].value + // ) + // break + // } + // } + // heightModeDataLegpElm.addEventListener('input', () => { + // for (let i = 0; i < heightModeData.length; i++) { + // if (heightModeData[i].value === heightModeDataLegpElm.value) { + // this.heightMode = heightModeData[i].key + // break + // } + // } + // }) + + + // this._elms.height = heightElm + // this._elms.heightBox = heightBoxElm + // this._elms.heightMode = heightModeDataLegpElm + // this._elms.heightConfirm = heightConfirmElm + // this._elms.heightModeObject = heightModeObject + + // heightConfirmElm.addEventListener('click', () => { + // this.positionEditing = false + // for (let i = 0; i < this.options.positions.length; i++) { + // this.options.positions[i].alt = Number((this.options.positions[i].alt + Number(heightElm.value)).toFixed(2)) + // this._elms.alt[i].innerHTML = this.options.positions[i].alt + // } + // let fromDegreesArray = this.renewPositions(this.options.positions) + // this.entity.polyline.positions = Cesium.Cartesian3.fromDegreesArrayHeights( + // fromDegreesArray + // ) + + // this.positionEditing = false + // PolylineObject.closeNodeEdit(this) + // }) + // } + + + + + + let all_elm = contentElm.getElementsByTagName("*") + this._EventBinding.on(this, all_elm) + this._elms = this._EventBinding.element + } else { + // if (this._element_style) { + // document.getElementsByTagName('head')[0].removeChild(this._element_style) + // this._element_style = null + // } + // if (this._DialogObject && this._DialogObject.remove) { + // this._DialogObject.remove() + // this._DialogObject = null + // } + } + } + + reset() { + if (!this.viewer.entities.getById(this.options.id)) { + return + } + this.name = this.originalOptions.name + this.carRoadWidth = this.originalOptions.carRoadWidth + this.sideWidth = this.originalOptions.sideWidth + this.positions = this.originalOptions.positions + this.roadImage = this.originalOptions.roadImage + this.sideImage = this.originalOptions.sideImage + } + + /** + * 飞到对应实体 + */ + async flyTo(options = {}) { + setActiveViewer(0) + closeRotateAround(this.sdk) + closeViewFollow(this.sdk) + + if (this.options.customView && this.options.customView.relativePosition && this.options.customView.orientation) { + let orientation = { + heading: Cesium.Math.toRadians(this.options.customView.orientation.heading || 0.0), + pitch: Cesium.Math.toRadians(this.options.customView.orientation.pitch || -60.0), + roll: Cesium.Math.toRadians(this.options.customView.orientation.roll || 0.0) + } + + let lng = this.options.customView.relativePosition.lng + let lat = this.options.customView.relativePosition.lat + let alt = this.options.customView.relativePosition.alt + let destination = Cesium.Cartesian3.fromDegrees(lng, lat, alt) + + let position = { lng: 0, lat: 0 } + if (this.options.position) { + position = { ...this.options.position } + } + else if (this.options.positions) { + position = { ...this.options.positions[0] } + } + else if (this.options.center) { + position = { ...this.options.center } + } + else if (this.options.start) { + position = { ...this.options.start } + } + else { + if (this.options.hasOwnProperty('lng')) { + position.lng = this.options.lng + } + if (this.options.hasOwnProperty('lat')) { + position.lat = this.options.lat + } + if (this.options.hasOwnProperty('alt')) { + position.alt = this.options.alt + } + } + // 如果没有高度值,则获取紧贴高度计算 + // if (!position.hasOwnProperty('alt')) { + // position.alt = await this.getClampToHeight(position) + // } + lng = this.options.customView.relativePosition.lng + position.lng + lat = this.options.customView.relativePosition.lat + position.lat + alt = this.options.customView.relativePosition.alt + position.alt + destination = Cesium.Cartesian3.fromDegrees(lng, lat, alt) + this.sdk.viewer.camera.flyTo({ + destination: destination, + orientation: orientation + }) + } + else { + let positionArray = [] + for (let i = 0; i < this.positions.length; i++) { + let a = Cesium.Cartesian3.fromDegrees( + this.positions[i][0], + this.positions[i][1], + this.options.height + this.options.heightDifference / 2 + ) + positionArray.push(a.x, a.y, a.z) + } + let BoundingSphere = Cesium.BoundingSphere.fromVertices(positionArray) + this.viewer.camera.flyToBoundingSphere(BoundingSphere, { + offset: { + heading: Cesium.Math.toRadians(0.0), + pitch: Cesium.Math.toRadians(-20.0), + roll: Cesium.Math.toRadians(0.0) + } + }) + + } + } + + getSphere() { + return new Promise((resolve) => { + // entity没有加载完成时 state 不会等于0 所以设置定时器直到获取到为止 + const interval = setInterval(() => { + const sphere = new Cesium.BoundingSphere() + const state = this.sdk.viewer._dataSourceDisplay.getBoundingSphere( + this.viewer.entities.getById(this.options.id), + false, + sphere + ) + if (state === Cesium.BoundingSphereState.DONE) { + clearInterval(interval) + } + }, 1000) + }) + } + + /** + * 删除 + */ + async remove() { + this.positions = [] + this.lineEntity = null + + if (this.viewer.entities.getById(this.options.id)) { + this.viewer.entities.getById(this.options.id)._children.forEach((item) => { + this.viewer.entities.remove(item); + }); + this.viewer.entities.remove(this.viewer.entities.getById(this.options.id)) + } + + if (this._DialogObject && !this._DialogObject.isDestroy) { + this._DialogObject.close() + this._DialogObject = null + } + await this.sdk.removeIncetance(this.options.id) + await syncData(this.sdk, this.options.id) + } + + flicker() { } +} + +export default Road diff --git a/src/Obj/Element/Dialog/eventBinding.js b/src/Obj/Element/Dialog/eventBinding.js index 70cecf8..96ef856 100644 --- a/src/Obj/Element/Dialog/eventBinding.js +++ b/src/Obj/Element/Dialog/eventBinding.js @@ -46,8 +46,16 @@ class EventBinding { value = Number(value) if (e.data != '.' && (e.data != '-' || e.target.value)) { if (((!e.target.max) && (!e.target.min)) || ((value <= Number(e.target.max)) && value >= Number(e.target.min))) { - that[m.value] = value + // that[m.value] = value + value = value } + if ((e.target.max) && value > Number(e.target.max)) { + value = Number(e.target.max) + } + if ((e.target.min) && value < Number(e.target.min)) { + value = Number(e.target.min) + } + that[m.value] = value } } }) diff --git a/src/Obj/Materail/RoadTextureMaterialProperty.js b/src/Obj/Materail/RoadTextureMaterialProperty.js new file mode 100644 index 0000000..8f759b2 --- /dev/null +++ b/src/Obj/Materail/RoadTextureMaterialProperty.js @@ -0,0 +1,113 @@ +/* + * @Description: 流动线 + */ +function RoadTexture() { + class RoadTextureMaterialProperty { + constructor(options) { + this._definitionChanged = new Cesium.Event(); + this._image = undefined; + this._repeat = undefined; + this._stRotation = undefined; + this._repeatLength = undefined; + this.image = options.image || ""; + this.repeat = options.repeat || 1.0; + this.stRotation = options.stRotation || 0.0; + // this.rotations = options.rotations || new Array(100).fill(0.0); + } + + get isConstant() { + return false; + } + + get definitionChanged() { + return this._definitionChanged; + } + + getType(time) { + return Cesium.Material.RoadTextureMaterialType; + } + + getValue(time, result) { + if (!Cesium.defined(result)) { + result = {}; + } + result.image = Cesium.Property.getValueOrDefault( + this._image, + time, + "", + result.image + ); + result.repeat = Cesium.Property.getValueOrDefault( + this._repeat, + time, + 1.0, + result.repeat + ); + result.stRotation = Cesium.Property.getValueOrDefault( + this._stRotation, + time, + 0.0, + result.stRotation + ); + console.log(result, 'result') + return result; + } + + equals(other) { + return ( + this === other || + (other instanceof RoadTextureMaterialProperty && + Cesium.Property.equals(this._image, other._image) && + Cesium.Property.equals(this._repeat, other._repeat) && + // Cesium.Property.equals(this._rotations, other._rotations) && + Cesium.Property.equals(this._stRotation, other._stRotation) + ) + ); + } + } + + Object.defineProperties(RoadTextureMaterialProperty.prototype, { + image: Cesium.createPropertyDescriptor("image"), + repeat: Cesium.createPropertyDescriptor("repeat"), + repeatLength: Cesium.createPropertyDescriptor("stRotation"), + }); + + Cesium.RoadTextureMaterialProperty = RoadTextureMaterialProperty; + Cesium.Material.RoadTextureMaterialProperty = "RoadTextureMaterialProperty"; + Cesium.Material.RoadTextureMaterialType = "RoadTextureMaterialType"; + Cesium.Material.RoadTextureMaterialSource = ` + uniform sampler2D image; + uniform float repeat; + czm_material czm_getMaterial(czm_materialInput materialInput) + { + czm_material material = czm_getDefaultMaterial(materialInput); + vec2 st = materialInput.st; + st.s *= repeat; + mat2 rot = mat2(cos(stRotation), -sin(stRotation), sin(stRotation), cos(stRotation)); + vec2 newSt = rot * (st - 0.5) + 0.5; + + vec4 colorImage = texture2D(image, newSt); + material.diffuse = colorImage.rgb; + return material; + } + `; + Cesium.Material._materialCache.addMaterial( + Cesium.Material.RoadTextureMaterialType, + { + fabric: { + type: Cesium.Material.RoadTextureMaterialType, + uniforms: { + image: '', + repeat: 1.0, + stRotation: 0.0, + }, + source: Cesium.Material.RoadTextureMaterialSource, + }, + translucent: function (material) { + return true; + }, + } + ); +} + +export { RoadTexture } diff --git a/src/Obj/Materail/index.js b/src/Obj/Materail/index.js index e7fff6b..324d41c 100644 --- a/src/Obj/Materail/index.js +++ b/src/Obj/Materail/index.js @@ -7,6 +7,7 @@ import { PolylineFlow } from './PolylineFlowMaterialProperty' import { PolylineFlowMult } from './PolylineFlowMultMaterialProperty' import { FlowDashedLine } from './FlowDashedLineFlowMaterialProperty' import { LineTexture } from './LineTextureMaterialProperty' +import { RoadTexture } from './RoadTextureMaterialProperty' function init_material() { StreamWall1() @@ -19,6 +20,7 @@ function init_material() { PolylineFlowMult() FlowDashedLine() LineTexture() + RoadTexture() } export { init_material } diff --git a/static/img/roadPhoto.png b/static/img/roadPhoto.png new file mode 100644 index 0000000000000000000000000000000000000000..8000eb90178ec516572cda305c6b6f34e264622f GIT binary patch literal 40435 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 literal 0 HcmV?d00001 diff --git a/static/img/roadTexture.png b/static/img/roadTexture.png new file mode 100644 index 0000000000000000000000000000000000000000..407266cada00749671c4ebfb0380b127437dd244 GIT binary patch literal 1927 zcmV;22YC32P)00001b5ch_0Itp) z=>Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D2OLR6K~z{rZP@8@ z+(rzAQNKwNOY*LLvE@Z_?5vf%|LaWZYu8~xto-OA*^LG+E-sp!dG`G1n3Q!?QqSz+ny=ZOG8*x%Fv#{r>CW?&J3re9j9Nh_08n z56g$SlIq%gKo*FcHPPt4ff3gnbl)0>-GBJ}`x-I89;m=HVlPGujNyD_myZP^2Thpd zQ1)dQg#{?{mv3)YHrYpY*<^%%fiP2mMhs!9s}cg1zkmgzWVdo8j*N&?Rsv%}EzycS6tnLcE0R+px|l345K%fQ z(*bk>1XZ`5104b|CR&$JHu;!3k!UvU*}B6dl+heRm1?r2GzFWYYfKO`7@-3v0lHZr z)E!#dNc90J6F{>QEGMxLLrNsDuIjnkPw+s9w?HJa92Be#`nNL4Vv21#>K-7q*XjZ-IbP{|eB2!E4p79H{#gX0`xp zO~I5B!%k4lED+QyBZ35C3bMgzL1;#nbTLm*tU8%#19J-mk*N2ix@XyTP6W!j09~=6 z=GGnU^AyGwmKF#MgfNj39-}Di-Wk%fqgrhPINuM}68Gn?e=lauDI?al7Hy&+znq*s zX<6q5jWn0T<^s}sd)rq6?VSQ{{o})*i;eEJek=v}2?)q$r-VEjwJG|wZ_WzPd}jrw zu#+GA>1+Zo?p6*h*FYJ46o8`J+z_O>z!WH_**52_F_--;VPL1eHcjju1R$i%7f5?` zpHqU>0fBNh7!EQ?mOyruE#|PjKdAM0MW_P6KfYzmoUVnwsBvHt^_4ee~2&;bsb?lbbi0=W|D3XXJ2iR4QH-ZkCR*na;4 zf$A1RN(arUt?UKMW&*}QYXM->*lTQRVk<|dUME_cp=CH<@5<2tu3-C8wcpp4Qe#8# z_BUEzAUb~aKj*S+_`o#>T%+Lab%b}HP3MDe5G+fOhOo3iOarv%$a|y>1$4?eWSkuV zQBw0$N7nNlIARSz_6r0RYrDoCwGlvX)z)Uve0Y;$43K$4p5%PW~PV~yA-<2q*)&rk59>4#)fHY52 z0dH?tV$R>DiN<^(QFR;o&}|OL88qiWwkOBg=(fP>g2wC$rCcDQMHU6 zuPn2h?B%C@&K~}~>I1TYXfY>c%ZZnnpjvW@mWTtt2uN#{MG)CIgk}=W`v#qQ%3uCtCO~{_OsFrK32@S^<~BEaZqS7Nl8A-bl16h* z(gmU$K?^vArnQtttpUzX>k*#d&W#BI$l|mo`|SmWF74AM&YgHZUeMYVh-`pxZW#G& zoraW8@(V=uYeMg1W0s}+05m`KqC^c{GfUaC?jUAtV0HVR=po_#bo6ff0o;gEn`IqK z1=JZ-kB#i-`M_qcFD2T#qlY#IzzL^z=vEdVtF8Ig#0WoLR543e{23Zz>kjp2pr3UB zQ4txZ>W^NU(b56oix!+S;w=zFvTs#gvVTLeHwxAq(Cu}&DMh|%rP00Q76>Yzb)$py z;xt4nCWsdkmB_mVb6p$sqXV03e{4KEI{xtjn~pOeE<$n*y$@r|$!v;F0F>*f$wYIF z1tI``*vd|4%=ZOqvvX#suaeD|RqIMI+Zam#Zy-=uAm0gUDwEj-vmEy9UsSF2tb{%l z2v}P$5EQ!yKpzdll?}Re$s7~3GDrXm{C2-Uq;9{Sn-tsz6r&PYT@sMl;to3N%DUIW z6HR!%>SwLTafa$Ah<8WN*=aYKOQ`*x_u`f00zu7G4}==JI=HMW$(~;RCiO~=;kmvE z)4RP%b)Sd@oi)amwP~w#1qD2RjrREes`&-NrxvsvM7h&Yf6vkorgBWh>xc$_{CDsB zWa|!Dc$E`pV)c7O*L1Ht_F2!e^c)ZgIs%lW;M#S8;5a8TF5vtzqlE>XSv8jrHtp#u zV}BC`M#%z!sRbf2`co@MPD|ev^kOsC3gqlietryc+R N002ovPDHLkV1lGmm~#LC literal 0 HcmV?d00001 diff --git a/static/img/sidePhoto.png b/static/img/sidePhoto.png new file mode 100644 index 0000000000000000000000000000000000000000..d6218a4de94b213a450788236aa49b6e2607e329 GIT binary patch literal 47987 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 LNkvXXu0mjfeE Date: Fri, 22 Aug 2025 18:46:29 +0800 Subject: [PATCH 07/17] =?UTF-8?q?=E4=BF=AE=E6=94=B9bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Global/efflect/Sunshine/eventBinding.js | 101 ++++++++++++++------ src/Global/efflect/Sunshine/index.js | 2 +- src/Obj/Element/Dialog/eventBinding.js | 8 +- 3 files changed, 74 insertions(+), 37 deletions(-) diff --git a/src/Global/efflect/Sunshine/eventBinding.js b/src/Global/efflect/Sunshine/eventBinding.js index 6e1b840..321dbdb 100644 --- a/src/Global/efflect/Sunshine/eventBinding.js +++ b/src/Global/efflect/Sunshine/eventBinding.js @@ -1,45 +1,89 @@ -class eventBinding { +class EventBinding { constructor() { this.element = {} } static event = {} getEvent(name) { - return eventBinding.event[name] + return EventBinding.event[name] } getEventAll() { - return eventBinding.event + return EventBinding.event } setEvent(name, event) { - eventBinding.event[name] = event + EventBinding.event[name] = event } on(that, elements) { + this.element = {} for (let i = 0; i < elements.length; i++) { - let Event = [] - let isEvent = false - let removeName = [] if (!elements[i] || !elements[i].attributes) { continue; } + let Event = { + 'input': [], + 'change': [], + 'blur': [], + 'click': [] + } + let isEvent = false + let removeName = [] for (let m of elements[i].attributes) { switch (m.name) { case '@model': { isEvent = true if (elements[i].type == 'checkbox') { - Event.push((e) => { that[m.value] = e.target.checked }) + Event.change.push((e) => { that[m.value] = e.target.checked }) elements[i].checked = that[m.value] } else { - Event.push((e) => { - let value = e.target.value - if (e.target.type == 'number') { - value = Number(value) - } - that[m.value] = value - }) + if (elements[i].type == 'number') { + Event.input.push((e) => { + if (e.target.value || e.target.value === 0) { + let value = e.target.value + value = Number(value) + if (e.data != '.' && (e.data != '-' || e.target.value)) { + if (((!e.target.max) && (!e.target.min)) || ((value <= Number(e.target.max)) && value >= Number(e.target.min))) { + // that[m.value] = value + value = value + } + if ((e.target.max) && value > Number(e.target.max)) { + value = Number(e.target.max) + } + if ((e.target.min) && value < Number(e.target.min)) { + value = Number(e.target.min) + } + // if ((e.target.dataset.min) && value < Number(e.target.dataset.min)) { + // value = Number(e.target.dataset.min) + // } + that[m.value] = value + } + } + }) + Event.blur.push((e) => { + let value = e.target.value + if (e.target.value || (e.target.dataset.null !== 'undefined' && e.target.dataset.null !== '' && !Boolean(e.target.dataset.null))) { + value = Number(value) + if ((e.target.max) && value > Number(e.target.max)) { + value = Number(e.target.max) + } + if ((e.target.min) && value < Number(e.target.min)) { + value = Number(e.target.min) + } + if ((e.target.dataset.min) && value < Number(e.target.dataset.min)) { + value = Number(e.target.dataset.min) + } + } + that[m.value] = value + }) + } + else { + Event.input.push((e) => { + that[m.value] = e.target.value + }) + } if (elements[i].nodeName == 'IMG') { elements[i].src = that[m.value] } @@ -57,13 +101,13 @@ class eventBinding { break; } case '@click': { - elements[i].addEventListener('click', (e) => { - if (typeof (that.Dialog[m.value]) === 'function') { - that.Dialog[m.value](e) + isEvent = true + Event.click.push((e) => { + if (typeof (that[m.value]) === 'function') { + that[m.value](e) } - }); + }) removeName.push(m.name) - // elements[i].attributes.removeNamedItem(m.name) break; } } @@ -74,19 +118,18 @@ class eventBinding { } if (isEvent) { - let ventType = 'input' - if (elements[i].tagName != 'INPUT' || elements[i].type == 'checkbox') { - ventType = 'change' - } - elements[i].addEventListener(ventType, (e) => { - for (let t = 0; t < Event.length; t++) { - Event[t](e) + for (let key in Event) { + if (Event[key].length > 0) { + elements[i].addEventListener(key, (e) => { + for (let t = 0; t < Event[key].length; t++) { + Event[key][t](e) + } + }); } - }); + } } } } } -const EventBinding = new eventBinding(); export default EventBinding; diff --git a/src/Global/efflect/Sunshine/index.js b/src/Global/efflect/Sunshine/index.js index 12119d5..4a921a1 100644 --- a/src/Global/efflect/Sunshine/index.js +++ b/src/Global/efflect/Sunshine/index.js @@ -3,7 +3,7 @@ */ import Dialog from '../../../Obj/Element/Dialog'; import { html } from "./_element"; -import EventBinding from '../../../Obj/Element/Dialog/eventBinding'; +import EventBinding from './eventBinding'; import { syncData } from '../../MultiViewportMode' import Tools from '../../../Tools' import TimeLine from './TimeLine' diff --git a/src/Obj/Element/Dialog/eventBinding.js b/src/Obj/Element/Dialog/eventBinding.js index 6bd6b26..18074c5 100644 --- a/src/Obj/Element/Dialog/eventBinding.js +++ b/src/Obj/Element/Dialog/eventBinding.js @@ -45,10 +45,6 @@ class EventBinding { let value = e.target.value value = Number(value) if (e.data != '.' && (e.data != '-' || e.target.value)) { - if (((!e.target.max) && (!e.target.min)) || ((value <= Number(e.target.max)) && value >= Number(e.target.min))) { - // that[m.value] = value - value = value - } if ((e.target.max) && value > Number(e.target.max)) { value = Number(e.target.max) } @@ -58,10 +54,8 @@ class EventBinding { if ((e.target.dataset.min) && value < Number(e.target.dataset.min)) { value = Number(e.target.dataset.min) } - that[m.value] = value } - } - }) + }) Event.blur.push((e) => { let value = e.target.value if (e.target.value || (e.target.dataset.null !== 'undefined' && e.target.dataset.null !== '' && !Boolean(e.target.dataset.null))) { From 6117e74a4447ab08ad25fc0511ea529ba220df20 Mon Sep 17 00:00:00 2001 From: zh <972939975@qq.com> Date: Fri, 22 Aug 2025 18:49:45 +0800 Subject: [PATCH 08/17] =?UTF-8?q?=E5=B1=9E=E6=80=A7=E6=A1=86=E4=BD=8D?= =?UTF-8?q?=E7=BD=AE=E9=AB=98=E5=BA=A6=E8=AE=A1=E7=AE=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Obj/Element/Dialog/eventBinding.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Obj/Element/Dialog/eventBinding.js b/src/Obj/Element/Dialog/eventBinding.js index 18074c5..c47327f 100644 --- a/src/Obj/Element/Dialog/eventBinding.js +++ b/src/Obj/Element/Dialog/eventBinding.js @@ -55,7 +55,8 @@ class EventBinding { value = Number(e.target.dataset.min) } } - }) + } + }) Event.blur.push((e) => { let value = e.target.value if (e.target.value || (e.target.dataset.null !== 'undefined' && e.target.dataset.null !== '' && !Boolean(e.target.dataset.null))) { From f37f95d646b1e83fc87563637f066ece7673d576 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=8F=E5=A4=A7=E8=83=86?= <1101282782@qq.com> Date: Fri, 22 Aug 2025 21:43:34 +0800 Subject: [PATCH 09/17] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=89=B9=E9=87=8F?= =?UTF-8?q?=E6=A8=A1=E5=9E=8B=E9=80=89=E6=8B=A9=E7=82=B9=20=E4=BC=9A?= =?UTF-8?q?=E5=BC=B9=E6=A1=86=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Obj/Base/BatchModel/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Obj/Base/BatchModel/index.js b/src/Obj/Base/BatchModel/index.js index d61e6cd..a42c94e 100644 --- a/src/Obj/Base/BatchModel/index.js +++ b/src/Obj/Base/BatchModel/index.js @@ -48,7 +48,7 @@ class BatchModel extends Base { this.sdk.addIncetance(this.options.id, this) // BatchModel.computeDis(this) // if (this.options.positions.length > 0 || this.options.positions.lng) { - if (options.type && options.spacing != undefined) { + if ((options.type && options.spacing != undefined) || options.type == '点') { // BatchModel.computeDis(this) let Draw From ce065fb845fecb168cad81eca9c30b51f8c829a8 Mon Sep 17 00:00:00 2001 From: zh <972939975@qq.com> Date: Fri, 22 Aug 2025 21:55:50 +0800 Subject: [PATCH 10/17] =?UTF-8?q?=E4=BA=8C=E4=B8=89=E7=BB=B4=E6=A8=A1?= =?UTF-8?q?=E5=BC=8F=E6=A0=87=E7=AD=BE=E5=90=8C=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Obj/Analysis/test2/index.js | 2 +- src/Obj/Base/AssembleObject/index.js | 2 +- src/Obj/Base/AttackArrowObject/index.js | 2 +- src/Obj/Base/BaseSource/BaseModel/Model/index.js | 2 +- src/Obj/Base/CurvelineObject/index.js | 2 +- src/Obj/Base/EllipseObject/index.js | 2 +- src/Obj/Base/PincerArrowObject/index.js | 2 +- src/Obj/Base/PolygonObject/index.js | 2 +- src/Obj/Base/PolyhedronObject/index.js | 2 +- src/Obj/Base/PolylineObject/index.js | 2 +- src/Obj/Base/RadarScanStereoscopic/index.js | 2 +- src/Obj/Base/SectorObject/index.js | 2 +- src/Obj/Base/StraightArrowObject/index.js | 2 +- src/Obj/Base/WallRealStereoscopic/index.js | 2 +- src/Obj/Base/WallRealStereoscopic/index2.js | 2 +- src/Obj/Base/WallRealStereoscopic2/index.js | 2 +- src/Obj/Base/WallStereoscopic/index.js | 2 +- 17 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/Obj/Analysis/test2/index.js b/src/Obj/Analysis/test2/index.js index 1cb3415..61fdea9 100644 --- a/src/Obj/Analysis/test2/index.js +++ b/src/Obj/Analysis/test2/index.js @@ -292,7 +292,7 @@ class PolygonObject extends Base { } set labelShow(v) { this.options.label.show = v - if (this.show) { + if (this.show && !this.showView || this.showView == 3) { this.label.show = v } else { diff --git a/src/Obj/Base/AssembleObject/index.js b/src/Obj/Base/AssembleObject/index.js index 3ee6ba0..f682f25 100644 --- a/src/Obj/Base/AssembleObject/index.js +++ b/src/Obj/Base/AssembleObject/index.js @@ -319,7 +319,7 @@ class AssembleObject extends Base { } set labelShow(v) { this.options.label.show = v - if (this.show) { + if (this.show && !this.showView || this.showView == 3) { this.label.show = v } else { diff --git a/src/Obj/Base/AttackArrowObject/index.js b/src/Obj/Base/AttackArrowObject/index.js index ec83033..278e654 100644 --- a/src/Obj/Base/AttackArrowObject/index.js +++ b/src/Obj/Base/AttackArrowObject/index.js @@ -324,7 +324,7 @@ class AttackArrowObject extends Base { } set labelShow(v) { this.options.label.show = v - if (this.show) { + if (this.show && !this.showView || this.showView == 3) { this.label.show = v } else { diff --git a/src/Obj/Base/BaseSource/BaseModel/Model/index.js b/src/Obj/Base/BaseSource/BaseModel/Model/index.js index d4bd9b0..6b24f46 100644 --- a/src/Obj/Base/BaseSource/BaseModel/Model/index.js +++ b/src/Obj/Base/BaseSource/BaseModel/Model/index.js @@ -637,7 +637,7 @@ class Model extends BaseModel { set labelShow(v) { this.options.label.show = v - if (this.show) { + if (this.show && !this.showView || this.showView == 3) { this.label && (this.label.show = v) } else { diff --git a/src/Obj/Base/CurvelineObject/index.js b/src/Obj/Base/CurvelineObject/index.js index 0f632a0..f6ea05f 100644 --- a/src/Obj/Base/CurvelineObject/index.js +++ b/src/Obj/Base/CurvelineObject/index.js @@ -666,7 +666,7 @@ class CurvelineObject extends Base { } set labelShow(v) { this.options.label.show = v - if (this.show) { + if (this.show && !this.showView || this.showView == 3) { this.label.show = v setTimeout(() => { this.label.position = [ diff --git a/src/Obj/Base/EllipseObject/index.js b/src/Obj/Base/EllipseObject/index.js index 7b4631c..db05474 100644 --- a/src/Obj/Base/EllipseObject/index.js +++ b/src/Obj/Base/EllipseObject/index.js @@ -347,7 +347,7 @@ class EllipseObject extends Base { } set labelShow(v) { this.options.label.show = v - if (this.show) { + if (this.show && !this.showView || this.showView == 3) { this.label.show = v } else { diff --git a/src/Obj/Base/PincerArrowObject/index.js b/src/Obj/Base/PincerArrowObject/index.js index 5788789..6c5850c 100644 --- a/src/Obj/Base/PincerArrowObject/index.js +++ b/src/Obj/Base/PincerArrowObject/index.js @@ -334,7 +334,7 @@ class PincerArrowObject extends Base { } set labelShow(v) { this.options.label.show = v - if (this.show) { + if (this.show && !this.showView || this.showView == 3) { this.label.show = v } else { diff --git a/src/Obj/Base/PolygonObject/index.js b/src/Obj/Base/PolygonObject/index.js index ca87983..6d51972 100644 --- a/src/Obj/Base/PolygonObject/index.js +++ b/src/Obj/Base/PolygonObject/index.js @@ -355,7 +355,7 @@ class PolygonObject extends Base { } set labelShow(v) { this.options.label.show = v - if (this.show) { + if (this.show && !this.showView || this.showView == 3) { this.label.show = v } else { this.label.show = false diff --git a/src/Obj/Base/PolyhedronObject/index.js b/src/Obj/Base/PolyhedronObject/index.js index 17d9934..1687d93 100644 --- a/src/Obj/Base/PolyhedronObject/index.js +++ b/src/Obj/Base/PolyhedronObject/index.js @@ -517,7 +517,7 @@ class PolyhedronObject extends Base { } set labelShow(v) { this.options.label.show = v - if (this.show) { + if (this.show && !this.showView || this.showView == 3) { this.label.show = v } else { diff --git a/src/Obj/Base/PolylineObject/index.js b/src/Obj/Base/PolylineObject/index.js index f85e43f..3769d21 100644 --- a/src/Obj/Base/PolylineObject/index.js +++ b/src/Obj/Base/PolylineObject/index.js @@ -701,7 +701,7 @@ class PolylineObject extends Base { } set labelShow(v) { this.options.label.show = v - if (this.show) { + if (this.show && !this.showView || this.showView == 3) { this.label.show = v setTimeout(() => { this.label.position = [ diff --git a/src/Obj/Base/RadarScanStereoscopic/index.js b/src/Obj/Base/RadarScanStereoscopic/index.js index aef9c62..4c07dec 100644 --- a/src/Obj/Base/RadarScanStereoscopic/index.js +++ b/src/Obj/Base/RadarScanStereoscopic/index.js @@ -424,7 +424,7 @@ class RadarScanStereoscopic extends Base { } set labelShow(v) { this.options.label.show = v - if (this.show) { + if (this.show && !this.showView || this.showView == 3) { this.label.show = v } else { diff --git a/src/Obj/Base/SectorObject/index.js b/src/Obj/Base/SectorObject/index.js index 66e9c1e..60d8366 100644 --- a/src/Obj/Base/SectorObject/index.js +++ b/src/Obj/Base/SectorObject/index.js @@ -344,7 +344,7 @@ class SectorObject extends Base { } set labelShow(v) { this.options.label.show = v - if (this.show) { + if (this.show && !this.showView || this.showView == 3) { this.label.show = v } else { diff --git a/src/Obj/Base/StraightArrowObject/index.js b/src/Obj/Base/StraightArrowObject/index.js index f4037ef..8ab636c 100644 --- a/src/Obj/Base/StraightArrowObject/index.js +++ b/src/Obj/Base/StraightArrowObject/index.js @@ -321,7 +321,7 @@ class StraightArrowObject extends Base { } set labelShow(v) { this.options.label.show = v - if (this.show) { + if (this.show && !this.showView || this.showView == 3) { this.label.show = v } else { diff --git a/src/Obj/Base/WallRealStereoscopic/index.js b/src/Obj/Base/WallRealStereoscopic/index.js index 340eaaa..6ae0a9f 100644 --- a/src/Obj/Base/WallRealStereoscopic/index.js +++ b/src/Obj/Base/WallRealStereoscopic/index.js @@ -253,7 +253,7 @@ class WallRealStereoscopic extends Base { } set labelShow(v) { this.options.label.show = v - if (this.show) { + if (this.show && !this.showView || this.showView == 3) { this.label.show = v } else { diff --git a/src/Obj/Base/WallRealStereoscopic/index2.js b/src/Obj/Base/WallRealStereoscopic/index2.js index bb12e89..9583344 100644 --- a/src/Obj/Base/WallRealStereoscopic/index2.js +++ b/src/Obj/Base/WallRealStereoscopic/index2.js @@ -157,7 +157,7 @@ class WallStereoscopic extends Base { } set labelShow(v) { this.options.label.show = v - if (this.show) { + if (this.show && !this.showView || this.showView == 3) { this.label.show = v } else { diff --git a/src/Obj/Base/WallRealStereoscopic2/index.js b/src/Obj/Base/WallRealStereoscopic2/index.js index bd5ee5d..225c6d5 100644 --- a/src/Obj/Base/WallRealStereoscopic2/index.js +++ b/src/Obj/Base/WallRealStereoscopic2/index.js @@ -243,7 +243,7 @@ class WallRealStereoscopic extends Base { } set labelShow(v) { this.options.label.show = v - if (this.show) { + if (this.show && !this.showView || this.showView == 3) { this.label.show = v } else { diff --git a/src/Obj/Base/WallStereoscopic/index.js b/src/Obj/Base/WallStereoscopic/index.js index 49a0edf..1b37194 100644 --- a/src/Obj/Base/WallStereoscopic/index.js +++ b/src/Obj/Base/WallStereoscopic/index.js @@ -195,7 +195,7 @@ class WallStereoscopic extends Base { } set labelShow(v) { this.options.label.show = v - if (this.show) { + if (this.show && !this.showView || this.showView == 3) { this.label.show = v } else { From cb2741862af6b3a9b1a23aa85ea19e012e971ff3 Mon Sep 17 00:00:00 2001 From: zh <972939975@qq.com> Date: Fri, 22 Aug 2025 23:48:51 +0800 Subject: [PATCH 11/17] =?UTF-8?q?=E8=A7=A3=E5=86=B3=E4=BA=8C=E4=B8=89?= =?UTF-8?q?=E7=BB=B4=E9=BC=A0=E6=A0=87=E4=BA=8B=E4=BB=B6=E7=BB=91=E5=AE=9A?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MultiViewportMode/ClickCallback/index.js | 578 ++++++++++++++++++ src/Global/MultiViewportMode/index.js | 13 + src/Obj/Base/index.js | 24 +- src/YJEarth/index.js | 6 +- 4 files changed, 614 insertions(+), 7 deletions(-) create mode 100644 src/Global/MultiViewportMode/ClickCallback/index.js diff --git a/src/Global/MultiViewportMode/ClickCallback/index.js b/src/Global/MultiViewportMode/ClickCallback/index.js new file mode 100644 index 0000000..cc940af --- /dev/null +++ b/src/Global/MultiViewportMode/ClickCallback/index.js @@ -0,0 +1,578 @@ +/** + * @name: click + * @author: Administrator + * @date: 2023-05-28 11:05 + * @description:click + * @update: 2023-05-28 11:05 + */ +let leftClickHandler = null +let rightClickHandler = null +let MoveHandler = null +let leftClickCallbackMap = new Map() +let rightClickCallbackMap = new Map() +let MoveCallbackMap = new Map() +let selectedFeature; + + +function cartesian3Towgs84(cartesian, viewer) { + var ellipsoid = viewer.scene.globe.ellipsoid + var cartesian3 = new Cesium.Cartesian3( + cartesian.x, + cartesian.y, + cartesian.z + ) + var cartographic = ellipsoid.cartesianToCartographic(cartesian3) + var lat = Cesium.Math.toDegrees(cartographic.latitude) + var lng = Cesium.Math.toDegrees(cartographic.longitude) + var alt = cartographic.height < 0 ? 0 : cartographic.height + return { + lng: lng, + lat: lat, + alt: alt, + } +} + +function getcartesian(sdk, movement) { + if (movement.endPosition) { + movement.endPosition.y -= 2 + } + let position = movement.position || movement.endPosition + // 获取世界坐标系地表坐标,考虑地形,不包括模型,倾斜摄影模型表面; + let cartesian = sdk.viewer.scene.pickPosition(position) + if (!cartesian) { + const ray = sdk.viewer.camera.getPickRay(position); //相交的射线 + cartesian = sdk.viewer.scene.globe.pick(ray, sdk.viewer.scene); + } + return cartesian +} + +function openLeftClick(sdk, cb) { + if (!sdk || !sdk.viewer) { + return + } + let click = true + leftClickHandler = new Cesium.ScreenSpaceEventHandler(sdk.viewer.canvas) + leftClickHandler.setInputAction((movement) => { + let cartesian = sdk.viewer.scene.pickPosition(movement.position) + if (!cartesian) { + const ray = sdk.viewer.camera.getPickRay(movement.position); //相交的射线 + cartesian = sdk.viewer.scene.globe.pick(ray, sdk.viewer.scene); + } + if (!cartesian) { + return + } + + let pos84 = cartesian3Towgs84(cartesian, sdk.viewer) + + cb && cb(pos84) + + if (click) { + click = false + setTimeout(() => { + click = true + }, 600); + if (!YJ.Measure.GetMeasureStatus() && cartesian) { + let flag = false + for (let i = leftClickCallbackMap.size - 1; i >= 0; i--) { + let key = Array.from(leftClickCallbackMap.keys())[i] + let obj = leftClickCallbackMap.get(key) + if (obj) { + + if (obj.that) { + // 是否为多边形 + if (obj.that.type === 'PolygonObject') { + // 是否可点击y + if (obj.that.picking) { + if (obj.that.options.positions && obj.that.options.positions.length >= 3) { + let pt = turf.point([pos84.lng, pos84.lat]); + let polyPos = [] + for (let i = 0; i < obj.that.options.positions.length; i++) { + polyPos.push([ + obj.that.options.positions[i].lng, + obj.that.options.positions[i].lat + ]) + } + polyPos.push([ + obj.that.options.positions[0].lng, + obj.that.options.positions[0].lat + ]) + let poly = turf.polygon([polyPos]); + let contain = turf.booleanPointInPolygon(pt, poly); + if (contain) { + obj.callback( + movement, + obj.that.options.id, + cartesian3Towgs84(getcartesian(sdk, movement), sdk.viewer), obj.that) + flag = true + break + } + } + } + } + // 聚集地 + else if (obj.that.type === 'AssembleObject') { + if (obj.that.picking) { + if (obj.that.options.positions && obj.that.options.positions.length >= 3) { + let positions = obj.that.computeAssemble(obj.that.options.positions, true) + let pt = turf.point([pos84.lng, pos84.lat]); + let polyPos = [] + for (let i = 0; i < positions.length; i += 2) { + polyPos.push([ + positions[i], + positions[i + 1] + ]) + } + let poly = turf.polygon([polyPos]); + let contain = turf.booleanPointInPolygon(pt, poly); + if (contain) { + obj.callback( + movement, + obj.that.options.id, + cartesian3Towgs84(getcartesian(sdk, movement), sdk.viewer), obj.that) + flag = true + break + } + } + } + } + // 单箭头 + else if (obj.that.type === 'AttackArrowObject') { + if (obj.that.picking) { + if (obj.that.options.positions && obj.that.options.positions.length >= 3) { + let pt = turf.point([pos84.lng, pos84.lat]); + let positions = obj.that.computeAttackArrow(obj.that.options.positions) + let polyPos = [] + for (let m = 0; m < positions.length; m++) { + let pos84 = cartesian3Towgs84(positions[m], sdk.viewer) + polyPos.push([pos84.lng, pos84.lat]) + } + let poly = turf.polygon([polyPos]); + let contain = turf.booleanPointInPolygon(pt, poly); + if (contain) { + obj.callback( + movement, + obj.that.options.id, + cartesian3Towgs84(getcartesian(sdk, movement), sdk.viewer), obj.that) + flag = true + break + } + } + } + } + // 双箭头 + else if (obj.that.type === 'PincerArrowObject') { + if (obj.that.picking) { + if (obj.that.options.positions && obj.that.options.positions.length >= 5) { + let pt = turf.point([pos84.lng, pos84.lat]); + let positions = obj.that.computePincerArrow(obj.that.options.positions) + let polyPos = [] + for (let m = 0; m < positions.length; m++) { + let pos84 = cartesian3Towgs84(positions[m], sdk.viewer) + polyPos.push([pos84.lng, pos84.lat]) + } + let pos84_0 = cartesian3Towgs84(positions[0], sdk.viewer) + polyPos.push([pos84_0.lng, pos84_0.lat]) + let poly = turf.polygon([polyPos]); + let contain = turf.booleanPointInPolygon(pt, poly); + if (contain) { + obj.callback( + movement, + obj.that.options.id, + cartesian3Towgs84(getcartesian(sdk, movement), sdk.viewer), obj.that) + flag = true + break + } + } + } + } + // 圆 + else if (obj.that.type === 'CircleObject') { + if (obj.that.picking) { + let pt = turf.point([pos84.lng, pos84.lat]); + if (obj.that.options.center && obj.that.options.radius) { + let center = [obj.that.options.center.lng, obj.that.options.center.lat]; + let radius = obj.that.options.radius / 1000; + let options = { steps: 360, units: 'kilometers' }; + let circle = turf.circle(center, radius, options); + let contain = turf.booleanPointInPolygon(pt, circle); + if (contain) { + obj.callback( + movement, + obj.that.options.id, + cartesian3Towgs84(getcartesian(sdk, movement), sdk.viewer), obj.that) + flag = true + break + } + } + + } + } + // 扇形 + else if (obj.that.type === 'SectorObject') { + if (obj.that.picking) { + let pt = turf.point([pos84.lng, pos84.lat]); + if (obj.that.options.center && obj.that.options.radius && obj.that.options.startAngle && obj.that.options.endAngle) { + let positions = obj.that.calSector(obj.that.options.center, obj.that.options.radius, obj.that.options.startAngle, obj.that.options.endAngle, undefined, true) + let polyPos = [] + for (let m = 0; m < positions.length; m++) { + polyPos.push([positions[m].lng, positions[m].lat]) + } + let poly = turf.polygon([polyPos]); + let contain = turf.booleanPointInPolygon(pt, poly); + if (contain) { + obj.callback( + movement, + obj.that.options.id, + cartesian3Towgs84(getcartesian(sdk, movement), sdk.viewer), obj.that) + flag = true + break + } + } + + } + } + } + } + } + if (!flag) { + const pick = sdk.viewer.scene.pick(movement.position) + if (pick) { + if (pick.id) { + let entityId + // 矢量 + if (pick.id.type && pick.id.type === 'vector' && pick.id.parentId) { + let obj = leftClickCallbackMap.get(pick.id.parentId) + if (obj.that.picking && obj.that.geojson) { + for (let i = 0; i < obj.that.geojson.features.length; i++) { + if (obj.that.geojson.features[i].id === pick.id._id) { + obj.callback( + movement, + obj.that.geojson.features[i].id, + cartesian3Towgs84(getcartesian(sdk, movement), sdk.viewer), obj.that) + } + } + } + } + else if (typeof pick.id.id == 'string') { + let array = pick.id.id.split('-') + array.splice(array.length - 1, 1) + entityId = array.join('-') + } + + if (pick.id.properties && pick.id.properties.id && leftClickCallbackMap.has(pick.id.properties.id._value)) { + let obj = leftClickCallbackMap.get(pick.id.properties.id._value) + if (obj.that.picking) { + obj.callback( + movement, + pick.id.properties.id._value, + cartesian3Towgs84(getcartesian(sdk, movement), sdk.viewer), obj.that) + } + } + else if (leftClickCallbackMap.has(pick.id.id)) { + let obj = leftClickCallbackMap.get(pick.id.id) + if (obj.that.picking) { + obj.callback( + movement, + pick.id.id, + cartesian3Towgs84(getcartesian(sdk, movement), sdk.viewer), obj.that) + } + } + else if (entityId && leftClickCallbackMap.has(entityId)) { + let obj = leftClickCallbackMap.get(entityId) + if (obj.that.picking) { + obj.callback( + movement, + entityId, + cartesian3Towgs84(getcartesian(sdk, movement), sdk.viewer), obj.that) + } + } + else if (pick.primitive) { + if (typeof pick.id == 'string' && leftClickCallbackMap.has(pick.id)) { + let obj = leftClickCallbackMap.get(pick.id) + obj.callback( + movement, + pick.id, + cartesian3Towgs84(getcartesian(sdk, movement), sdk.viewer), obj.that) + } + } + } + else { + if (pick.primitive && pick.primitive.id) { + if (leftClickCallbackMap.has(pick.primitive.id)) { + let obj = leftClickCallbackMap.get(pick.primitive.id) + if (obj.that.picking) { + if (obj.that.type === 'bim') { + if (YJ.Global.getBimPickStatus(sdk)) { + obj.callback( + movement, + pick.primitive, + cartesian3Towgs84(getcartesian(sdk, movement), sdk.viewer), obj.that) + } + } + else { + obj.callback( + movement, + pick.primitive.id, + cartesian3Towgs84(getcartesian(sdk, movement), sdk.viewer), obj.that) + } + } + } + } + } + if (pick.content && (!pick.primitive || !pick.primitive.id)) { + if (leftClickCallbackMap.has(pick.content.tileset.id)) { + let obj = leftClickCallbackMap.get(pick.content.tileset.id) + if (obj.that.picking) { + if (obj.that.type === 'bim') { + if (YJ.Global.getBimPickStatus(sdk)) { + obj.callback( + movement, + pick.content.tileset, + cartesian3Towgs84(getcartesian(sdk, movement), sdk.viewer), obj.that) + } + } + else { + obj.callback( + movement, + pick.content.tileset.id, + cartesian3Towgs84(getcartesian(sdk, movement), sdk.viewer), obj.that) + } + } + } + } + } + } + } + } + + + // if (click) { + // click = false + // setTimeout(() => { + // click = true + // }, 300); + // if (!YJ.Measure.GetMeasureStatus()) { + + // } + // } + }, Cesium.ScreenSpaceEventType.LEFT_CLICK) + + // leftClickHandler.setInputAction(function (movement) { + // const feature = sdk.viewer.scene.pick(movement.endPosition); + // // unselectFeature(selectedFeature); + // if (selectedFeature) { + // selectedFeature.color = Cesium.Color.WHITE; + // } + // selectedFeature = feature + // if (feature) { + // feature.color = Cesium.Color.YELLOW; + // } + // }, Cesium.ScreenSpaceEventType.MOUSE_MOVE); + // } + +} + +function closeLeftClick(sdk) { + leftClickHandler.destroy() //关闭事件句柄 + leftClickHandler = null + // } +} + +function openRightClick(sdk) { + if (!sdk || !sdk.viewer) { + return + } + rightClickHandler = new Cesium.ScreenSpaceEventHandler(sdk.viewer.canvas) + rightClickHandler.setInputAction((movement) => { + if (!YJ.Measure.GetMeasureStatus()) { + const pick = sdk.viewer.scene.pick(movement.position) + if (pick && pick.id) { + let id + if (pick.id.type && pick.id.type === 'vector' && pick.id.parentId) { + let obj = rightClickCallbackMap.get(pick.id.parentId) + if (obj.that.picking && obj.that.geojson) { + for (let i = 0; i < obj.that.geojson.features.length; i++) { + if (obj.that.geojson.features[i].id === pick.id._id) { + obj.callback( + movement, + obj.that.geojson.features[i].id, + cartesian3Towgs84(getcartesian(sdk, movement), sdk.viewer), obj.that) + } + } + } + } + else { + if (typeof pick.id === 'string') { + id = pick.id + } + else { + id = pick.id.id + } + if (rightClickCallbackMap.has(id)) { + let obj = rightClickCallbackMap.get(id) + if (obj.that.picking) { + let cartesian = getcartesian(sdk, movement) + if (!cartesian) { + return + } + obj.callback( + movement, + id, + cartesian3Towgs84(cartesian, sdk.viewer), obj.that) + } + } + } + } + if (pick && pick.content) { + if (rightClickCallbackMap.has(pick.content.tileset.id)) { + let obj = rightClickCallbackMap.get(pick.content.tileset.id) + if (obj.that.picking) { + if (obj.that.type === 'bim') { + if (YJ.Global.getBimPickStatus(sdk)) { + let cartesian = getcartesian(sdk, movement) + if (!cartesian) { + return + } + obj.callback( + movement, + pick.getProperty('id'), + cartesian3Towgs84(cartesian, sdk.viewer), obj.that) + } + } + else { + let cartesian = getcartesian(sdk, movement) + if (!cartesian) { + return + } + obj.callback( + movement, + pick.content.tileset.id, + cartesian3Towgs84(cartesian, sdk.viewer), obj.that) + } + } + } + } + } + }, Cesium.ScreenSpaceEventType.RIGHT_CLICK) +} + +function closeRightClick() { + if (rightClickHandler) { + rightClickHandler.destroy() //关闭事件句柄 + rightClickHandler = null + } +} + +function openMove(sdk) { + MoveHandler = new Cesium.ScreenSpaceEventHandler(sdk.viewer.canvas) + MoveHandler.setInputAction(function (movement) { + const pick = sdk.viewer.scene.pick(movement.endPosition); + // unselectFeature(selectedFeature); + // if (selectedFeature) { + // let color = '#fff' + // let state = selectedFeature.getProperty('state') + // switch (state) { + // case '0': + // color = '#fff' + // break; + // case '1': + // color = '#f00' + // break; + // case '2': + // color = '#0f0' + // break; + // case '3': + // color = '#00f' + // break; + // default: + // } + // selectedFeature.color = Cesium.Color.fromCssColorString(color).withAlpha(selectedFeature.tileset.transparency) + // } + // if (pick && pick.id) { } + // if (pick && pick.content) { + // if (MoveCallbackMap.has(pick.content.tileset.id)) { + // let obj = MoveCallbackMap.get(pick.content.tileset.id) + // if (obj.that.picking) { + // if (obj.that.type === 'bim') { + // if (YJ.Global.getBimPickStatus(sdk)) { + // selectedFeature = pick + // pick.color = Cesium.Color.YELLOW; + // } + // else { + // selectedFeature = null + // } + // } + // else { + // selectedFeature = pick + // pick.color = Cesium.Color.YELLOW; + // } + // } + // else { + // selectedFeature = null + // } + // } + // } + }, Cesium.ScreenSpaceEventType.MOUSE_MOVE); +} + +function closeMove() { + if (MoveHandler) { + MoveHandler.destroy() //关闭事件句柄 + MoveHandler = null + } +} + +/*注册左键回调*/ +function regLeftClickCallback(id, callback, that) { + + leftClickCallbackMap.set(id, { callback, that }) +}/*取消左键回调*/ +function unRegLeftClickCallback(id,) { + leftClickCallbackMap.delete(id,) +} + +/*注册右键回调*/ +function regRightClickCallback(id, callback, that) { + rightClickCallbackMap.set(id, { callback, that }) +}/*取消右键回调*/ +function unRegRightClickCallback(id,) { + rightClickCallbackMap.delete(id,) +} + +/*注册左键回调*/ +function regMoveCallback(id, callback, that) { + MoveCallbackMap.set(id, { callback, that }) +}/*取消左键回调*/ +function unregMoveCallback(id,) { + MoveCallbackMap.delete(id,) +} + +function getLeftClickState() { + if (leftClickHandler) { + return true + } + else { + false + } +} + +function getRightClickState() { + if (rightClickHandler) { + return true + } + else { + false + } +} + +function getMoveState() { + if (MoveHandler) { + return true + } + else { + false + } +} + + +export { openLeftClick, closeLeftClick, regLeftClickCallback, unRegLeftClickCallback, openRightClick, closeRightClick, regRightClickCallback, unRegRightClickCallback, openMove, closeMove, regMoveCallback, unregMoveCallback, getLeftClickState, getRightClickState, getMoveState } diff --git a/src/Global/MultiViewportMode/index.js b/src/Global/MultiViewportMode/index.js index 9e61089..d9c616d 100644 --- a/src/Global/MultiViewportMode/index.js +++ b/src/Global/MultiViewportMode/index.js @@ -7,6 +7,9 @@ import { CesiumContainer } from '../global' import { off as offSplitScreen } from "../SplitScreen"; import { FlwStatusSwitch, JwwStatusSwitch, getFlwStatus, getJwwStatus } from "../global" import { SheetIndexStatusSwitch, getStatus } from '../SheetIndex' +import { getLeftClickState, getRightClickState, getMoveState } from "../../Global/ClickCallback" +import { openLeftClick, openRightClick, openMove } from "./ClickCallback" + let sdk2D let sdk3D @@ -32,6 +35,16 @@ async function init(sdk) { }) sdk2.viewer.scene.mode = Cesium.SceneMode.SCENE2D sdk2D = await sdk2 + if(getLeftClickState()) { + openLeftClick(sdk2D) + } + if(getRightClickState()) { + openRightClick(sdk2D) + } + if(getMoveState()) { + openMove(sdk2D) + } + // window.sdk2D = sdk2D solveBug() syncObject = { sdks: [sdk, sdk2], tools } diff --git a/src/Obj/Base/index.js b/src/Obj/Base/index.js index fedb92a..86f8d5f 100644 --- a/src/Obj/Base/index.js +++ b/src/Obj/Base/index.js @@ -9,6 +9,7 @@ import Tools from "../../Tools"; import { getHost, getToken } from "../../on"; import { regLeftClickCallback, regRightClickCallback, regMoveCallback } from "../../Global/ClickCallback"; import { regLeftClickCallback as regLeftClickCallback2, regRightClickCallback as regRightClickCallback2, regMoveCallback as regMoveCallback2 } from "../../Global/SplitScreen/ClickCallback"; +import { regLeftClickCallback as regLeftClickCallback3, regRightClickCallback as regRightClickCallback3, regMoveCallback as regMoveCallback3 } from "../../Global/MultiViewportMode/ClickCallback"; import { setSplitDirection, syncSplitData, getSdk } from "../../Global/SplitScreen"; import { syncData, getSdk as get2DSdk } from '../../Global/MultiViewportMode' import { setActiveViewer, closeRotateAround, closeViewFollow } from '../../Global/global' @@ -68,7 +69,7 @@ class Base extends Tools { let sdk2D = get2DSdk().sdkD if (!sdk2D) { this.#_showView = v - if(this.entity) { + if (this.entity) { this.entity._showView = v } return @@ -361,12 +362,17 @@ class Base extends Tools { console.error('val:', val, '不是一个function') } else { let sdkD = getSdk().sdkD + let sdk2D = get2DSdk().sdkD if (sdkD && this.sdk === sdkD) { if (this.clickCallBack == null && this.options && this.options.id) { regLeftClickCallback2(this.options.id, this.leftClickCB, this) } } - else { + else if (sdk2D && this.sdk === sdk2D) { + if (this.clickCallBack == null && this.options && this.options.id) { + regLeftClickCallback3(this.options.id, this.leftClickCB, this) + } + } else { if (this.clickCallBack == null && this.options && this.options.id) { regLeftClickCallback(this.options.id, this.leftClickCB, this) } @@ -384,12 +390,17 @@ class Base extends Tools { console.error('val:', val, '不是一个function') } else { let sdkD = getSdk().sdkD + let sdk2D = get2DSdk().sdkD if (sdkD && this.sdk === sdkD) { if (this.rightClickCallBack == null && this.entity && this.entity.id) { regRightClickCallback2(this.entity.id, this.rightClickCB, this) } } - else { + else if (sdk2D && this.sdk === sdk2D) { + if (this.clickCallBack == null && this.options && this.options.id) { + regRightClickCallback3(this.options.id, this.leftClickCB, this) + } + } else { if (this.rightClickCallBack == null && this.entity && this.entity.id) { regRightClickCallback(this.entity.id, this.rightClickCB, this) } @@ -407,12 +418,17 @@ class Base extends Tools { console.error('val:', val, '不是一个function') } else { let sdkD = getSdk().sdkD + let sdk2D = get2DSdk().sdkD if (sdkD && this.sdk === sdkD) { if (this.mouseMoveCallBack == null && this.entity && this.entity.id) { regMoveCallback2(this.entity.id, this.mouseMoveCB, this) } } - else { + else if (sdk2D && this.sdk === sdk2D) { + if (this.clickCallBack == null && this.options && this.options.id) { + regMoveCallback3(this.options.id, this.leftClickCB, this) + } + } else { if (this.mouseMoveCallBack == null && this.entity && this.entity.id) { regMoveCallback(this.entity.id, this.mouseMoveCB, this) } diff --git a/src/YJEarth/index.js b/src/YJEarth/index.js index 0f5df67..6fc9060 100644 --- a/src/YJEarth/index.js +++ b/src/YJEarth/index.js @@ -62,9 +62,9 @@ class YJEarth { removeIncetance(id) { this.entityMap.delete(id) - unRegLeftClickCallback(id) - unRegRightClickCallback(id) - unregMoveCallback(id) + unRegLeftClickCallback(this,id) + unRegRightClickCallback(this,id) + unregMoveCallback(this,id) syncSplitData(this, id) } From dff3739a0f14bb797d1c4a8b9aba2894186f5c99 Mon Sep 17 00:00:00 2001 From: zh <972939975@qq.com> Date: Fri, 22 Aug 2025 23:50:22 +0800 Subject: [PATCH 12/17] =?UTF-8?q?=E5=B1=9E=E6=80=A7=E6=A1=86=E7=BA=BF?= =?UTF-8?q?=E6=9D=A1=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Obj/Base/BillboardObject/index.js | 4 +++- static/custom/css/index.css | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Obj/Base/BillboardObject/index.js b/src/Obj/Base/BillboardObject/index.js index 98f8857..2ff4bc9 100644 --- a/src/Obj/Base/BillboardObject/index.js +++ b/src/Obj/Base/BillboardObject/index.js @@ -224,6 +224,7 @@ class BillboardObject extends Base { this.attributeElm.style.top = (winpos.y + pixelOffset.y - (this.options.label.show ? (this.options.label.fontSize / 2) : -(this.options.label.fontSize / 2)) - this.attributeElm.offsetHeight - this.options.attributePos.y + height).toFixed(0) + 'px' this.attributeElm.style.width = this.options.attributePos.width + 'px' this.attributeElm.style.height = this.options.attributePos.height + 'px' + lineElm.style.zIndex = '-1' if (this.options.attributePos.x < -this.options.attributePos.width / 2) { flag = true lineElm.style.left = 'unset' @@ -2602,6 +2603,7 @@ class BillboardObject extends Base { } if (height !== undefined) { this.options.positions.alt = Number(Number(height).toFixed(2)) + this.#_billboardHeight = this.options.positions.alt this._elms.alt && this._elms.alt.forEach(item => { item.value = this.options.positions.alt @@ -2982,7 +2984,7 @@ class BillboardObject extends Base { ` if (!linkHtml && !goodsHtml && !richTextHtml) { - boxHtml = boxHtml + '

暂无属性信息

' + boxHtml = boxHtml + '

暂无属性信息

' } else { boxHtml = boxHtml + ` diff --git a/static/custom/css/index.css b/static/custom/css/index.css index e27a648..2392cf0 100644 --- a/static/custom/css/index.css +++ b/static/custom/css/index.css @@ -3627,6 +3627,7 @@ height: 100%; display: flex; flex-direction: column; + backdrop-filter: blur(2px); } .billboard-attribute-box .DIV-cy-tabs .DIV-cy-tab-top .DIV-cy-tab-pane-title { From 4247327bfa9835873a0a19d29731a13d67c8924b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=8F=E5=A4=A7=E8=83=86?= <1101282782@qq.com> Date: Sat, 23 Aug 2025 00:07:15 +0800 Subject: [PATCH 13/17] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=96=87=E6=9C=AC?= =?UTF-8?q?=E6=A1=86=E4=BD=8D=E7=BD=AE=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Obj/Base/TextBox/index.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Obj/Base/TextBox/index.js b/src/Obj/Base/TextBox/index.js index 0179243..8a974d9 100644 --- a/src/Obj/Base/TextBox/index.js +++ b/src/Obj/Base/TextBox/index.js @@ -54,7 +54,6 @@ class TextBox extends Base { // 配置CSS样式和内容结构 viewer.cesiumWidget.container.appendChild(dom); let posi = Cesium.Cartesian3.fromDegrees(that.options.position.lng.toFixed(4), that.options.position.lat.toFixed(4), that.options.position.alt.toFixed(4)) - that.handler = function () { const position = Cesium.SceneTransforms.wgs84ToWindowCoordinates( viewer.scene, posi @@ -80,12 +79,15 @@ class TextBox extends Base { var cartographic = Cesium.Cartographic.fromCartesian(cartesian); var longitude = Cesium.Math.toDegrees(cartographic.longitude); var latitude = Cesium.Math.toDegrees(cartographic.latitude); + + let height = await that.getClampToHeight({ lng: longitude, lat: latitude }) that.position = { lng: longitude, lat: latitude, - alt: cartographic.height + // alt: cartographic.height + alt: height } - let posi = Cesium.Cartesian3.fromDegrees(longitude.toFixed(4), latitude.toFixed(4), cartographic.height.toFixed(4)) + let posi = Cesium.Cartesian3.fromDegrees(longitude.toFixed(4), latitude.toFixed(4), height.toFixed(4)) that.handler = function () { const position = Cesium.SceneTransforms.wgs84ToWindowCoordinates( From 5ceaa3b649719b79e7ccac1fafb62193c85942f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=8F=E5=A4=A7=E8=83=86?= <1101282782@qq.com> Date: Sat, 23 Aug 2025 01:17:51 +0800 Subject: [PATCH 14/17] =?UTF-8?q?=E6=96=87=E6=9C=AC=E6=A1=86=E4=BD=8D?= =?UTF-8?q?=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Obj/Base/TextBox/index.js | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/Obj/Base/TextBox/index.js b/src/Obj/Base/TextBox/index.js index 8a974d9..5c6f3da 100644 --- a/src/Obj/Base/TextBox/index.js +++ b/src/Obj/Base/TextBox/index.js @@ -53,7 +53,7 @@ class TextBox extends Base { // 配置CSS样式和内容结构 viewer.cesiumWidget.container.appendChild(dom); - let posi = Cesium.Cartesian3.fromDegrees(that.options.position.lng.toFixed(4), that.options.position.lat.toFixed(4), that.options.position.alt.toFixed(4)) + let posi = Cesium.Cartesian3.fromDegrees(that.options.position.lng, that.options.position.lat, that.options.position.alt) that.handler = function () { const position = Cesium.SceneTransforms.wgs84ToWindowCoordinates( viewer.scene, posi @@ -71,8 +71,16 @@ class TextBox extends Base { } async setHandeler(data) { let that = this - const ray = this.sdk.viewer.camera.getPickRay(new Cesium.Cartesian2(data.x, data.y)); - var cartesian = this.sdk.viewer.scene.globe.pick(ray, this.sdk.viewer.scene); + + let cartesian = this.sdk.viewer.scene.pickPosition(new Cesium.Cartesian2(data.x, data.y)); //屏幕坐标转为笛卡尔空间坐标 + // if (!cartesian) return; + + // let c = Cesium.Cartographic.fromCartesian(position); + if (!cartesian) { + const ray = this.sdk.viewer.camera.getPickRay(new Cesium.Cartesian2(data.x, data.y)); + cartesian = this.sdk.viewer.scene.globe.pick(ray, this.sdk.viewer.scene); + } + if (Cesium.defined(cartesian)) { that.sdk.viewer.scene.postRender.removeEventListener(that.handler); @@ -84,10 +92,10 @@ class TextBox extends Base { that.position = { lng: longitude, lat: latitude, - // alt: cartographic.height - alt: height + alt: cartographic.height + // alt: height } - let posi = Cesium.Cartesian3.fromDegrees(longitude.toFixed(4), latitude.toFixed(4), height.toFixed(4)) + let posi = Cesium.Cartesian3.fromDegrees(longitude, latitude, cartographic.height) that.handler = function () { const position = Cesium.SceneTransforms.wgs84ToWindowCoordinates( From 7feab0786a89914588017555f926cb3e8da1f2a9 Mon Sep 17 00:00:00 2001 From: zh <972939975@qq.com> Date: Sat, 23 Aug 2025 02:35:50 +0800 Subject: [PATCH 15/17] =?UTF-8?q?=E8=A7=A3=E5=86=B3=E8=B4=B4=E5=9C=B0svg?= =?UTF-8?q?=E7=BC=96=E8=BE=91=E6=97=B6=E6=8B=96=E5=8A=A8=E5=87=BA=E7=8E=B0?= =?UTF-8?q?=E8=B4=9F=E6=95=B0=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Obj/Base/GroundSvg/index.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Obj/Base/GroundSvg/index.js b/src/Obj/Base/GroundSvg/index.js index 3f1200c..e6916ae 100644 --- a/src/Obj/Base/GroundSvg/index.js +++ b/src/Obj/Base/GroundSvg/index.js @@ -1335,8 +1335,8 @@ class GroundSvg extends Base { w = (Math.cos(radiansW) * distance) * 2 h = (Math.cos(radiansH) * distance) * 2 // scaleY值 - this.scale.x = w / 3.5 - this.scale.y = h / 3.5 + this.scale.x = Math.abs(w) / 3.5 + this.scale.y = Math.abs(h) / 3.5 break case 'svg-control-points_2': case 'svg-control-points_8': @@ -1407,8 +1407,8 @@ class GroundSvg extends Base { w = (Math.cos(radiansW) * distance) * 2 h = (Math.cos(radiansH) * distance) * 2 // scaleY值 - this.scale.x = w / 3.5 - this.scale.y = h / 3.5 + this.scale.x = Math.abs(w) / 3.5 + this.scale.y = Math.abs(h) / 3.5 break case 'svg-control-points_4': bearingW = (((turf.rhumbBearing(pointC, turf.point(controlPoints[4])) + 360) - this.angle) % 360) From f8443b7f62d2bd9831802d6940db3984d5c44756 Mon Sep 17 00:00:00 2001 From: zh <972939975@qq.com> Date: Sat, 23 Aug 2025 02:36:17 +0800 Subject: [PATCH 16/17] =?UTF-8?q?=E4=BC=98=E5=8C=96=E8=B4=B4=E5=9C=B0?= =?UTF-8?q?=E6=96=87=E5=AD=97=E3=80=81=E7=AB=8B=E4=BD=93=E6=96=87=E5=AD=97?= =?UTF-8?q?=E9=A2=9C=E8=89=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Obj/Base/TextObject/GroundText/index.js | 2 +- src/Obj/Base/TextObject/StandText/index.js | 2 +- src/Obj/Materail/WallMaterialProperty.js | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Obj/Base/TextObject/GroundText/index.js b/src/Obj/Base/TextObject/GroundText/index.js index 7009cb0..3eafced 100644 --- a/src/Obj/Base/TextObject/GroundText/index.js +++ b/src/Obj/Base/TextObject/GroundText/index.js @@ -649,7 +649,7 @@ class GroundText extends Base { ctx.font = 200 + 'px serif' ctx.fillStyle = 'rgba(255, 255, 255, 0)' ctx.fillRect(0, 0, maxWidth + 30, 210) - ctx.fillStyle = this.options.color + ctx.fillStyle = 'rgba(255, 255, 255, 1)' ctx.font = '200px serif' ctx.fillText(textArray[i], 0, 210 * (i + 1)) } diff --git a/src/Obj/Base/TextObject/StandText/index.js b/src/Obj/Base/TextObject/StandText/index.js index 6e2a173..1cc67bf 100644 --- a/src/Obj/Base/TextObject/StandText/index.js +++ b/src/Obj/Base/TextObject/StandText/index.js @@ -508,7 +508,7 @@ class StandText extends Base { ctx.font = 200 + "px serif"; ctx.fillStyle = 'rgba(255, 255, 255, 0)' ctx.fillRect(0, 0, maxWidth + 30, 210) - ctx.fillStyle = this.options.color; + ctx.fillStyle = 'rgba(255, 255, 255, 1)'; ctx.font = "200px serif"; ctx.fillText(textArray[i], 0, 210 * (i+1)); } diff --git a/src/Obj/Materail/WallMaterialProperty.js b/src/Obj/Materail/WallMaterialProperty.js index bda11b0..fbe9068 100644 --- a/src/Obj/Materail/WallMaterialProperty.js +++ b/src/Obj/Materail/WallMaterialProperty.js @@ -238,8 +238,8 @@ function StreamWall2() { else { material.alpha = 1.0; } - material.diffuse = color.rgb*0.0; - material.emission = color.rgb * 1.0; + material.diffuse = colorImage.rgb * color.rgb*0.0; + material.emission = colorImage.rgb * color.rgb * 1.0; return material; }`, components: { From 819166bae044d9afaf65ee0586eb67e9f10c8c51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=8F=E5=A4=A7=E8=83=86?= <1101282782@qq.com> Date: Sat, 23 Aug 2025 03:21:18 +0800 Subject: [PATCH 17/17] =?UTF-8?q?=E6=96=87=E6=9C=AC=E6=A1=86=E7=82=B9?= =?UTF-8?q?=E5=87=BB=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Obj/Base/TextBox/index.js | 21 +++++++++++++++++++++ src/YJEarth/index.js | 7 ++++--- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/Obj/Base/TextBox/index.js b/src/Obj/Base/TextBox/index.js index 5c6f3da..bd8cfbc 100644 --- a/src/Obj/Base/TextBox/index.js +++ b/src/Obj/Base/TextBox/index.js @@ -69,6 +69,17 @@ class TextBox extends Base { that.textDom = dom; } + async isClick(posi, id) { + let params = [ + { + position: posi + }, + id, + null + ] + + this.clickCallBack({ position: posi }, id, null) + } async setHandeler(data) { let that = this @@ -118,6 +129,16 @@ class TextBox extends Base { async returnFun() { return this.handler } + get onClick() { + return this.clickCallBack + } + set onClick(val) { + if (val && typeof val !== 'function') { + console.error('val:', val, '不是一个function') + } else { + this.clickCallBack = val + } + } get show() { return this.options.show } diff --git a/src/YJEarth/index.js b/src/YJEarth/index.js index 6fc9060..775f153 100644 --- a/src/YJEarth/index.js +++ b/src/YJEarth/index.js @@ -62,9 +62,9 @@ class YJEarth { removeIncetance(id) { this.entityMap.delete(id) - unRegLeftClickCallback(this,id) - unRegRightClickCallback(this,id) - unregMoveCallback(this,id) + unRegLeftClickCallback(this, id) + unRegRightClickCallback(this, id) + unregMoveCallback(this, id) syncSplitData(this, id) } @@ -436,6 +436,7 @@ class YJEarth { textList[i].style['pointer-events'] = 'all' textList[i].querySelector('textarea').focus() _this.isLeftClick = true + _this.entityMap.get(_this.clickTextDom.id).isClick(movement.position, _this.clickTextDom.id) break; } }