2023-11-20 10:45:49 +08:00

1156 lines
34 KiB
Vue
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.

<template>
<div class="main-content">
<div class="main-content-title">
<span>空间分析</span>
<el-icon size="16" color="#fff" @click="closeDiv"><Close /></el-icon>
</div>
<div class="main-content-menu" v-if="mainMenuShow">
<div v-for="(item, i) in menuList" :key="i" class="menu-item" @click="mapOperate(item.name)">
<div><img class="icon" :src="item.icon" alt="" srcset="" /></div>
<div>{{ item.name }}</div>
</div>
</div>
<div class="operate-content" v-if="subMenuShow">
<!-- 日照分析 -->
<div class="sunshine" v-if="selectedName == '日照分析'">
<div class="title">
<el-icon class="back" size="14" @click="backMainMenu"><ArrowLeft /></el-icon>
<span>日照分析</span>
<!-- <el-icon class="delete" size="14"><Delete /></el-icon> -->
</div>
<div class="subtitle-tip">提示:模拟设定时间范围内的太阳光照效果。</div>
<div class="item-classify">
<div class="item-classify-item">
<span>日期选择:</span>
<el-date-picker v-model="allProperty.currDate" type="date" format="YYYY-MM-DD" value-format="YYYY-MM-DD" />
</div>
<div class="item-classify-item">
<span>时间选择:</span>
<el-slider v-model="allProperty.timeVal" :min="0" :max="1440" :step="1" @input="timeChange" />
</div>
<div class="item-classify-item">
<span>当前时间:</span>
<span>{{ allProperty.currDate }} {{ hours }} 时 {{ minutes }}分</span>
</div>
</div>
<div class="sunshing-play">
<el-button type="primary" @click="playOrPause">{{ sunshineText }}</el-button>
</div>
</div>
<!-- 可视域分析 -->
<div class="viewShed" v-if="selectedName == '可视域'">
<div class="title">
<el-icon class="back" size="14" @click="backMainMenu"><ArrowLeft /></el-icon>
<span>可视域分析</span>
<el-icon class="delete" size="14" @click="clearShow"><Delete /></el-icon>
</div>
<div class="subtitle-tip">提示:单击按钮后在图上绘制,红色代表不可视,绿色代表可视。</div>
<div class="item-classify">
<div class="item-classify-item">
<span>四周方向:</span>
<el-slider v-model="allProperty.heading" :max="360" @input="e => viewShedOperate(e, 'heading')" />
</div>
<div class="item-classify-item">
<span>视角距离:</span>
<el-slider v-model="allProperty.distance" :step="10" :max="5000" @input="e => viewShedOperate(e, 'distance')" />
</div>
<div class="item-classify-item">
<span>透明度:</span>
<el-slider v-model="allProperty.opacity" :step="0.1" :max="1" @input="e => viewShedOperate(e, 'opacity')" />
</div>
<div class="item-classify-item">
<span>视椎线框:</span>
<el-switch v-model="allProperty.showFrustum" @input="e => viewShedOperate(e, 'showFrustum')" />
</div>
</div>
<div class="sunshing-play">
<el-button type="primary" @click="addviewShed">添加可视域</el-button>
<el-button type="primary" @click="saveJson">保存</el-button>
</div>
</div>
<!-- 方量分析 -->
<div class="volumeAnalysis" v-if="selectedName == '方量分析'">
<div class="title">
<el-icon class="back" size="14" @click="backMainMenu"><ArrowLeft /></el-icon>
<span>方量分析</span>
<el-icon class="delete" size="14" @click="clearShow"><Delete /></el-icon>
</div>
<div class="subtitle-tip">提示:请首先单击 “绘制区域” 按钮,再在图上绘制分析区域。</div>
<div class="item-classify">
<div class="item-classify-item">
<span>基准面高(米)</span>
<el-input-number v-model="allProperty.height" controls-position="right" @change="e => volumeOperate(e, 'height')" />
<el-button v-if="allProperty.height != 0" type="primary" @click="selectHeight">点选高度</el-button>
</div>
<div class="item-classify-item">
<span>围墙底高(米)</span>
<el-input-number
v-model="allProperty.minHeight"
controls-position="right"
@change="e => volumeOperate(e, 'minHeight')"
/>
<span>(可选)</span>
</div>
<div class="item-classify-item">
<span>围墙顶高(米)</span>
<el-input-number
v-model="allProperty.maxHeight"
controls-position="right"
@change="e => volumeOperate(e, 'maxHeight')"
/>
<span>(可选)</span>
</div>
</div>
<div class="volume-operate">
<el-button type="primary" @click="addVolume">绘制区域</el-button>
</div>
</div>
<!-- 地形开挖 -->
<div class="areaDraw" v-if="selectedName == '地形开挖'">
<div class="title">
<el-icon class="back" size="14" @click="backMainMenu"><ArrowLeft /></el-icon>
<span>地形开挖</span>
<el-icon class="delete" size="14" @click="clearShow"><Delete /></el-icon>
</div>
<div class="subtitle-tip">提示:单击“矩形开挖”或“多边形开挖”按钮开始绘制开挖区域。</div>
<div class="item-classify">
<div class="item-classify-item">
<span>开挖深度:</span>
<el-input-number
v-model="allProperty.diffHeight"
controls-position="right"
@blur="e => areaOperate(e, 'diffHeight')"
/>
<span>(米)</span>
</div>
</div>
<div class="volume-operate">
<el-button type="primary" @click="btnDrawExtent">添加矩形</el-button>
<el-button type="primary" @click="btnDraw">添加多边形</el-button>
<el-button type="primary" @click="saveJson">保存</el-button>
</div>
</div>
<!-- 地表透明 -->
<div class="surfaceOpacity" v-if="selectedName == '地表透明'">
<div class="title">
<el-icon class="back" size="14" @click="backMainMenu"><ArrowLeft /></el-icon>
<span>地表透明</span>
<!-- <el-icon class="delete" size="14" @click="clearShow"><Delete /></el-icon> -->
</div>
<div class="subtitle-tip">提示:可以透明地表,进入地下模式,可以看地下管网等数据。</div>
<div class="item-classify">
<div class="item-classify-item">
<span>状态:</span>
<el-checkbox v-model="allProperty.enabled" @change="e => surfaceOpacityOperate(e, 'enabled')"
>开启地下模式</el-checkbox
>
</div>
<div class="item-classify-item">
<span>透明度:</span>
<el-slider v-model="allProperty.alpha" :step="0.1" :max="1" @input="e => surfaceOpacityOperate(e, 'alpha')" />
</div>
</div>
</div>
<!-- 坡度坡向 -->
<div class="slopeAspect" v-if="selectedName == '坡度坡向'">
<div class="title">
<el-icon class="back" size="14" @click="backMainMenu"><ArrowLeft /></el-icon>
<span>坡度坡向</span>
<el-icon class="delete" size="14" @click="clearShow"><Delete /></el-icon>
</div>
<div class="subtitle-tip">提示:单击按钮后在图上绘制区域。</div>
<div class="item-classify">
<div class="item-classify-item">
<span>插值数:</span>
<el-input-number v-model="allProperty.splitNum" controls-position="right" />
</div>
<div class="item-classify-item">
<span>地表渲染:</span>
<el-radio-group v-model="allProperty.shadingType" @change="e => slopeAspectOperate(e, 'shadingType')">
<el-radio label="none">无阴影</el-radio>
<el-radio label="slope">坡度</el-radio>
<el-radio label="aspect">坡向</el-radio>
</el-radio-group>
</div>
</div>
<div class="volume-operate">
<el-button type="primary" @click="btnSlopeAspectDrawExtent">添加矩形</el-button>
<el-button type="primary" @click="btnSlopeAspectDraw">添加多边形</el-button>
</div>
</div>
<!-- 模型剖切 -->
<div class="modelSection" v-if="selectedName == '模型剖切'">
<div class="title">
<el-icon class="back" size="14" @click="backMainMenu"><ArrowLeft /></el-icon>
<span>模型剖切</span>
<el-icon class="delete" size="14" @click="clearShow"><Delete /></el-icon>
</div>
<div class="subtitle-tip">提示:先图上点选模型后,设置剖切方向及距离。</div>
<div class="item-classify">
<!-- <div class="item-classify-item">
<span>选择模型:</span>
<span>未选择</span>
<el-button type="primary" @click="selectModal">图上选点</el-button>
</div> -->
<div class="item-classify-item">
<span>按线剖切:</span>
<el-button type="primary" @click="drawSectionLine">绘制线</el-button>
</div>
<div class="item-classify-item">
<span>按面裁剪:</span>
<el-button type="primary" @click="drawSectionPoly">绘制面</el-button>
<el-radio-group v-model="allProperty.clipOutSide" @change="e => modelSectionOperate(e, 'clipOutSide')">
<el-radio :label="false">内裁</el-radio>
<el-radio :label="true">外裁</el-radio>
</el-radio-group>
</div>
<div class="item-classify-item">
<span>剖切方向</span>
<el-radio-group v-model="allProperty.type" style="width: 70%" @change="e => modelSectionOperate(e, 'type')">
<el-radio label="ZR"></el-radio>
<el-radio label="Z"></el-radio>
<el-radio label="XR"></el-radio>
<el-radio label="X">西</el-radio>
<el-radio label="Y"></el-radio>
<el-radio label="YR"></el-radio>
</el-radio-group>
</div>
<div class="item-classify-item">
<span>剖切距离</span>
<el-input-number
v-model="allProperty.sectionDistance"
controls-position="right"
@change="e => modelSectionOperate(e, 'sectionDistance')"
/>
</div>
</div>
</div>
<!-- 模型压平 -->
<div class="modelFlatten" v-if="selectedName == '模型压平'">
<div class="title">
<el-icon class="back" size="14" @click="backMainMenu"><ArrowLeft /></el-icon>
<span>模型压平</span>
<el-icon class="delete" size="14" @click="clearShow"><Delete /></el-icon>
</div>
<div class="subtitle-tip">
提示: 1只支持部分模型压平(如有带着色器的) 2部分模型的压平可能存在异常(如缩放出现碎片现象)
</div>
<div class="item-classify">
<div class="item-classify-item">
<span>选择模型</span>
<el-button type="primary" @click="showDytDemo">大雁塔</el-button>
<el-button type="primary" @click="showTehDemo">天鹅湖</el-button>
</div>
<div class="item-classify-item">
<span>压平区高度</span>
<el-input-number v-model="allProperty.flattenHeight" controls-position="right" @change="modelFlattenOperate" />
</div>
</div>
<div class="volume-operate">
<el-button type="primary" @click="btnModelFlattenDrawExtent">添加矩形</el-button>
<el-button type="primary" @click="btnModelFlattenDraw">添加多边形</el-button>
</div>
</div>
<!-- 限高分析 -->
<div class="heightLimit" v-if="selectedName == '限高分析'">
<div class="title">
<el-icon class="back" size="14" @click="backMainMenu"><ArrowLeft /></el-icon>
<span>限高分析</span>
<el-icon class="delete" size="14" @click="clearShow"><Delete /></el-icon>
</div>
<div class="item-classify">
<!-- <div class="item-classify-item">
<span>地表海拔</span>
<span>0</span>
<el-button type="primary" @click="drawSectionLine">绘制线</el-button>
</div> -->
<div class="item-classify-item">
<span>限制高度</span>
<el-slider v-model="allProperty.limitHeight" :max="1000" @input="limitHeightOperate" />
</div>
<div class="item-classify-item">
<span>当前高度</span>
<span>{{ allProperty.limitHeight }}</span>
</div>
<div class="item-classify-item">
<span>分析区域</span>
<el-button type="primary" @click="drawheightLimitDrawExtent">绘制矩形</el-button>
<el-button type="primary" @click="drawheightLimitPoly">绘制面</el-button>
</div>
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
//引入cesium基础库
import "mars3d-cesium/Build/Cesium/Widgets/widgets.css";
import * as Cesium from "mars3d-cesium";
//导入mars3d主库
import "mars3d/dist/mars3d.css";
import * as mars3d from "mars3d";
import dayjs from "dayjs";
import { ref, watch, onMounted, computed, onUnmounted } from "vue";
import { ElMessage } from "element-plus";
const props = defineProps(["mapInstance"]);
const emits = defineEmits(["hiddenConfim", "saveJson"]);
const mainMenuShow = ref(true);
const subMenuShow = ref(false);
const sunshineText = ref("播放");
const allProperty = ref<any>({
// 日照分析数据
timeVal: 420,
currDate: dayjs().format("YYYY-MM-DD"),
// 可视域数据
heading: 45,
distance: 80,
opacity: 0.8,
showFrustum: true,
// 方量分析数据
height: 0,
minHeight: 0,
maxHeight: 0,
// 地形开挖数据
diffHeight: 20,
// 地下透明数据
enabled: false,
alpha: 0.5,
// 坡度坡向数据
splitNum: 10,
shadingType: "none",
// 模型剖切
clipOutSide: false,
type: "ZR",
sectionDistance: 0,
// 模型压平
flattenHeight: 0,
// 限高分析
limitHeight: 0
});
const selectedName = ref("");
const menuList = ref([
{ name: "日照分析", icon: new URL("../../../../assets/images/Mars3DIcon/rzfx.png", import.meta.url).href },
{ name: "可视域", icon: new URL("../../../../assets/images/Mars3DIcon/ksy.png", import.meta.url).href },
{ name: "方量分析", icon: new URL("../../../../assets/images/Mars3DIcon/flfx.png", import.meta.url).href },
{ name: "地形开挖", icon: new URL("../../../../assets/images/Mars3DIcon/dxkw.png", import.meta.url).href },
{ name: "地表透明", icon: new URL("../../../../assets/images/Mars3DIcon/dbtm.png", import.meta.url).href },
{ name: "坡度坡向", icon: new URL("../../../../assets/images/Mars3DIcon/pdpx.png", import.meta.url).href },
{ name: "模型剖切", icon: new URL("../../../../assets/images/Mars3DIcon/mxpq.png", import.meta.url).href },
{ name: "模型压平", icon: new URL("../../../../assets/images/Mars3DIcon/mxyp.png", import.meta.url).href },
{ name: "限高分析", icon: new URL("../../../../assets/images/Mars3DIcon/xgfx.png", import.meta.url).href }
]);
const clearBool = ref(true);
const initGraphicLength = ref(0);
let map: any = props.mapInstance;
let graphicLayer: any;
let measure: any;
let measureVolume: any;
let shadows: any;
let terrainClip: any;
let underground: any;
let slope: any;
let contourLine: any;
let modelPlanClip: any;
let tilesetLayer: any; // 3dtiles模型添加模型选择
let limitHeight: any;
const hours = computed(() => Math.floor(allProperty.value.timeVal / 60));
const minutes = computed(() => Math.floor(allProperty.value.timeVal / 60));
onMounted(() => {
console.log(map, "66666");
graphicLayer = new mars3d.layer.GraphicLayer();
map.addLayer(graphicLayer);
// map.scene.globe.depthTestAgainstTerrain = true; // 不加无法投射到地形上
initGraphicLength.value = graphicLayer.getGraphics().length;
// 地形开挖
terrainClip = new mars3d.thing.TerrainClip({
diffHeight: allProperty.value.diffHeight, // 井的深度
image: new URL("../../../../assets/images/Mars3DImg/textures/poly-stone.jpg", import.meta.url).href,
imageBottom: new URL("../../../../assets/images/Mars3DImg/textures/poly-soil.jpg", import.meta.url).href,
splitNum: 80, // 井边界插值数
enabled: true
});
map.addThing(terrainClip);
measure = new mars3d.thing.Measure({
label: {
color: "#ffffff",
font_family: "楷体",
font_size: 20,
background: false
}
});
map.addThing(measure);
// 触发事件:开始分析前
measure.on(mars3d.EventType.start, function (e: any) {
console.log("开始分析", e);
});
// 触发事件:异步分析完成后
measure.on(mars3d.EventType.end, function (e: any) {
console.log(e);
});
// 渲染效果
contourLine = new mars3d.thing.ContourLine({
contourShow: false, // 是否显示等高线
shadingType: "none", // 地表渲染效果类型:无nono, 高程 elevation, 坡度slope, 坡向aspect
shadingAlpha: 0.6 /// 地表渲染的透明度
});
map.addThing(contourLine);
});
// 保存配置的图上JSON数据
const saveJson = async () => {
// if (graphicLayer.length == 0) {
// ElMessage("当前没有任何数据,无需保存!");
// return;
// }
console.log(map);
console.log(terrainClip.list);
let arr: any = terrainClip.list;
// map.eachThing(item => {
// console.log(item);
// arr.push(item);
// });
arr = arr.concat(graphicLayer.getGraphics());
clearBool.value = false;
console.log(arr);
emits("saveJson", arr);
};
// 限定高度--绘制面
const drawheightLimitPoly = () => {
graphicLayer.clear();
graphicLayer.startDraw({
type: "polygon",
style: {
color: "#ffff00",
opacity: 0.3,
clampToGround: true
},
success: function (graphic) {
// 绘制成功后回调
const positions = graphic.positionsShow;
limitHeight.positions = positions;
graphicLayer.clear();
console.log("绘制坐标为", JSON.stringify(mars3d.LngLatArray.toArray(positions))); // 方便测试拷贝坐标
}
});
};
// 限定高度--绘制矩形
const drawheightLimitDrawExtent = () => {
graphicLayer.clear();
graphicLayer.startDraw({
type: "rectangle",
style: {
color: "#ffff00",
opacity: 0.3,
clampToGround: true
},
success: function (graphic: any) {
// 绘制成功后回调
const positions = graphic.getOutlinePositions(false);
limitHeight.positions = positions;
graphicLayer.clear();
}
});
};
// 限定高度数据操作
const limitHeightOperate = (e: any) => {
limitHeight.height = e;
};
// 模型压平--绘制多边形
const btnModelFlattenDraw = () => {
graphicLayer.clear();
graphicLayer.startDraw({
type: "polygon",
style: {
color: "#007be6",
opacity: 0.5
},
success: function (graphic: any) {
// 绘制成功后回调
const positions = graphic.positionsShow;
graphicLayer.clear();
console.log("绘制坐标为", JSON.stringify(mars3d.LngLatArray.toArray(positions))); // 方便测试拷贝坐标
tilesetLayer.flat.addArea(positions, { height: allProperty.value.flattenHeight });
}
});
};
// 模型压平--绘制矩形
const btnModelFlattenDrawExtent = () => {
graphicLayer.clear();
graphicLayer.startDraw({
type: "rectangle",
style: {
color: "#007be6",
opacity: 0.2,
outline: false
},
success: function (graphic: any) {
// 绘制成功后回调
const positions = graphic.getOutlinePositions(false);
graphicLayer.clear();
console.log("绘制坐标为", JSON.stringify(mars3d.LngLatArray.toArray(positions))); // 方便测试拷贝坐标
tilesetLayer.flat.addArea(positions, { height: allProperty.value.flattenHeight });
}
});
};
// 模型剖切数据改变
const modelFlattenOperate = (e: any) => {
tilesetLayer.flat.updateHeight(e);
};
const showTehDemo = () => {
// 以下数据为cesiumlab v3处理目前其材质有做偏移处理不知道内部逻辑及具体值无法平整压平。
tilesetLayer = new mars3d.layer.TilesetLayer({
name: "合肥天鹅湖",
url: "//data.mars3d.cn/3dtiles/qx-teh/tileset.json",
position: { lng: 117.218434, lat: 31.81807, alt: 163 },
editHeight: -130.0, // 相对高度 (单位:米),基于 压平/淹没区域 最低点高度的偏移量
maximumScreenSpaceError: 16,
dynamicScreenSpaceError: true,
cullWithChildrenBounds: false,
skipLevelOfDetail: true,
preferLeaves: true,
center: { lat: 31.795308, lng: 117.21948, alt: 1820, heading: 0, pitch: -39 },
flat: {
enabled: true
},
flyTo: true
});
map.addLayer(tilesetLayer);
};
const showDytDemo = () => {
// 加模型
tilesetLayer = new mars3d.layer.TilesetLayer({
name: "大雁塔",
url: "//data.mars3d.cn/3dtiles/qx-dyt/tileset.json",
position: { lng: 108.963363, lat: 34.221298, alt: -27 },
maximumScreenSpaceError: 1,
// 可传入TilesetFlat构造参数下面是演示压平区域
flat: {
area: [
{
positions: [
[108.962938, 34.221141, 402.4],
[108.963847, 34.221141, 402.4],
[108.963847, 34.221868, 402.4],
[108.962938, 34.221868, 402.4]
]
}
],
editHeight: 420 // 相对高度 (单位:米),基于 压平/淹没区域 最低点高度的偏移量
},
flyTo: true
});
map.addLayer(tilesetLayer);
};
// 绘制剖切面
const drawSectionPoly = () => {
modelPlanClip.clear();
map.graphicLayer.startDraw({
type: "polygon",
style: {
color: "#007be6",
opacity: 0.5,
outline: true,
outlineWidth: 2,
addHeight: 0.5
},
success: function (graphic: any) {
// 绘制成功后回调
const positions = graphic.positionsShow;
graphicLayer.clear();
modelPlanClip.clipOutSide = allProperty.value.clipOutSide;
modelPlanClip.positions = positions;
}
});
};
// 绘制剖切线
const drawSectionLine = () => {
modelPlanClip.clear();
map.graphicLayer.startDraw({
type: "polyline",
maxPointNum: 2,
style: {
color: "#007be6",
opacity: 0.8,
outline: false
},
success: function (graphic: any) {
// 绘制成功后回调
const positions = graphic.positionsShow;
graphicLayer.clear();
modelPlanClip.positions = positions;
}
});
};
// 模型剖切数据改变
const modelSectionOperate = (e: any, label: any) => {
if (label == "sectionDistance") {
modelPlanClip["distance"] = e;
} else if (label == "type") {
modelPlanClip["type"] = mars3d.ClipType[e];
} else {
modelPlanClip[label] = e;
}
};
// 图上选中模型
const selectModal = () => {
// 拾取点位
graphicLayer.startDraw({
type: "point",
style: {
color: "#00fff2"
},
success: (graphic: any) => {
console.log(graphic);
const modal = graphic.point?.alt;
}
});
};
// 坡向坡度数据操作
const slopeAspectOperate = (e: any, label: any) => {
contourLine[label] = e;
};
// 绘制多边形坡向坡度
const btnSlopeAspectDraw = () => {
slope.clear();
contourLine.clear();
graphicLayer.startDraw({
type: "polygon",
style: {
color: "#29cf34",
opacity: 0.3,
outline: true,
outlineColor: "#ffffff",
clampToGround: true
},
success: function (graphic: any) {
// 绘制成功后回调
const positions = graphic.positionsShow;
graphicLayer.clear();
console.log("绘制坐标为", JSON.stringify(mars3d.LngLatArray.toArray(positions))); // 方便测试拷贝坐标
contourLine.positions = positions;
slope.add(positions, {
splitNum: allProperty.value.splitNum, // splitNum插值分割的个数
radius: 1, // 缓冲半径(影响坡度坡向的精度)
count: 4 // 缓冲的数量(影响坡度坡向的精度)会求周边(count*4)个点
});
}
});
};
// 绘制矩形坡向坡度
const btnSlopeAspectDrawExtent = () => {
slope.clear();
contourLine.clear();
graphicLayer.startDraw({
type: "rectangle",
style: {
color: "#007be6",
opacity: 0.5,
outline: false
},
success: (graphic: any) => {
const positions = graphic.getOutlinePositions(false);
graphicLayer.clear();
console.log("绘制坐标为", JSON.stringify(mars3d.LngLatArray.toArray(positions))); // 方便测试拷贝坐标
contourLine.positions = positions;
slope.add(positions, {
splitNum: allProperty.value.splitNum, // splitNum插值分割的个数
radius: 1, // 缓冲半径(影响坡度坡向的精度)
count: 4 // 缓冲的数量(影响坡度坡向的精度)会求周边(count*4)个点
});
}
});
};
// 地下透明数据操作
const surfaceOpacityOperate = (e: any, label: any) => {
underground[label] = e;
};
// 地形开挖---多边形
const btnDraw = () => {
graphicLayer.startDraw({
type: "polygon",
style: {
color: "#007be6",
opacity: 0.5,
outline: false
},
success: (graphic: any) => {
const positions = graphic.positionsShow;
graphicLayer.clear();
console.log(JSON.stringify(mars3d.LngLatArray.toArray(positions))); // 打印下边界
// 挖地区域
const areaItem = terrainClip.addArea(positions);
}
});
};
// 地形开挖---矩形
const btnDrawExtent = () => {
graphicLayer.startDraw({
type: "rectangle",
style: {
color: "#007be6",
opacity: 0.8
},
success: (graphic: any) => {
const positions = graphic.getOutlinePositions(false);
graphicLayer.clear();
console.log(JSON.stringify(mars3d.LngLatArray.toArray(positions))); // 打印下边界
// 挖地区域
const areaItem = terrainClip.addArea(positions);
}
});
};
// 地形开挖数据操作
const areaOperate = (e: any, label: any) => {
terrainClip[label] = allProperty.value.diffHeight;
};
// 获取点选高度
const selectHeight = () => {
if (!measureVolume || !measure) {
ElMessage("请先开始方量分析");
}
// 拾取高度
graphicLayer.startDraw({
type: "point",
style: {
color: "#00fff2"
},
success: (graphic: any) => {
console.log(666);
const height = graphic.point?.alt;
graphicLayer.removeGraphic(graphic);
if (!height) {
return;
}
console.log(height, "777888");
measureVolume.height = height;
showHeightVal();
}
});
};
// 显示方量分析点选高度
const showHeightVal = () => {
const baseHeight = measureVolume.height.toFixed(1);
const minHeight = measureVolume.minHeight.toFixed(1);
const maxHeight = measureVolume.maxHeight.toFixed(1);
allProperty.value.height = baseHeight;
allProperty.value.minHeight = minHeight;
allProperty.value.maxHeight = maxHeight;
};
// 绘制方量分析区域
const addVolume = () => {
measure
.volume({
splitNum: 6, // 面内插值次数,控制精度[注意精度越大,分析时间越长]
// minHeight: 50 , //可以设置一个固定的最低高度
exact: false
})
.then((e: any) => {
measureVolume = e;
showHeightVal();
});
};
// 方量分析数据操作
const volumeOperate = (e: any, label: any) => {
console.log(e, label);
if (label == "minHeight" && e > measureVolume.height) {
ElMessage("墙底部高度不能高于基准面高");
return;
}
if (label == "maxHeight") {
const maxHeight = Math.ceil(measureVolume.polygonMaxHeight * 10) / 10;
if (e < maxHeight) {
ElMessage("墙顶部高度不能低于区域内的地表高" + maxHeight);
measureVolume.maxHeight = Number(maxHeight);
return;
}
if (e < measureVolume.height) {
ElMessage("墙顶部高度不能低于基准面高");
return;
}
}
measureVolume[label] = e;
};
// 清楚地图指定效果
const clearShow = () => {
console.log(selectedName.value);
if (selectedName.value == "可视域") {
graphicLayer.getGraphics().map(item => {
if (item.type == "viewShed") {
graphicLayer.removeGraphic(item);
}
});
} else if (selectedName.value == "方量分析") {
measure.clear();
measureVolume = null;
} else if (selectedName.value == "地形开挖") {
terrainClip.clear();
} else if (selectedName.value == "坡度坡向") {
slope.clear();
contourLine.clear();
} else if (selectedName.value == "模型剖切") {
modelPlanClip.clear();
} else if (selectedName.value == "模型压平") {
console.log(tilesetLayer);
tilesetLayer && tilesetLayer.remove(true);
} else if (selectedName.value == "限高分析") {
limitHeight.clear();
graphicLayer.clear();
}
};
// 添加可视域
const addviewShed = () => {
// 开始绘制
graphicLayer.startDraw({
id: "100",
type: "viewShed",
style: {
angle: 60,
angle2: 45,
distance: allProperty.value.distance,
heading: allProperty.value.heading,
showFrustum: allProperty.value.showFrustum,
pitch: -12,
addHeight: 0.5 // 在坐标点增加的高度值,规避遮挡,效果更友好
}
});
};
// 可视域属性值操作
const viewShedOperate = (e: any, label: any) => {
console.log(e, label);
const updateView = graphicLayer.getGraphicById("100");
updateView[label] = e;
};
// 返回主菜单
const backMainMenu = () => {
const name = selectedName.value;
console.log(name);
if (name == "日照分析") {
shadows.remove(true);
} else if (name == "可视域") {
} else if (name == "方量分析") {
} else if (name == "地形开挖") {
// terrainClip.clear(); // 清除挖地区域
} else if (name == "地表透明") {
underground.remove(); // 清除地表透明
allProperty.value.enabled = false;
} else if (name == "坡度坡向") {
slope.clear();
contourLine.clear();
} else if (name == "模型剖切") {
modelPlanClip.clear();
} else if (name == "模型压平") {
tilesetLayer && tilesetLayer.remove(true);
} else if (name == "限高分析") {
}
mainMenuShow.value = true;
subMenuShow.value = false;
};
// 日照播放或暂停
const playOrPause = () => {
if (sunshineText.value == "播放") {
const currentTime = new Date(`${allProperty.value.currDate} ${hours}:${minutes}:00`);
shadows.time = currentTime;
const startDate = new Date(allProperty.value.currDate + " 00:00:00");
const endDate = new Date(allProperty.value.currDate + " 23:59:59");
shadows.multiplier = 1600;
shadows.start(startDate, endDate, currentTime);
sunshineText.value = "暂停";
} else {
if (shadows && shadows.isStart) {
shadows.pause();
sunshineText.value = "播放";
}
}
};
// 日照时间操作
const timeChange = (e: any) => {
const dateTime = new Date(`${allProperty.value.currDate} ${hours.value}:${minutes.value}:00`);
shadows.time = dateTime;
};
const closeDiv = () => {
emits("hiddenConfim");
};
const mapOperate = (name: any) => {
console.log(name);
selectedName.value = name;
if (name == "日照分析") {
shadows = new mars3d.thing.Shadows({
multiplier: 1600
});
map.addThing(shadows);
shadows.on(mars3d.EventType.change, function (e: any) {
allProperty.value.timeVal = shadows.time.getHours() * 60 + shadows.time.getMinutes();
});
} else if (name == "可视域") {
map.addLayer(graphicLayer);
} else if (name == "方量分析") {
} else if (name == "地形开挖") {
} else if (name == "地表透明") {
// 地下透明
map.scene.contextOptions = { webgl: { alpha: true } };
underground = new mars3d.thing.Underground({
enabled: false
});
map.addThing(underground);
} else if (name == "坡度坡向") {
slope = new mars3d.thing.Slope({
arrow: {
scale: 0.3, // 箭头长度的比例范围0.1-0.9
color: Cesium.Color.YELLOW,
width: 15, // 箭头宽度
// materialType: mars3d.MaterialType.LineFlow,
// materialOptions: {
// color: "#1a9850",
// image: "img/textures/line-arrow-right.png",
// speed: 10
// },
// clampToGround: true,
show: true
},
tooltip: function (event) {
const attr = event.graphic?.attr;
return `坡度: ${attr.slopeStr1} (${attr.slopeStr2})<br />坡向: ${attr.direction}°`;
}
});
map.addThing(slope);
} else if (name == "模型剖切") {
map.scene.center = { lat: 31.841619, lng: 117.140395, alt: 1259, heading: 90, pitch: -51 };
// 加模型
const graphic = new mars3d.graphic.ModelPrimitive({
position: [117.150365, 31.841954, 50.26],
style: {
url: "//data.mars3d.cn/gltf/mars/dikuai/d1.gltf",
scale: 1
}
});
graphicLayer.addGraphic(graphic);
modelPlanClip = new mars3d.thing.ModelPlanClip({
graphic: graphic,
height: 1, // 开挖的深度
clipOutSide: false,
edgeColor: Cesium.Color.GREY,
edgeWidth: 2.0
});
map.addThing(modelPlanClip);
} else if (name == "模型压平") {
} else if (name == "限高分析") {
// 限高分析
map.fixedLight = true; // 固定光照避免gltf模型随时间存在亮度不一致。
// 限高分析类
limitHeight = new mars3d.thing.LimitHeight({
color: "rgba(255,0,0,0.5)",
height: 80, // 限高
bottomHeight: 32, // 模型地面的海拔高度(单位:米)
positions: [
[117.210446, 31.829032, 0],
[117.226334, 31.826662, 0],
[117.226694, 31.807882, 0],
[117.209776, 31.808359, 0],
[117.209778, 31.808341, 0]
]
});
map.addThing(limitHeight);
}
mainMenuShow.value = false;
subMenuShow.value = true;
};
onUnmounted(() => {
map = null;
measure = null;
measureVolume = null;
shadows = null;
graphicLayer = null;
});
</script>
<style scoped lang="scss">
@mixin flex {
display: flex;
align-items: center;
}
// 菜单弹框出现动画
@keyframes fadeIn {
0% {
opacity: 0;
transform: translate3d(100%, 0, 0);
}
100% {
opacity: 1;
transform: none;
}
}
.main-content {
position: absolute;
top: 60px;
right: 10px;
padding: 0 !important;
background-image: none !important;
border: 1px solid #008aff70;
border-radius: 2px !important;
background-color: rgba(23, 49, 71, 0.8);
// min-width: 300px;
min-height: 370px;
box-shadow: 0 4px 15px 1px #02213bb3;
animation: fadeIn 1s;
&-title {
@include flex;
width: 100%;
height: 40px;
padding: 0 5px 0 10px;
background-image: url("@/assets/images/Mars3DIcon/subClassTitle.png");
background-size: 100% 100%;
background-repeat: no-repeat;
span {
font-size: 16px;
color: #0089fe;
margin-right: auto;
}
:deep() {
.el-icon {
cursor: pointer;
}
}
}
&-menu {
width: 300px;
height: max-content;
display: flex;
justify-content: center;
flex-wrap: wrap;
overflow-y: scroll;
.menu-item {
display: inline-block;
width: 75px;
margin-top: 20px;
margin-left: 10px;
vertical-align: top;
text-align: center;
cursor: pointer;
font-size: 12px;
color: white;
.icon {
width: 50px;
height: 50px;
padding: 1px;
}
}
}
.operate-content {
width: 100%;
height: calc(100% - 40px);
> div {
width: 300px;
padding: 10px;
.title {
color: #fff;
padding-top: 10px;
padding-bottom: 4px;
border-bottom: 1px dotted white;
@include flex;
.back {
margin: 0 10px;
cursor: pointer;
}
.delete {
margin-right: 10px;
cursor: pointer;
}
> span {
font-size: 14px;
margin-right: auto;
}
}
.subtitle-tip {
color: #cad1d1;
padding-top: 5px;
margin-bottom: 15px;
}
.item-classify {
display: flex;
flex-direction: column;
&-item {
display: flex;
align-items: center;
color: white;
margin-bottom: 10px;
* {
margin-right: 10px;
}
:deep() {
.el-slider,
.el-date-editor {
width: 60% !important;
}
.el-input-number {
width: 50%;
}
.el-checkbox__inner,
.el-radio__inner {
background-color: transparent;
}
.el-checkbox__label,
.el-radio__label {
color: white;
}
}
}
}
.sunshing-play {
@include flex;
justify-content: center;
margin-top: 10px;
}
.volume-operate {
@include flex;
justify-content: center;
margin-top: 10px;
:deep() {
.el-button {
margin-left: 10px;
}
}
}
}
.volumeAnalysis {
width: 400px;
}
}
}
// element 组件样式
:deep() {
.el-input__wrapper {
background-color: transparent;
}
.el-input__inner {
color: white;
}
.el-input-number__increase,
.el-input-number__decrease {
background-color: transparent;
color: white;
}
.el-button {
background: rgba(51, 89, 181, 0.6);
border-radius: 6px;
border: 0;
}
}
</style>