You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
411 lines
13 KiB
411 lines
13 KiB
<script>
|
|
import { getCanvasImageAndViewPoint } from '@/utils';
|
|
import { getSceneConfig, getLayerTreeBySceneId, updateSceneConfig } from '@/api/aiSupervision/layerConfigApi';
|
|
import LayerTree from '../layerTree/index.vue';
|
|
import { addLayer, removeLayer, zoomToLayer } from '../layerTree/index.js';
|
|
|
|
let viewer = undefined;
|
|
export default {
|
|
name: 'SceneConfig',
|
|
components: {
|
|
LayerTree
|
|
},
|
|
props: {
|
|
sceneId: {
|
|
type: String,
|
|
default: ''
|
|
},
|
|
defaultCheckedKeys: {
|
|
type: Array,
|
|
default: () => []
|
|
}
|
|
},
|
|
data() {
|
|
return {
|
|
activeNames: ['1', '2', '3'],
|
|
viewForm: { image: '', lng: '', lat: '', alt: '', heading: '', pitch: '', roll: '' },
|
|
initForm: {
|
|
initTime: '',
|
|
initWeather: 1,
|
|
ambientLightIntensity: '',
|
|
weatherIntensity: '',
|
|
movementSensitivity: '',
|
|
rotationSensitivity: '',
|
|
zoomSensitivity: ''
|
|
},
|
|
weatherOptions: [
|
|
{ label: '晴天', value: 1 },
|
|
{ label: '多云', value: 2 },
|
|
{ label: '阴天', value: 3 },
|
|
{ label: '小雨', value: 4 },
|
|
{ label: '中雨', value: 5 },
|
|
{ label: '暴雨', value: 6 },
|
|
{ label: '雪天', value: 7 },
|
|
{ label: '雾天', value: 8 }
|
|
],
|
|
isEdit: false,
|
|
treeData: [],
|
|
defaultProps: { children: 'children', label: 'text' },
|
|
defaultViewProps: { children: 'children', label: 'text', disabled: this.disabledNode }
|
|
};
|
|
},
|
|
methods: {
|
|
initData() {
|
|
this.initMap();
|
|
this.fetchSceneConfig();
|
|
this.fetchLayerTree();
|
|
},
|
|
disabledNode(data, node) {
|
|
if (node.isLeaf) {
|
|
const checkedKeys = this.$refs.layerTree.getCheckedKeys();
|
|
node.checked = checkedKeys.includes(data.id);
|
|
}
|
|
return true;
|
|
},
|
|
fetchSceneConfig() {
|
|
getSceneConfig(this.sceneId).then((res) => {
|
|
if (res.success && res.data) {
|
|
const {
|
|
initTime,
|
|
initWeather,
|
|
ambientLightIntensity,
|
|
weatherIntensity,
|
|
movementSensitivity,
|
|
rotationSensitivity,
|
|
zoomSensitivity,
|
|
cameraPosture,
|
|
viewImg,
|
|
layerId,
|
|
id
|
|
} = res.data;
|
|
this.initForm.id = id;
|
|
this.initForm.initTime = initTime;
|
|
this.initForm.initWeather = initWeather;
|
|
this.initForm.ambientLightIntensity = ambientLightIntensity;
|
|
this.initForm.weatherIntensity = weatherIntensity;
|
|
this.initForm.movementSensitivity = movementSensitivity;
|
|
this.initForm.rotationSensitivity = rotationSensitivity;
|
|
this.initForm.zoomSensitivity = zoomSensitivity;
|
|
this.viewForm = JSON.parse(cameraPosture);
|
|
this.viewForm.image = viewImg;
|
|
const layerIds = JSON.parse(layerId);
|
|
this.$emit('set-checked-keys', layerIds);
|
|
this.$nextTick(() => {
|
|
if (this.viewForm.image) {
|
|
const { lng, lat, alt, heading, pitch, roll } = this.viewForm;
|
|
setTimeout(() => {
|
|
window.viewer.scene.camera.setView({
|
|
destination: new Cesium.Cartesian3.fromDegrees(lng, lat, alt),
|
|
orientation: {
|
|
heading,
|
|
pitch,
|
|
roll
|
|
}
|
|
});
|
|
}, 1000);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
},
|
|
fetchLayerTree() {
|
|
getLayerTreeBySceneId(this.sceneId).then((res) => {
|
|
if (res.success && res.data) {
|
|
this.treeData = this.restructTreeData([res.data]);
|
|
|
|
this.$nextTick(() => {
|
|
const checkedNodes = this.$refs.layerTree.getCheckedNodes(true);
|
|
checkedNodes.forEach((node, index) => {
|
|
this.onCheck(node);
|
|
});
|
|
});
|
|
}
|
|
});
|
|
},
|
|
restructTreeData(data) {
|
|
return data.map((item) => {
|
|
if (item.children && item.children.length) {
|
|
item.children = this.restructTreeData(item.children);
|
|
}
|
|
item.children = item.children || [];
|
|
if (item.layers && item.layers.length) {
|
|
const layers = item.layers.map((layer) => {
|
|
return {
|
|
...layer,
|
|
children: []
|
|
};
|
|
});
|
|
item.children = item.children.concat(layers);
|
|
}
|
|
return item;
|
|
});
|
|
},
|
|
initMap() {
|
|
viewer = new sycim.Viewer('cesiumContainer');
|
|
window.viewer = viewer;
|
|
let baseLayer = sycim.ImageryLayerFactory.createImageryLayer(sycim.ImageryType.ARCGIS, {
|
|
url: 'https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer'
|
|
});
|
|
viewer.addBaseLayer(baseLayer);
|
|
},
|
|
fetchView() {
|
|
const { imageSrc, position, heading, pitch, roll } = getCanvasImageAndViewPoint(viewer);
|
|
const { lng, lat, alt } = sycim.T.transformCartesianToWGS84(position);
|
|
this.viewForm.lng = lng;
|
|
this.viewForm.lat = lat;
|
|
this.viewForm.alt = alt;
|
|
this.viewForm.heading = heading;
|
|
this.viewForm.pitch = pitch;
|
|
this.viewForm.roll = roll;
|
|
this.viewForm.image = imageSrc;
|
|
},
|
|
handleEvent() {
|
|
if (this.isEdit) {
|
|
this.saveSceneConfig();
|
|
}
|
|
this.isEdit = !this.isEdit;
|
|
},
|
|
onCheck(data) {
|
|
if (!data.iconCls) {
|
|
// 获取树的勾选值,不一定是默认勾选的值
|
|
const checkedKeys = this.$refs.layerTree.getCheckedKeys();
|
|
this.handleCheckChange(data, { checkedKeys });
|
|
}
|
|
},
|
|
handleCheckChange(data, checkedData) {
|
|
const { id } = data;
|
|
const { checkedKeys } = checkedData;
|
|
const nodes = [data];
|
|
if (data.children?.length > 0) {
|
|
this.flattenTree(data.children, nodes);
|
|
}
|
|
const checked = checkedKeys.includes(id);
|
|
this.toggleLayer(nodes, checked);
|
|
},
|
|
flattenTree(tree, flatArray = []) {
|
|
tree.forEach((node) => {
|
|
const { children, ...rest } = node;
|
|
flatArray.push(rest);
|
|
if (children && children.length > 0) {
|
|
flattenTree(children, flatArray);
|
|
}
|
|
});
|
|
return flatArray;
|
|
},
|
|
toggleLayer(nodes, checked) {
|
|
if (Array.isArray(nodes)) {
|
|
nodes.forEach((node) => {
|
|
if (checked) {
|
|
addLayer(node);
|
|
} else {
|
|
removeLayer(node);
|
|
}
|
|
});
|
|
}
|
|
},
|
|
goPosition(node) {
|
|
zoomToLayer(node.data);
|
|
},
|
|
saveSceneConfig() {
|
|
const form = JSON.parse(JSON.stringify(this.viewForm));
|
|
delete form.image;
|
|
const layerId = this.$refs.layerConfigTree.getCheckedKeys();
|
|
const params = {
|
|
sceneId: this.sceneId,
|
|
...this.initForm,
|
|
viewImg: this.viewForm.image,
|
|
layerId: JSON.stringify(layerId),
|
|
cameraPosture: JSON.stringify({ ...form })
|
|
};
|
|
updateSceneConfig(params).then((res) => {
|
|
if (res.success) {
|
|
this.$message.success('保存成功');
|
|
}
|
|
});
|
|
},
|
|
cancelEvent() {
|
|
this.isEdit = false;
|
|
}
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<div class="box-wrapper">
|
|
<div class="left-panel">
|
|
<el-collapse class="sy-collapse" v-model="activeNames">
|
|
<el-collapse-item title="初始环境配置" name="1">
|
|
<el-form :model="initForm" label-width="100px" :disabled="!isEdit">
|
|
<el-form-item label="时间模式" prop="initTime">
|
|
<el-time-select
|
|
v-model="initForm.initTime"
|
|
:picker-options="{
|
|
start: '00:00',
|
|
step: '00:15',
|
|
end: '23:45'
|
|
}"
|
|
placeholder="选择时间"
|
|
>
|
|
</el-time-select>
|
|
</el-form-item>
|
|
<el-form-item label="天气模式" prop="initWeather">
|
|
<el-select v-model="initForm.initWeather">
|
|
<el-option v-for="(item, index) in weatherOptions" :label="item.label" :value="item.value"></el-option>
|
|
</el-select>
|
|
</el-form-item>
|
|
<el-form-item label="环境光强度" prop="ambientLightIntensity">
|
|
<el-input v-model="initForm.ambientLightIntensity"></el-input>
|
|
</el-form-item>
|
|
<el-form-item label="天气强度" prop="weatherIntensity">
|
|
<el-input v-model="initForm.weatherIntensity"></el-input>
|
|
</el-form-item>
|
|
<el-form-item label="移动灵敏度" prop="movementSensitivity">
|
|
<el-input v-model="initForm.movementSensitivity"></el-input>
|
|
</el-form-item>
|
|
<el-form-item label="旋转灵敏度" prop="rotationSensitivity">
|
|
<el-input v-model="initForm.rotationSensitivity"></el-input>
|
|
</el-form-item>
|
|
<el-form-item label="缩放灵敏度" prop="zoomSensitivity">
|
|
<el-input v-model="initForm.zoomSensitivity"></el-input>
|
|
</el-form-item>
|
|
</el-form>
|
|
</el-collapse-item>
|
|
<el-collapse-item title="初始视角配置" name="2">
|
|
<el-form :model="viewForm" label-width="80px" :disabled="!isEdit">
|
|
<el-form-item class="mix" label="缩略图" prop="image">
|
|
<el-button v-show="isEdit" type="text" @click="fetchView">捕获当前视角</el-button>
|
|
<el-image v-if="viewForm.image" :src="viewForm.image"></el-image>
|
|
</el-form-item>
|
|
<el-form-item label="视角位置"></el-form-item>
|
|
<el-form-item label="经度" prop="lng">
|
|
<el-input v-model="viewForm.lng"></el-input>
|
|
</el-form-item>
|
|
<el-form-item label="纬度" prop="lat">
|
|
<el-input v-model="viewForm.lat"></el-input>
|
|
</el-form-item>
|
|
<el-form-item label="高度" prop="alt">
|
|
<el-input v-model="viewForm.alt"></el-input>
|
|
</el-form-item>
|
|
<el-form-item label="偏航角" prop="heading">
|
|
<el-input v-model="viewForm.heading"></el-input>
|
|
</el-form-item>
|
|
<el-form-item label="俯仰角" prop="pitch">
|
|
<el-input v-model="viewForm.pitch"></el-input>
|
|
</el-form-item>
|
|
<el-form-item label="翻滚角" prop="roll">
|
|
<el-input v-model="viewForm.roll"></el-input>
|
|
</el-form-item>
|
|
</el-form>
|
|
</el-collapse-item>
|
|
<el-collapse-item title="初始化图层配置" name="3">
|
|
<layer-tree
|
|
ref="layerConfigTree"
|
|
:show-search="true"
|
|
:show-checkbox="true"
|
|
:default-checked-keys="defaultCheckedKeys"
|
|
:default-props="defaultViewProps"
|
|
:data="treeData"
|
|
></layer-tree>
|
|
</el-collapse-item>
|
|
</el-collapse>
|
|
<div class="footer-panel">
|
|
<el-button v-show="isEdit" class="btn" @click="cancelEvent">取 消</el-button>
|
|
<el-button type="primary" class="btn" @click="handleEvent">{{ isEdit ? '保 存' : '编 辑' }}</el-button>
|
|
</div>
|
|
</div>
|
|
<div class="right-panel">
|
|
<div id="cesiumContainer"></div>
|
|
<el-popover class="layer-btn" placement="left-start" width="320" trigger="click">
|
|
<div class="layer-wrapper">
|
|
<div class="title-box">
|
|
<span class="title">项目图层资源目录</span>
|
|
</div>
|
|
<layer-tree
|
|
ref="layerTree"
|
|
:show-checkbox="true"
|
|
:show-search="true"
|
|
:show-btns="true"
|
|
:default-checked-keys="defaultCheckedKeys"
|
|
:data="treeData"
|
|
:default-props="defaultProps"
|
|
@check-change="onCheck"
|
|
@location="goPosition"
|
|
></layer-tree>
|
|
</div>
|
|
<el-button slot="reference" icon="el-icon-menu"></el-button>
|
|
</el-popover>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style lang="less">
|
|
.layer-wrapper {
|
|
.title-box {
|
|
margin-bottom: 12px;
|
|
.title {
|
|
font-size: 16px;
|
|
}
|
|
}
|
|
}
|
|
</style>
|
|
|
|
<style lang="less" scoped>
|
|
.box-wrapper {
|
|
width: 100%;
|
|
height: 100%;
|
|
display: flex;
|
|
.left-panel {
|
|
flex: 1;
|
|
height: 100%;
|
|
padding: 0 12px;
|
|
.sy-collapse {
|
|
height: calc(100% - 50px);
|
|
overflow: auto;
|
|
.el-form {
|
|
.el-form-item {
|
|
&.mix {
|
|
text-align: right;
|
|
.el-form-item__content {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: flex-end;
|
|
.el-button {
|
|
width: 100px;
|
|
}
|
|
.el-image {
|
|
width: 100%;
|
|
height: 300px;
|
|
}
|
|
}
|
|
}
|
|
.el-date-editor,
|
|
.el-select {
|
|
width: 100%;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
.footer-panel {
|
|
height: 50px;
|
|
line-height: 50px;
|
|
text-align: center;
|
|
}
|
|
}
|
|
.right-panel {
|
|
position: relative;
|
|
width: 70%;
|
|
height: 100%;
|
|
#cesiumContainer {
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
.layer-btn {
|
|
position: absolute;
|
|
top: 20px;
|
|
right: 20px;
|
|
z-index: 1;
|
|
}
|
|
}
|
|
}
|
|
</style>
|
|
|