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() {
+
+
`
}
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 `
+
+
+
+
+
+
模型间隔
+
+
线型选择
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 新增模型风格设置
+
+
+
+
+
+
+
+
+
+ 显隐
+
+
+
+
图标
+
+
![]()
+
+
+
+
默认图标
+
+
![]()
+
+
+
+
+
+
+
+
+
+
+ `
+}
+
+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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