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