Files
sdk4.0/src/Obj/Base/RoadObject/index-直接连接.js
2025-08-22 18:28:38 +08:00

616 lines
22 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* @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