This commit is contained in:
zh
2025-09-01 16:17:11 +08:00
parent d802602200
commit 6fa99df21c
1035 changed files with 377083 additions and 1 deletions

View File

@ -0,0 +1,125 @@
class CreatePolygon {
constructor(viewer) {
if (!viewer) throw new Error("no viewer object!");
this.activePoints = [];
this.viewer = viewer;
this.handler = undefined;
this.init();
}
init() {
this.activeShapePoints = [];
this.floatingPoint = undefined;
this.activeShape = undefined;
this.activePoints.forEach((element) => {
this.viewer.entities.remove(element);
});
this.activePoints = [];
this.initHandler();
}
start(callback) {
const $this = this;
$this.keyDownStatus(true);
$this.init();
$this.handler = new Cesium.ScreenSpaceEventHandler($this.viewer.canvas);
$this.handler.setInputAction(function (event) {
let earthPosition = $this.viewer.scene.pickPosition(event.position);
if (Cesium.defined(earthPosition)) {
if ($this.activeShapePoints.length === 0) {
$this.floatingPoint = $this.createPoint(earthPosition);
$this.activeShapePoints.push(earthPosition);
let dynamicPositions = new Cesium.CallbackProperty(function () {
return new Cesium.PolygonHierarchy($this.activeShapePoints);
}, false);
$this.activeShape = $this.drawShape(dynamicPositions); //绘制动态图
}
$this.activeShapePoints.push(earthPosition);
$this.createPoint(earthPosition);
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
$this.handler.setInputAction(function (event) {
if (Cesium.defined($this.floatingPoint)) {
let newPosition = $this.viewer.scene.pickPosition(event.endPosition);
if (Cesium.defined(newPosition)) {
$this.floatingPoint.position.setValue(newPosition);
$this.activeShapePoints.pop();
$this.activeShapePoints.push(newPosition);
}
}
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
$this.handler.setInputAction(function () {
$this.activeShapePoints.pop(); //去除最后一个动态点
if ($this.activeShapePoints.length) {
$this.polygon = $this.drawShape($this.activeShapePoints); //绘制最终图
}
$this.viewer.entities.remove($this.floatingPoint); //去除动态点图形(当前鼠标点)
$this.viewer.entities.remove($this.activeShape); //去除动态图形
$this.activePoints.forEach((element) => {
$this.viewer.entities.remove(element);
});
$this.handler.destroy();
setTimeout(() => {
if (typeof callback == "function") callback();
}, 1000);
}, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
}
initHandler() {
if (this.handler) {
this.handler.destroy();
this.handler = undefined;
}
}
createPoint(worldPosition) {
let point = this.viewer.entities.add({
position: worldPosition,
point: {
color: Cesium.Color.SKYBLUE,
pixelSize: 5,
},
});
this.activePoints.push(point);
return point;
}
drawShape(positionData) {
let shape = this.viewer.entities.add({
polygon: {
hierarchy: positionData,
material: new Cesium.ColorMaterialProperty(
Cesium.Color.BLUE.withAlpha(0.4)
),
zIndex: 99999999
},
});
return shape;
}
//快捷键//Ctrl + Z
keyDownStatus(bool) {
const $this = this;
document.onkeydown = function (event) {
if (event.ctrlKey && window.event.keyCode == 90) {
if (!bool) {
return false;
}
$this.activeShapePoints.pop();
$this.viewer.entities.remove(
$this.activePoints[$this.activePoints.length - 1]
);
$this.activePoints.pop();
}
};
}
/**
* Cesium中世界坐标系笛卡尔转经纬度
* @param {*} cartesian3
* @returns 经纬度
*/
Cartesian3ToDgrees(cartesian3) {
let cartographic =
window.viewer.scene.globe.ellipsoid.cartesianToCartographic(cartesian3);
let lat = Cesium.Math.toDegrees(cartographic.latitude);
let lng = Cesium.Math.toDegrees(cartographic.longitude);
let alt = cartographic.height;
return { lng: lng, lat: lat, alt: alt };
}
}
export default CreatePolygon;

View File

@ -0,0 +1,75 @@
function html() {
return `
<span class="custom-divider"></span>
<div class="div-item">
<div class="row">
<div class="col">
<span class="label" style="flex: 0 0 70px;">绘制分析区域</span>
<button class="draw-btn"><svg class="icon-edit"><use xlink:href="#yj-icon-edit"></use></svg>开始绘制</button>
</div>
</div>
</div>
<span class="custom-divider"></span>
<div class="div-item">
<div class="row">
<div class="col">
<span class="label">基准高度</span>
<div class="input-number input-number-unit-1">
<input class="input" type="number" title="" min="-999999" max="999999" name="height">
<span class="unit">m</span>
<span class="arrow"></span>
</div>
</div>
<div class="col">
<span class="label">精度</span>
<div class="input-number input-number-unit">
<input class="input" type="number" title="" min="1" max="1250" name="precision">
<span class="arrow"></span>
</div>
</div>
</div>
</div>
<span class="custom-divider"></span>
<div class="div-item">
<div class="row">
<div class="col">
<span class="label" style="flex: 0 0 74px;">总分析面积:</span>
<span class="text-number" name="allArea">0</span>
<span class="unit text-number">m²</span>
</div>
<div class="col">
<span class="label" style="flex: 0 0 90px;">无须填挖面积:</span>
<span class="text-number" name="noArea">0</span>
<span class="unit text-number">m²</span>
</div>
</div>
<div class="row">
<div class="col">
<span class="label" style="flex: 0 0 74px;">填方面积:</span>
<span class="text-number" name="fillArea">0</span>
<span class="unit text-number">m²</span>
</div>
<div class="col">
<span class="label" style="flex: 0 0 90px;">挖方面积:</span>
<span class="text-number" name="cutArea">0</span>
<span class="unit text-number">m²</span>
</div>
</div>
<div class="row">
<div class="col">
<span class="label" style="flex: 0 0 74px;">填方体积:</span>
<span class="text-number" name="fillVolume">0</span>
<span class="unit text-number">m³</span>
</div>
<div class="col">
<span class="label" style="flex: 0 0 90px;">挖方体积:</span>
<span class="text-number" name="cutVolume">0</span>
<span class="unit text-number">m³</span>
</div>
</div>
</div>
<span class="custom-divider"></span>
`
}
export { html }

View File

@ -0,0 +1,354 @@
import Dialog from '../../../BaseDialog';
import { html } from "./_element";
// import CreatePolygon from "./CreatePolygon";
import DrawPolygon from "../../../Draw/drawPolygon"
class CutFillAnalysis {
/**
* @constructor 填挖方分析
* @param sdk
* **/
constructor(sdk, options = {}, _Dialog = {}) {
this.sdk = sdk;
this.viewer = sdk.viewer;
// if (!positions) throw new Error("no positions object!");
// this.positions = positions;
this.height = options.height || 70
this.maxHeigh = -1000000;
this.precision = options.precision || 125
this.Dialog = _Dialog
this.result = {
allArea: "",
cutArea: "",
cutVolume: "",
fillArea: "",
fillVolume: "",
noArea: "",
}
this.entities = []
this.Draw = new DrawPolygon(this.sdk)
YJ.Analysis.AnalysesResults.push(this)
CutFillAnalysis.EditBox(this)
}
create() {
this.clean()
this.Draw.start((a, positions) => {
if(!positions || positions.length<3) {
let _error = '最少需要三个坐标!'
console.warn(_error)
window.ELEMENT && window.ELEMENT.Message({
message: _error,
type: 'warning',
duration: 1500
});
return
}
let fromDegreesArray = []
for (let i = 0; i < positions.length; i++) {
fromDegreesArray.push(positions[i].lng, positions[i].lat, positions[i].alt)
}
this.positions = Cesium.Cartesian3.fromDegreesArrayHeights(fromDegreesArray)
this.createPolygonGeo(this.positions);
this.result = this.VolumeAnalysis();
this.viewer.scene.screenSpaceCameraController.enableCollisionDetection = false; //允许相机进入地下
})
// const $this = this;
// if (!this.cp) {
// this.cp = new CreatePolygon(this.viewer)
// }
// this.cp.start(function () {
// console.log($this.cp.activeShapePoints)
// $this.positions = $this.cp.activeShapePoints;
// $this.createPolygonGeo($this.positions);
// $this.result = $this.VolumeAnalysis();
// $this.viewer.entities.remove($this.cp.polygon);
// $this.viewer.scene.screenSpaceCameraController.enableCollisionDetection = false; //允许相机进入地下
// });
}
createPolygonGeo(points) {
//计算网格粒度-精度
let granularity = Math.PI / Math.pow(2, 11);
granularity = granularity / this.precision;
let polygonGeometry = new Cesium.PolygonGeometry.fromPositions({
positions: points,
vertexFormat: Cesium.PerInstanceColorAppearance.FLAT_VERTEX_FORMAT,
granularity: granularity,
});
//创建自定义平面几何体
this.geom = new Cesium.PolygonGeometry.createGeometry(polygonGeometry);
}
VolumeAnalysis() {
let cutArea = 0,
cutVolume = 0,
fillArea = 0,
fillVolume = 0,
noArea = 0;
const indices = this.geom.indices; //获取顶点索引数据
if (!this.geom || !this.geom.attributes || !this.geom.attributes.position) {
return;
}
const positions = this.geom.attributes.position.values;
for (let index = 0; index < indices.length; index += 3) {
const pos0 = this.returnPosition(positions, indices[index]);
const pos1 = this.returnPosition(positions, indices[index + 1]);
const pos2 = this.returnPosition(positions, indices[index + 2]);
let entity = this.viewer.entities.add({
name: "三角面",
polygon: {
hierarchy: [pos0.heightPos, pos1.heightPos, pos2.heightPos],
perPositionHeight: true,
material: Cesium.Color.fromRandom(),
extrudedHeight: this.height,
outline: true,
outlineColor: Cesium.Color.BLACK,
},
});
this.entities.push(entity)
//水平状态下三角形面积
const area = this.computeArea4Triangle(
pos0.noHeightPos,
pos1.noHeightPos,
pos2.noHeightPos
);
//计算三个点的均高
const height = (pos0.height + pos1.height + pos2.height) / 3;
if (height < this.height) {
// 需要填方的部分
fillArea += area;
const volume = area * (this.height - height);
fillVolume += volume;
} else if (height == this.height) {
noArea += area;
} else {
// 需要挖方的部分
cutArea += area;
const volume = area * (height - this.height);
cutVolume += volume;
}
}
const allArea = cutArea + fillArea + noArea;
// this.result = {
// allArea,
// cutArea,
// cutVolume,
// fillArea,
// fillVolume,
// noArea,
// };
this.result.allArea = allArea
this.result.cutArea = cutArea
this.result.cutVolume = cutVolume
this.result.fillArea = fillArea
this.result.fillVolume = fillVolume
this.result.noArea = noArea
return this.result;
}
computeCentroid4Polygon(positions) {
let x = [],
y = [];
let allX = 0,
allY = 0;
for (let i = 0; i < positions.length; i++) {
let cartographic = Cesium.Cartographic.fromCartesian(positions[i]);
allX += cartographic.longitude;
allY += cartographic.latitude;
x.push(cartographic.longitude);
y.push(cartographic.latitude);
}
let centroidx = allX / positions.length;
let centroidy = allY / positions.length;
const Cartographic = new Cesium.Cartographic(centroidx, centroidy);
return Cesium.Cartesian3.fromRadians(
Cartographic.longitude,
Cartographic.latitude,
this.maxHeigh + 30
);
}
/**
* 海伦公式求取三角形面积
* @param {*} pos1
* @param {*} pos2
* @param {*} pos3
* @returns 三角形面积㎡
*/
computeArea4Triangle(pos1, pos2, pos3) {
let a = Cesium.Cartesian3.distance(pos1, pos2);
let b = Cesium.Cartesian3.distance(pos2, pos3);
let c = Cesium.Cartesian3.distance(pos3, pos1);
let S = (a + b + c) / 2;
return Math.sqrt(S * (S - a) * (S - b) * (S - c));
}
returnPosition(positions, index) {
let cartesian = new Cesium.Cartesian3(
positions[index * 3],
positions[index * 3 + 1],
positions[index * 3 + 2]
);
let cartographic = Cesium.Cartographic.fromCartesian(cartesian);
let height = this.viewer.scene.sampleHeightSupported
? this.viewer.scene.sampleHeight(cartographic)
: this.viewer.scene.globe.getHeight(cartographic);
if (height > this.maxHeigh) {
this.maxHeigh = height;
}
return {
heightPos: Cesium.Cartesian3.fromRadians(
cartographic.longitude,
cartographic.latitude,
height
),
noHeightPos: Cesium.Cartesian3.fromRadians(
cartographic.longitude,
cartographic.latitude,
0
),
height: height,
};
}
static async EditBox(that) {
if (that._DialogObject && that._DialogObject.close) {
that._DialogObject.close()
that._DialogObject = null
}
that._DialogObject = await new Dialog(that.sdk.viewer._container, {
title: '土方分析', left: '180px', top: '100px',
closeCallBack: () => {
that.clean()
that.Dialog.closeCallBack && that.Dialog.closeCallBack()
},
})
await that._DialogObject.init()
let contentElm = document.createElement('div');
contentElm.innerHTML = html()
that._DialogObject.contentAppChild(contentElm)
that._DialogObject._element.body.className = that._DialogObject._element.body.className + ' cut-fill'
// 高度值
let e_height = contentElm.querySelector("input[name='height']")
e_height.value = that.height
e_height.addEventListener('blur', (e) => {
let value = e.target.value
if (e.data != '.' && (e.data != '-' || e.target.value)) {
value = Number(value)
if ((e.target.max) && value > Number(e.target.max)) {
value = Number(e.target.max)
}
if ((e.target.min) && value < Number(e.target.min)) {
value = Number(e.target.min)
}
e_height.value = value
that.height = e_height.value;
}
});
// 精度值
let e_precision = contentElm.querySelector("input[name='precision']")
e_precision.value = that.precision
e_precision.addEventListener('blur', (e) => {
let value = Number(e.target.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)
}
e_precision.value = value
that.precision = e_precision.value;
});
// 总分析面积
let e_allArea = contentElm.querySelector("span[name='allArea']")
e_allArea.innerHTML = that.result.allArea || 0
Object.defineProperty(that.result, 'allArea', {
get() {
return e_allArea.innerHTML
},
set(value) {
e_allArea.innerHTML = Number(value.toFixed(4))
}
})
// 填方面积
let e_fillArea = contentElm.querySelector("span[name='fillArea']")
e_fillArea.innerHTML = that.result.fillArea || 0
Object.defineProperty(that.result, 'fillArea', {
get() {
return e_fillArea.innerHTML
},
set(value) {
e_fillArea.innerHTML = Number(value.toFixed(4))
}
})
// 填方体积
let e_fillVolume = contentElm.querySelector("span[name='fillVolume']")
e_fillVolume.innerHTML = that.result.fillVolume || 0
Object.defineProperty(that.result, 'fillVolume', {
get() {
return e_fillVolume.innerHTML
},
set(value) {
e_fillVolume.innerHTML = Number(value.toFixed(4))
}
})
// 挖方面积
let e_cutArea = contentElm.querySelector("span[name='cutArea']")
e_cutArea.innerHTML = that.result.cutArea || 0
Object.defineProperty(that.result, 'cutArea', {
get() {
return e_cutArea.innerHTML
},
set(value) {
e_cutArea.innerHTML = Number(value.toFixed(4))
}
})
// 挖方体积
let e_cutVolume = contentElm.querySelector("span[name='cutVolume']")
e_cutVolume.innerHTML = that.result.cutVolume || 0
Object.defineProperty(that.result, 'cutVolume', {
get() {
return e_cutVolume.innerHTML
},
set(value) {
e_cutVolume.innerHTML = Number(value.toFixed(4))
}
})
// 无须填挖面积
let e_noArea = contentElm.querySelector("span[name='noArea']")
e_noArea.innerHTML = that.result.noArea || 0
Object.defineProperty(that.result, 'noArea', {
get() {
return e_noArea.innerHTML
},
set(value) {
e_noArea.innerHTML = Number(value.toFixed(4))
}
})
let newDivBtn = contentElm.getElementsByClassName('draw-btn')[0];
newDivBtn.addEventListener('click', () => {
that.create()
});
}
clean() {
this.Draw && this.Draw.end()
for (let i = 0; i < this.entities.length; i++) {
this.viewer.entities.remove(this.entities[i])
}
}
destroy() {
this.clean()
if (this._DialogObject && this._DialogObject.close) {
this._DialogObject.close()
this._DialogObject = null
}
}
}
export default CutFillAnalysis;