Files
sdk4.0_new/src/Obj/Analysis/Flat/index.js
2025-09-11 18:04:44 +08:00

493 lines
17 KiB
JavaScript

import Base from "../../Base/index";
import Dialog from '../../../BaseDialog'
import { setActiveViewer, closeRotateAround, closeViewFollow } from '../../../Global/global'
let FlatList = {}
class Flat extends Base {
/**
* @constructor
* @description 模型压平
* @param sdk
* @param {Cesium.Cesium3DTileset} tileset 三维模型
* @param {Object} options
* @param {string} attr.id id
* @param {Cesium.Cartesian3[]} attr.positions 压平面坐标
*/
constructor(sdk, tileset, options = {}, _Dialog = {}) {
super(sdk)
if (!tileset || !this.sdk || !this.sdk.viewer) return;
this.options = { ...options }
this.options.id = options.id || this.randomString()
this.options.name = options.name || '压平面'
this.options.positions = options.positions || []
this.options.show = (options.show || options.show === false) ? options.show : true
this.tileset = tileset;
this.Dialog = _Dialog
if (!this.options.height && this.options.height !== 0) {
let height = this.options.positions[0].alt
for (let i = 0; i < this.options.positions.length; i++) {
if (height > this.options.positions[i].alt) {
height = this.options.positions[i].alt
}
}
this.options.height = height
}
if (FlatList[this.tileset.id]) {
FlatList[this.tileset.id].push({ ...this.options })
}
else {
FlatList[this.tileset.id] = [{ ...this.options }]
}
this.center = tileset.boundingSphere.center.clone();
this.center84 = this.cartesian3Towgs84(this.center, this.sdk.viewer)
this.matrix = Cesium.Transforms.eastNorthUpToFixedFrame(this.center.clone());
this.localMatrix = Cesium.Matrix4.inverse(this.matrix, new Cesium.Matrix4());
// this.entity = {
// id: this.options.id
// }
this.addFlat()
// Flat.createPolygon(this)
}
get show() {
return this.options.show
}
set show(v) {
this.options.show = v
for (let i = 0; i < FlatList[this.tileset.id].length; i++) {
if (FlatList[this.tileset.id][i].id == this.options.id) {
FlatList[this.tileset.id][i].show = v
}
}
this.addFlat()
}
get height() {
return this.options.height
}
set height(v) {
this.options.height = Number(v)
for (let i = 0; i < FlatList[this.tileset.id].length; i++) {
if (FlatList[this.tileset.id][i].id == this.options.id) {
FlatList[this.tileset.id][i].height = Number(v)
}
}
this.addFlat()
}
get name() {
return this.options.name
}
set name(v) {
this.options.name = v
for (let i = 0; i < FlatList[this.tileset.id].length; i++) {
if (FlatList[this.tileset.id][i].id == this.options.id) {
FlatList[this.tileset.id][i].name = v
}
}
}
addFlat() {
let localPositionsArr = []
for (let i = 0; i < FlatList[this.tileset.id].length; i++) {
let item = FlatList[this.tileset.id][i];
if (item.show) {
const positions = item.positions;
let height = item.height
let fromDegreesArray = []
for (let i = 0; i < positions.length; i++) {
fromDegreesArray.push(positions[i].lng, positions[i].lat)
}
FlatList[this.tileset.id][i].flatHeight = height - this.center84.alt
let localCoor = this.cartesiansToLocal(Cesium.Cartesian3.fromDegreesArray(fromDegreesArray));
localPositionsArr.push(localCoor);
}
}
const funstr = this.getIsinPolygonFun(localPositionsArr);
let str = ``;
for (let i = 0; i < localPositionsArr.length; i++) {
const coors = localPositionsArr[i];
const n = coors.length;
let instr = ``;
coors.forEach((coordinate, index) => {
instr += `points_${n}[${index}] = vec2(${coordinate[0]}, ${coordinate[1]});\n`;
})
str += `
${instr}
if(isPointInPolygon_${n}(position2D)){
vec4 tileset_local_position_transformed = vec4(tileset_local_position.x, tileset_local_position.y, ground_z + ${FlatList[this.tileset.id][i].flatHeight}, 1.0);
vec4 model_local_position_transformed = czm_inverseModel * u_tileset_localToWorldMatrix * tileset_local_position_transformed;
vsOutput.positionMC.xy = model_local_position_transformed.xy;
vsOutput.positionMC.z = model_local_position_transformed.z+ modelMC.z*0.002;
return;
}`;
}
this.updateShader(funstr, str);
}
// static createPolygon(that) {
// let color = '#ffffff'
// let linecolor = '#000000'
// let positions = that.options.positions
// let fromDegreesArray = []
// for (let i = 0; i < positions.length; i++) {
// fromDegreesArray.push(positions[i].lng, positions[i].lat, that.options.height)
// }
// that.positions = Cesium.Cartesian3.fromDegreesArrayHeights(fromDegreesArray)
// that.entity = that.sdk.viewer.entities.add({
// show: that.options.show,
// id: that.options.id,
// polyline: {
// positions: [...that.positions, that.positions[0], that.positions[1]],
// width: 2,
// material: Cesium.Color.fromCssColorString(linecolor),
// depthFailMaterial: new Cesium.PolylineDashMaterialProperty({
// color: Cesium.Color.YELLOW
// }),
// clampToGround: false,
// zIndex: that.sdk._entityZIndex
// },
// })
// that.sdk._entityZIndex++
// }
remove() {
FlatList[this.tileset.id] = FlatList[this.tileset.id].filter((attr) => {
return attr.id != this.options.id;
})
let localPositionsArr = [];
for (let i = 0; i < FlatList[this.tileset.id].length; i++) {
let item = FlatList[this.tileset.id][i];
if (item.show) {
const positions = item.positions;
let height = item.height
let fromDegreesArray = []
for (let i = 0; i < positions.length; i++) {
fromDegreesArray.push(positions[i].lng, positions[i].lat)
}
FlatList[this.tileset.id][i].flatHeight = height - this.center84.alt
let localCoor = this.cartesiansToLocal(Cesium.Cartesian3.fromDegreesArray(fromDegreesArray));
localPositionsArr.push(localCoor);
}
}
const funstr = this.getIsinPolygonFun(localPositionsArr);
let str = ``;
for (let i = 0; i < localPositionsArr.length; i++) {
const coors = localPositionsArr[i];
const n = coors.length;
let instr = ``;
coors.forEach((coordinate, index) => {
instr += `points_${n}[${index}] = vec2(${coordinate[0]}, ${coordinate[1]});\n`;
})
str += `
${instr}
if(isPointInPolygon_${n}(position2D)){
vec4 tileset_local_position_transformed = vec4(tileset_local_position.x, tileset_local_position.y, ground_z + ${FlatList[this.tileset.id][i].flatHeight}, 1.0);
vec4 model_local_position_transformed = czm_inverseModel * u_tileset_localToWorldMatrix * tileset_local_position_transformed;
vsOutput.positionMC.xy = model_local_position_transformed.xy;
vsOutput.positionMC.z = model_local_position_transformed.z+ modelMC.z*0.002;
return;
}`;
}
this.updateShader(funstr, str);
}
// 根据数组长度,构建 判断点是否在面内 的压平函数
getIsinPolygonFun(polygons) {
let pmap = polygons.map((polygon) => polygon.length);
let uniqueArray = this.getUniqueArray(pmap);
let str = ``;
uniqueArray.forEach(length => {
str += `
vec2 points_${length}[${length}];
bool isPointInPolygon_${length}(vec2 point){
int nCross = 0; // 交点数
const int n = ${length};
for(int i = 0; i < n; i++){
vec2 p1 = points_${length}[i];
vec2 p2 = points_${length}[int(mod(float(i+1),float(n)))];
if(p1[1] == p2[1]){
continue;
}
if(point[1] < min(p1[1], p2[1])){
continue;
}
if(point[1] >= max(p1[1], p2[1])){
continue;
}
float x = p1[0] + ((point[1] - p1[1]) * (p2[0] - p1[0])) / (p2[1] - p1[1]);
if(x > point[0]){
nCross++;
}
}
return int(mod(float(nCross), float(2))) == 1;
}
`
})
return str
}
updateShader(vtx1, vtx2) {
let flatCustomShader = new Cesium.CustomShader({
uniforms: {
u_tileset_localToWorldMatrix: {
type: Cesium.UniformType.MAT4,
value: this.matrix,
},
u_tileset_worldToLocalMatrix: {
type: Cesium.UniformType.MAT4,
value: this.localMatrix,
},
u_flatHeight: {
type: Cesium.UniformType.FLOAT,
value: this.flatHeight,
},
},
vertexShaderText: `
// 所有isPointInPolygon函数
${vtx1}
void vertexMain(VertexInput vsInput, inout czm_modelVertexOutput vsOutput){
vec3 modelMC = vsInput.attributes.positionMC;
vec4 model_local_position = vec4(modelMC.x, modelMC.y, modelMC.z, 1.0);
vec4 tileset_local_position = u_tileset_worldToLocalMatrix * czm_model * model_local_position;
vec2 position2D = vec2(tileset_local_position.x,tileset_local_position.y);
float ground_z = 0.0;
// 多个多边形区域
${vtx2}
}`,
});
this.tileset.customShader = flatCustomShader;
this.sdk.viewer.scene.requestRender();
}
// 数组去重,不能处理嵌套的数组
getUniqueArray = (arr) => {
return arr.filter(function (item, index, arr) {
//当前元素,在原始数组中的第一个索引==当前索引值,否则返回当前元素
return arr.indexOf(item, 0) === index;
});
}
// 世界坐标转数组局部坐标
cartesiansToLocal(positions) {
let arr = [];
for (let i = 0; i < positions.length; i++) {
let position = positions[i];
let localp = Cesium.Matrix4.multiplyByPoint(
this.localMatrix,
position.clone(),
new Cesium.Cartesian3()
)
arr.push([localp.x, localp.y]);
}
return arr;
}
/**
* 飞到
*/
async flyTo() {
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.line && this.options.line.positions) {
position = { ...this.options.line.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.options.positions.length; i++) {
let a = Cesium.Cartesian3.fromDegrees(this.options.positions[i].lng, this.options.positions[i].lat, this.center84.alt)
positionArray.push(a.x, a.y, a.z)
}
let BoundingSphere = Cesium.BoundingSphere.fromVertices(positionArray)
this.sdk.viewer.camera.flyToBoundingSphere(BoundingSphere, {
offset: {
heading: Cesium.Math.toRadians(0.0),
pitch: Cesium.Math.toRadians(-90.0),
roll: Cesium.Math.toRadians(0.0)
}
})
}
}
async edit(state) {
if (state) {
this.originalOptions = this.deepCopyObj(this.options)
// this._DialogObject = await new Dialog(this.sdk.viewer._container, {
// title: '压平面属性', left: '180px', top: '100px',
// removeCallBack: () => {
// this.Dialog.removeCallBack && this.Dialog.removeCallBack()
// },
// closeCallBack: () => {
// this.reset()
// this.Dialog.closeCallBack && this.Dialog.closeCallBack()
// }
// })
// await this._DialogObject.init()
// 内容部分
// let contentElm = document.createElement('div');
// contentElm.innerHTML = `
// <span class="custom-divider"></span>
// <div class="div-item">
// <div class="row">
// <div class="col">
// <span class="label" style="width: 56px;flex: 0 0 56px;">名称</span>
// <input class="input input-name">
// </div>
// </div>
// <div class="row">
// <div class="col">
// <span class="label" style="width: 56px;flex: 0 0 56px;">压平高度</span>
// <div class="input-number input-number-unit-1">
// <input class="input flat-height" type="number" title="" min="-9999999" max="999999999">
// <span class="unit">m</span>
// <span class="arrow"></span>
// </div>
// </div>
// </div>
// </div>
// `
// this._DialogObject.contentAppChild(contentElm)
let contentElm = document.getElementsByClassName('flatPlane')[0]
let name_elm = contentElm.getElementsByClassName('input-name')[0]
name_elm.value = this.options.name
name_elm.addEventListener('input', () => {
this.name = name_elm.value
})
let height_elm = contentElm.getElementsByClassName('flat-height')[0]
height_elm.value = this.options.height
height_elm.addEventListener('input', () => {
this.height = Number(height_elm.value)
this.addFlat()
})
// let confirmElm = document.createElement('button');
// confirmElm.className = 'btn'
// confirmElm.innerHTML = '确认'
// this._DialogObject.footAppChild(confirmElm)
// confirmElm.addEventListener('click', () => {
// if (!this.options.name) {
// this.options.name = '压平面'
// }
// this.originalOptions = this.deepCopyObj(this.options)
// this._DialogObject.close()
// this.Dialog.confirmCallBack && this.Dialog.confirmCallBack(this.options)
// })
// let flatElm = document.createElement('button');
// flatElm.className = 'btn'
// flatElm.innerHTML = '<svg class="icon-edit"><use xlink:href="#yj-icon-edit"></use></svg>二次编辑'
// flatElm.style.width = 'auto'
// flatElm.style.position = 'absolute'
// flatElm.style.left = '10px'
// this._DialogObject.footAppChild(flatElm)
// flatElm.addEventListener('click', () => {
// console.log('二次编辑')
// })
}
else {
if (this._DialogObject && this._DialogObject.close) {
this._DialogObject.close()
this._DialogObject = null
}
}
}
sure() {
if (!this.options.name) {
this.options.name = '压平面'
}
this.originalOptions = this.deepCopyObj(this.options)
// this._DialogObject.close()
// this.Dialog.confirmCallBack && this.Dialog.confirmCallBack(this.options)
}
reset() {
this.options = this.deepCopyObj(this.originalOptions)
this.name = this.options.name
this.height = this.options.height
this.addFlat()
}
flatEdit(state) {
if (state) {
let positions = that.options.positions
let fromDegreesArray = []
for (let i = 0; i < positions.length; i++) {
fromDegreesArray.push(positions[i].lng, positions[i].lat, FlatList[this.tileset.id])
}
that.positions = Cesium.Cartesian3.fromDegreesArrayHeights(fromDegreesArray)
}
}
flicker() { }
}
export default Flat;