493 lines
17 KiB
JavaScript
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;
|