<template>
|
<div id="cesiumContainer">
|
<div class="volume-group">
|
<span class="item-group">
|
<div v-for="item in up" :key="item.name" class="item">
|
上顶点{{ item.name }}:
|
<el-input
|
v-model="item.coords"
|
size="mini"
|
placeholder="请输入坐标值"
|
></el-input>
|
</div>
|
</span>
|
<span class="item-group">
|
<div v-for="item in down" :key="item.name" class="item">
|
下顶点{{ item.name }}:
|
<el-input
|
v-model="item.coords"
|
size="mini"
|
placeholder="请输入坐标值"
|
></el-input>
|
</div>
|
</span>
|
<span class="item-layer">
|
<el-checkbox v-model="upPolygon">上表面</el-checkbox>
|
<el-checkbox v-model="downPolygon">下表面</el-checkbox>
|
</span>
|
</div>
|
</div>
|
</template>
|
<script>
|
import * as turf from "@turf/turf";
|
const Cesium = window.Cesium;
|
let viewer = undefined;
|
let upPolygonObj, downPolygonObj;
|
export default {
|
data() {
|
return {
|
up: [
|
{
|
name: "a1",
|
coords: "115.308846,39.302114,140.21",
|
},
|
{
|
name: "b1",
|
coords: "115.309067,39.302592,128.42",
|
},
|
{
|
name: "c1",
|
coords: "115.309595,39.302221,153.68",
|
},
|
],
|
down: [
|
{
|
name: "a2",
|
coords: "115.308846,39.302114,64.54",
|
},
|
{
|
name: "b2",
|
coords: "115.309067,39.302592,72.47",
|
},
|
{
|
name: "c2",
|
coords: "115.309595,39.302221,51.16",
|
},
|
],
|
upPos: [],
|
downPos: [],
|
upClipPos: [], //上表面最低点集合
|
downClipPos: [], //下表面最高点集合
|
upMinHight: -Infinity, //上表面最低点高度值
|
downMaxHight: Infinity, //下表面最高点高度值
|
upPolygon: true,
|
downPolygon: true,
|
upVolume: 0,
|
centerVolume: 0,
|
downVolume: 0,
|
};
|
},
|
watch: {
|
upPolygon(val) {
|
if (upPolygonObj) {
|
upPolygonObj.show = val;
|
}
|
},
|
downPolygon(val) {
|
if (downPolygonObj) {
|
downPolygonObj.show = val;
|
}
|
},
|
},
|
mounted() {
|
let key =
|
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIwZDhhOThhNy0zMzUzLTRiZDktYWM3Ni00NGI5MGY2N2UwZDUiLCJpZCI6MjQzMjYsInNjb3BlcyI6WyJhc3IiLCJnYyJdLCJpYXQiOjE1ODUwMzUwNDh9.DYuDF_RPKe5_8w849_y-sutM68LM51O9o3bTt_3rF1w";
|
Cesium.Ion.defaultAccessToken = key;
|
window.viewer = viewer = new Cesium.Viewer("cesiumContainer", {
|
imageryProvider: new Cesium.ArcGisMapServerImageryProvider({
|
url: "https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer",
|
}),
|
// terrainProvider: this.Cesium.createWorldTerrain(),
|
geocoder: true,
|
homeButton: true,
|
sceneModePicker: true,
|
baseLayerPicker: true,
|
navigationHelpButton: true,
|
animation: true,
|
timeline: true,
|
fullscreenButton: true,
|
vrButton: true,
|
//关闭点选出现的提示框
|
selectionIndicator: false,
|
infoBox: false,
|
});
|
viewer._cesiumWidget._creditContainer.style.display = "none"; // 隐藏版权
|
this.initPoints();
|
},
|
methods: {
|
//创建上下节点
|
initPoints() {
|
// 绘制上顶点
|
for (let i = 0; i < this.up.length; i++) {
|
const upEle = this.up[i];
|
const coords = upEle.coords.split(",");
|
const x = Number(coords[0]);
|
const y = Number(coords[1]);
|
const z = Number(coords[2]);
|
this.upPos.push({
|
x,
|
y,
|
z,
|
});
|
viewer.entities.add(
|
new Cesium.Entity({
|
name: "上顶点-" + upEle.name,
|
show: true,
|
position: Cesium.Cartesian3.fromDegrees(x, y, z),
|
point: {
|
pixelSize: 10,
|
color: Cesium.Color.YELLOW,
|
outlineColor: Cesium.Color.RED,
|
outlineWidth: 3,
|
},
|
label: {
|
text: upEle.name,
|
font: "14pt Source Han Sans CN", //字体样式
|
fillColor: Cesium.Color.YELLOW, //字体颜色
|
outlineWidth: 2,
|
verticalOrigin: Cesium.VerticalOrigin.CENTER, //垂直位置
|
horizontalOrigin: Cesium.HorizontalOrigin.CENTER, //水平位置
|
pixelOffset: new Cesium.Cartesian2(0, -22), //偏移
|
},
|
})
|
);
|
}
|
// 绘制下顶点
|
for (let n = 0; n < this.down.length; n++) {
|
const downEle = this.down[n];
|
const coords = downEle.coords.split(",");
|
const x = Number(coords[0]);
|
const y = Number(coords[1]);
|
const z = Number(coords[2]);
|
this.downPos.push({ x, y, z });
|
viewer.entities.add(
|
new Cesium.Entity({
|
name: "下顶点-" + downEle.name,
|
show: true,
|
position: Cesium.Cartesian3.fromDegrees(x, y, z),
|
point: {
|
pixelSize: 10,
|
color: Cesium.Color.GREEN,
|
outlineColor: Cesium.Color.RED,
|
outlineWidth: 3,
|
},
|
label: {
|
text: downEle.name,
|
font: "14pt Source Han Sans CN", //字体样式
|
fillColor: Cesium.Color.GREEN, //字体颜色
|
outlineWidth: 2,
|
verticalOrigin: Cesium.VerticalOrigin.CENTER, //垂直位置
|
horizontalOrigin: Cesium.HorizontalOrigin.CENTER, //水平位置
|
pixelOffset: new Cesium.Cartesian2(0, 22), //偏移
|
},
|
})
|
);
|
}
|
viewer.flyTo(viewer.entities);
|
this.initUpAndDownPolygon();
|
},
|
//绘制顶底面
|
initUpAndDownPolygon() {
|
//绘制上表面
|
const upPositions = [];
|
for (let i = 0; i < this.up.length; i++) {
|
const upEle = this.up[i];
|
const coords = upEle.coords.split(",");
|
upPositions.push(
|
Number(coords[0]),
|
Number(coords[1]),
|
Number(coords[2])
|
);
|
}
|
upPolygonObj = viewer.entities.add({
|
name: "上表面",
|
polygon: {
|
hierarchy: Cesium.Cartesian3.fromDegreesArrayHeights(upPositions),
|
perPositionHeight: true,
|
material: Cesium.Color.YELLOW.withAlpha(0.5),
|
outline: true,
|
outlineColor: Cesium.Color.BLACK,
|
},
|
});
|
// 绘制下表面
|
const upPositions = [];
|
for (let i = 0; i < this.down.length; i++) {
|
const downEle = this.down[i];
|
const coords = downEle.coords.split(",");
|
downPositions.push(
|
Number(coords[0]),
|
Number(coords[1]),
|
Number(coords[2])
|
);
|
}
|
downPolygonObj = viewer.entities.add({
|
name: "下表面",
|
polygon: {
|
hierarchy: Cesium.Cartesian3.fromDegreesArrayHeights(downPositions),
|
perPositionHeight: true,
|
material: Cesium.Color.GREEN.withAlpha(0.5),
|
outline: true,
|
outlineColor: Cesium.Color.BLACK,
|
},
|
});
|
this.clipUpByPoint();
|
},
|
// 上表面水平切割
|
clipUpByPoint() {
|
this.upPos.sort(this.compare("z"));
|
this.upMinHight = this.upPos[0].z;
|
const upClipPoints = [];
|
for (let i = 0; i < this.upPos.length; i++) {
|
const upEle = this.upPos[i];
|
const x = upEle.x;
|
const y = upEle.y;
|
const z = this.upMinHight;
|
this.upClipPos.push({ x, y, z });
|
upClipPoints.push(x, y, z);
|
viewer.entities.add(
|
new Cesium.Entity({
|
name: "上表面水平切割点-" + i,
|
show: true,
|
position: Cesium.Cartesian3.fromDegrees(x, y, z),
|
point: {
|
pixelSize: 20,
|
color: Cesium.Color.BLUE,
|
outlineColor: Cesium.Color.BLACK,
|
outlineWidth: 5,
|
},
|
})
|
);
|
}
|
viewer.entities.add({
|
name: "上表面水平切割面",
|
polygon: {
|
hierarchy: Cesium.Cartesian3.fromDegreesArrayHeights(upClipPoints),
|
perPositionHeight: true,
|
material: Cesium.Color.YELLOW.withAlpha(0.5),
|
outline: true,
|
outlineColor: Cesium.Color.BLACK,
|
},
|
});
|
this.clipBottomByPoint();
|
},
|
// 下表面水平切割
|
clipBottomByPoint() {
|
this.downPos.sort(this.compare("z"));
|
this.downMaxHight = this.downPos[this.downPos.length - 1].z;
|
const downClipPoints = [];
|
for (let i = 0; i < this.downPos.length; i++) {
|
const downEle = this.downPos[i];
|
const x = downEle.x;
|
const y = downEle.y;
|
const z = this.downMaxHight;
|
this.downClipPos.push({ x, y, z });
|
downClipPoints.push(x, y, z);
|
viewer.entities.add(
|
new Cesium.Entity({
|
name: "下表面水平切割点-" + i,
|
show: true,
|
position: Cesium.Cartesian3.fromDegrees(x, y, z),
|
point: {
|
pixelSize: 20,
|
color: Cesium.Color.BLUE,
|
outlineColor: Cesium.Color.BLACK,
|
outlineWidth: 5,
|
},
|
})
|
);
|
}
|
viewer.entities.add({
|
name: "下表面水平切割面",
|
polygon: {
|
hierarchy: Cesium.Cartesian3.fromDegreesArrayHeights(downClipPoints),
|
perPositionHeight: true,
|
material: Cesium.Color.GREEN.withAlpha(0.5),
|
outline: true,
|
outlineColor: Cesium.Color.BLACK,
|
},
|
});
|
this.createUpSolid();
|
},
|
// 创建上半部分空间体
|
createUpSolid() {
|
const vertex = this.upPos[0];
|
const upSolidPolygonPoints = [];
|
const fourpoints0 = [];
|
const fourpoints1 = [];
|
for (let i = 1; i < this.upPos.length; i++) {
|
const upEle = this.upPos[i];
|
const upClipEle = this.upClipPos[i];
|
const upClipEle1 = this.upClipPos[this.upPos.length - i];
|
fourpoints0.push(upEle.x, upEle.y, upEle.z);
|
fourpoints1.push(upClipEle1.x, upClipEle1.y, upClipEle1.z);
|
const curPolygonPoints = [
|
vertex.x,
|
vertex.y,
|
vertex.z,
|
upEle.x,
|
upEle.y,
|
upEle.z,
|
upClipEle.x,
|
upClipEle.y,
|
upClipEle.z,
|
];
|
upSolidPolygonPoints.push(curPolygonPoints);
|
}
|
const fourpoints = fourpoints0.concat(fourpoints1); //上半部分顶点对应的那个面的四个顶点
|
upSolidPolygonPoints.push(fourpoints);
|
for (let n = 0; n < upSolidPolygonPoints.length; n++) {
|
const element = upSolidPolygonPoints[n];
|
viewer.entities.add({
|
name: "上表面水平切割后形成的侧面",
|
polygon: {
|
hierarchy: Cesium.Cartesian3.fromDegreesArrayHeights(element),
|
perPositionHeight: true,
|
material: Cesium.Color.YELLOW.withAlpha(0.5),
|
outline: true,
|
outlineColor: Cesium.Color.RED,
|
},
|
});
|
}
|
this.createDownSolid();
|
},
|
//创建下半部分空间体
|
createDownSolid() {
|
const vertex = this.downPos[this.downPos.length - 1];
|
const downSolidPolygonPoints = [];
|
const fourpoints0 = [];
|
const fourpoints1 = [];
|
for (let i = 0; i < this.downPos.length - 1; i++) {
|
const downEle = this.downPos[i];
|
const downClipEle = this.downClipPos[i];
|
const downClipEle1 = this.downClipPos[this.downPos.length - 2 - i];
|
fourpoints0.push(downEle.x, downEle.y, downEle.z);
|
fourpoints1.push(downClipEle1.x, downClipEle1.y, downClipEle1.z);
|
const curPolygonPoints = [
|
vertex.x,
|
vertex.y,
|
vertex.z,
|
downEle.x,
|
downEle.y,
|
downEle.z,
|
downClipEle.x,
|
downClipEle.y,
|
downClipEle.z,
|
];
|
downSolidPolygonPoints.push(curPolygonPoints);
|
}
|
const fourpoints = fourpoints0.concat(fourpoints1); //下半部分顶点对应的那个面的四个顶点
|
downSolidPolygonPoints.push(fourpoints);
|
for (let n = 0; n < downSolidPolygonPoints.length; n++) {
|
const element = downSolidPolygonPoints[n];
|
viewer.entities.add({
|
name: "下表面水平切割后形成的侧面",
|
polygon: {
|
hierarchy: Cesium.Cartesian3.fromDegreesArrayHeights(element),
|
perPositionHeight: true,
|
material: Cesium.Color.GREEN.withAlpha(0.5),
|
outline: true,
|
outlineColor: Cesium.Color.RED,
|
},
|
});
|
}
|
this.calculateUpVolume();
|
},
|
// 计算上半部分体积//四棱锥体积计算公式:体积=底面积*高/3
|
calculateUpVolume() {
|
// 底面积=(上底+下底)*高/2
|
// 上底长度和下底长度
|
console.log("计算上半部分的底面积:", this.upPos, this.upClipPos, turf);
|
const clipLine = [];
|
let upAndDownDistance = 0;
|
for (let i = 1; i < this.upPos.length; i++) {
|
const upEle = this.upPos[i];
|
const upClipEle = this.upClipPos[i];
|
clipLine.push(upClipEle);
|
const distance = this.getSpaceDistance(upEle, upClipEle);
|
console.log("底面上下底边长度" + i, distance);
|
upAndDownDistance += distance;
|
}
|
const height = this.getSpaceDistance(clipLine[0], clipLine[1]);
|
console.log("底面的高", height);
|
const bottomArea = (upAndDownDistance * height) / 2;
|
console.log("底面积", bottomArea);
|
//高
|
const pt = turf.point([this.upClipPos[0].x, this.upClipPos[0].y]);
|
const line = turf.lineString([
|
[this.upClipPos[1].x, this.upClipPos[1].y],
|
[this.upClipPos[2].x, this.upClipPos[2].y],
|
]);
|
|
const heightDistance = turf.pointToLineDistance(pt, line) * 1000;
|
console.log("顶点到底面的高:", heightDistance);
|
this.upVolume = (bottomArea * heightDistance) / 3;
|
console.log("上半部分体积(黄色):", this.upVolume);
|
this.calculateDownVolume();
|
},
|
// 计算下半部分体积//四棱锥体积计算公式:体积=底面积*高/3
|
calculateDownVolume() {
|
// 底面积=(上底+下底)*高/2
|
// 上底长度和下底长度
|
console.log(
|
"计算下半部分的底面积:",
|
this.downPos,
|
this.downClipPos,
|
turf
|
);
|
const clipLine = [];
|
let upAndDownDistance = 0;
|
for (let i = 0; i < this.downPos.length - 1; i++) {
|
const upEle = this.downPos[i];
|
const upClipEle = this.downClipPos[i];
|
clipLine.push(upClipEle);
|
const distance = this.getSpaceDistance(upEle, upClipEle);
|
console.log("底面上下底边长度" + i, distance);
|
upAndDownDistance += distance;
|
}
|
const height = this.getSpaceDistance(clipLine[0], clipLine[1]);
|
console.log("底面的高", height);
|
const bottomArea = (upAndDownDistance * height) / 2;
|
console.log("底面积", bottomArea);
|
//高
|
const pt = turf.point([this.downClipPos[2].x, this.downClipPos[2].y]);
|
const line = turf.lineString([
|
[this.downClipPos[0].x, this.downClipPos[0].y],
|
[this.downClipPos[1].x, this.downClipPos[1].y],
|
]);
|
|
const heightDistance = turf.pointToLineDistance(pt, line) * 1000;
|
console.log("顶点到底面的高:", heightDistance);
|
this.downVolume = (bottomArea * heightDistance) / 3;
|
console.log("下半部分体积(绿色):", this.downVolume);
|
this.calculateCenterVolume(heightDistance, height);
|
},
|
// 计算中间部分体积//三棱柱体积计算公式:体积=底面积*高
|
//height:三棱柱底面三角形的高,bottom:三棱柱底面三角形的底边
|
calculateCenterVolume(height, bottom) {
|
//底面积
|
const area = (height * bottom) / 2;
|
console.log("底面三角形面积:", area);
|
const h = Math.abs(this.upClipPos[0].z - this.downClipPos[2].z); //三棱柱高
|
this.centerVolume = area * h;
|
console.log("中间部分三棱柱体积:", this.centerVolume);
|
const volume = this.upVolume + this.downVolume + this.centerVolume;
|
console.log("当前空间立方体的体积是(m³):", volume);
|
},
|
// 空间两点间距离,单位m
|
getSpaceDistance(pos1, pos2) {
|
const h = Math.abs(pos1.z - pos2.z);
|
const from = turf.point([pos1.x, pos1.y]);
|
const to = turf.point([pos2.x, pos2.y]);
|
const distance = turf.distance(from, to); //这里计算出来的单位是km,要转换成m,所以后面开平方的时候*1000
|
const result = Math.sqrt(Math.pow(distance * 1000, 2) + Math.pow(h, 2));
|
return result;
|
},
|
//数组根据某一字段从高到低排序
|
compare(property) {
|
return function (a, b) {
|
const value1 = a[property];
|
const value2 = b[property];
|
return value1 - value2;
|
};
|
},
|
initCamera() {
|
viewer.camera.flyTo({
|
destination: window.Cesium.Cartesian3.fromDegrees(
|
110.62898254394531,
|
40.02804946899414,
|
1000.0
|
), //相机飞入点
|
});
|
},
|
},
|
getLocation() {
|
let handler = new Cesium.ScreenSpaceEventHandler(viewer.canvas);
|
handler.setInputAction(function (event) {
|
let earthPosition = viewer.scene.pickPosition(event.position);
|
if (Cesium.defined(earthPosition)) {
|
let cartographic = Cesium.Cartographic.fromCartesian(earthPosition);
|
let lon = Cesium.Math.toDegrees(cartographic.longitude).toFixed(5);
|
let lat = Cesium.Math.toDegrees(cartographic.latitude).toFixed(5);
|
let height = cartographic.height.toFixed(2); //模型高度
|
console.log(earthPosition, {
|
lon: lon,
|
lat: lat,
|
height: height,
|
});
|
}
|
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
|
},
|
};
|
</script>
|
<style lang="scss" scoped>
|
#cesiumContainer {
|
width: 100%;
|
height: 100%;
|
position: relative;
|
.volume-group {
|
position: absolute;
|
padding: 10px;
|
margin: 10px;
|
z-index: 10;
|
background-color: rgb(113, 116, 116);
|
color: white;
|
.item-group {
|
display: flex;
|
flex-direction: column;
|
align-items: center;
|
.item {
|
margin: 5px 0;
|
display: flex;
|
align-items: center;
|
::v-deep {
|
.el-input {
|
width: 200px;
|
display: inline-block;
|
}
|
}
|
}
|
}
|
.item-layer {
|
background-color: aliceblue;
|
display: flex;
|
width: calc(100% - 10px);
|
padding: 5px;
|
}
|
}
|
}
|
</style>
|