<template>
|
<basic-container>
|
<div class="layerContainer">
|
<div>
|
<el-tabs v-model="activeType" @tab-click="handleClick">
|
<el-tab-pane v-for="tab in tabData" :key="tab.type" :label="tab.name" :name="tab.type">
|
</el-tab-pane>
|
</el-tabs>
|
</div>
|
<!-- 地图 -->
|
<div class="mapContainer">
|
<div id="layMap" class="ztzf-cesium"></div>
|
<leftList @update:coverData="handleCoverDataUpdate" :activeName="activeName"></leftList>
|
<rightEdit></rightEdit>
|
</div>
|
</div>
|
</basic-container>
|
</template>
|
|
<script setup>
|
import { flyVisual } from '@/utils/cesium/mapUtil'
|
import rightEdit from '@/views/layerManagement/components/rightEdit.vue';
|
import leftList from '@/views/layerManagement/components/leftList.vue';
|
import { DrawPolygon } from '@/views/layerManagement/components/utils';
|
import * as Cesium from 'cesium';
|
import { PublicCesium } from '@/utils/cesium/publicCesium';
|
import _, { cloneDeep, throttle } from 'lodash';
|
import { ElButton } from 'element-plus';
|
const activeName = ref('电子围栏');
|
const activeType = ref('0');
|
const tabData = ref([
|
{
|
name: '电子围栏',
|
type: '0',
|
},
|
{
|
name: '自定义禁飞区',
|
type: '1',
|
},
|
{
|
name: '国土空间规划',
|
type: '2',
|
},
|
]);
|
|
const handleClick = tab => {
|
const clickedTab = tabData.value.find(item => item.type === tab.paneName);
|
activeType.value = clickedTab.type;
|
activeName.value = clickedTab.name;
|
};
|
// 解析 geo_data 并转换为 DrawPolygon 所需的坐标格式
|
const parseGeoDataToPositions = (geoDataStr, altitude) => {
|
try {
|
// 解析 JSON 字符串为 GeoJSON 对象
|
const geoData = JSON.parse(geoDataStr);
|
// 仅处理 Polygon 类型的地理数据
|
if (geoData.type !== 'Polygon') return [];
|
|
// 提取 coordinates(取第一个面,忽略孔洞),并补充高度
|
const coordinates = geoData.coordinates[0];
|
return coordinates.map(([lng, lat]) => ({
|
lng: Number(lng),
|
lat: Number(lat),
|
height: Number(altitude) || 0, // 高度默认取数据中的 altitude,无则为 0
|
}));
|
} catch (error) {
|
console.error('解析 geo_data 失败:', error);
|
return [];
|
}
|
};
|
|
const selectDataList = ref([]);
|
// 提取图斑坐标
|
function polygonWktToTargetArray(wktPolygon, defaultHeight = 140.6) {
|
try {
|
if (!/^POLYGON\(\([\d\s,.]+\)\)$/.test(wktPolygon)) {
|
throw new Error('输入不是有效的 WKT POLYGON 格式');
|
}
|
|
const coordStr = wktPolygon
|
.replace(/^POLYGON\(\(/, '')
|
.replace(/\)\)$/, '')
|
.trim();
|
|
const coordPairs = coordStr.split(/,\s*/).filter(pair => pair.trim() !== '');
|
|
const result = coordPairs.map(pair => {
|
const [lng, lat] = pair
|
.split(/\s+/)
|
.map(Number)
|
.filter(num => !isNaN(num));
|
|
if (
|
lng === undefined ||
|
lat === undefined ||
|
lng < -180 ||
|
lng > 180 ||
|
lat < -90 ||
|
lat > 90
|
) {
|
throw new Error(`无效的经纬度对:${pair}`);
|
}
|
|
return {
|
lng: lng,
|
lat: lat,
|
longitude: lng, // 与 lng 一致
|
latitude: lat, // 与 lat 一致
|
};
|
});
|
|
const uniqueResult = Array.from(
|
new Map(result.map(item => [`${item.lng}-${item.lat}`, item])).values()
|
);
|
|
return uniqueResult;
|
} catch (error) {
|
return []; // 异常时返回空数组
|
}
|
}
|
|
const handleCoverDataUpdate = data => {
|
selectDataList.value = data;
|
console.log('获取的',selectDataList.value);
|
|
if (selectDataList.value.length > 0) {
|
loadDataToMap(selectDataList.value);
|
} else {
|
drawPolygonExample.removeEntities();
|
}
|
};
|
const loadDataToMap = (dataList) => {
|
if (!viewer) return;
|
viewer.entities.removeAll();
|
// 存储所有有效坐标,用于后续定位
|
const allCoordinates = [];
|
dataList.forEach(item => {
|
// 解析 geo_data 为经纬度坐标数组(使用已有的 parseGeoDataToPositions 方法)
|
const positions = parseGeoDataToPositions(item.geo_data, item.altitude);
|
if (positions.length < 3) {
|
console.warn(`数据 ${item.name} 坐标点不足,无法绘制`);
|
return;
|
}
|
console.log('positions',positions);
|
|
// 转换为 Cesium 所需的 [lng, lat, lng, lat, ...] 扁平数组格式
|
let degreesArray = [];
|
positions.forEach(pos => {
|
degreesArray.push(pos.lng, pos.lat, pos.height);
|
|
});
|
viewer.entities.add({
|
id: `polygon_${item.id}`,
|
customType: 'fence_polygon',
|
customInfo: item,
|
polygon: {
|
hierarchy: new Cesium.PolygonHierarchy(
|
Cesium.Cartesian3.fromDegreesArrayHeights(degreesArray)
|
),
|
material: Cesium.Color.YELLOW.withAlpha(0.5), // 填充色
|
outline: true, // 显示边框
|
outlineColor: Cesium.Color.YELLOW, // 边框色
|
outlineWidth: 2, // 边框宽度
|
clampToGround: true, // 贴地显示
|
|
},
|
|
polyline: {
|
positions: Cesium.Cartesian3.fromDegreesArrayHeights(degreesArray),
|
width: 2,
|
material: Cesium.Color.YELLOW,
|
clampToGround: true,
|
},
|
zIndex: 99, // 层级,确保在其他图层上方显示
|
});
|
});
|
|
// 添加点击事件(参考 entitiesAddSpot 的 spotHighlighting 逻辑)
|
// 移除已有点击事件,避免重复绑定
|
// viewInstance.value?.removeLeftClickEvent('fenceHighlighting');
|
// 绑定新的点击事件,用于高亮选中的围栏
|
// viewInstance.value?.addLeftClickEvent(null, handleFenceClick, 'fenceHighlighting');
|
};
|
// 围栏点击处理函数(高亮选中项 + 可联动右侧编辑组件)
|
const handleFenceClick = (movement) => {
|
if (!viewer) return;
|
|
// 拾取点击的实体
|
const pickedObject = viewer.scene.pick(movement.position);
|
if (Cesium.defined(pickedObject) && pickedObject.id?.customType === 'fence_polygon') {
|
const selectedEntity = pickedObject.id;
|
console.log('选中的围栏数据:', selectedEntity.customInfo);
|
|
// 1. 高亮处理(清除其他实体高亮,仅高亮当前选中)
|
viewer.entities.values.forEach(entity => {
|
if (entity.customType === 'fence_polygon') {
|
// 恢复默认样式
|
entity.polygon.material = entity === selectedEntity
|
? selectedEntity.polygon.material.color.withAlpha(0.8) // 选中时加深透明度
|
: selectedEntity.polygon.material.color.withAlpha(0.5); // 未选中恢复默认
|
}
|
});
|
|
// 2. 联动右侧编辑组件(假设 rightEdit 有接收数据的方法)
|
// 例如:通过 ref 调用 rightEdit 的 setData 方法
|
// rightEditRef.value?.setData(selectedEntity.customInfo);
|
}
|
};
|
let publicCesiumInstance = null;
|
let viewer = null;
|
const viewInstance = shallowRef(null);
|
const homeViewer = shallowRef(null);
|
|
// 初始化绘制工具实例
|
const drawPolygonExample = new DrawPolygon();
|
|
// 地图初始化
|
const initMap = () => {
|
publicCesiumInstance = new PublicCesium({
|
dom: 'layMap',
|
flatMode: false,
|
terrain: true,
|
layerMode: 4,
|
contour: false,
|
});
|
|
viewer = publicCesiumInstance.getViewer();
|
viewInstance.value = publicCesiumInstance;
|
drawPolygonExample.initHandler(viewer);
|
};
|
|
// 处理绘制完成后的多边形坐标(示例)
|
const loadPlanarRoute = async (positions = null, save = false) => {
|
if (!positions || positions.length < 3) return;
|
// console.log('绘制的多边形顶点坐标(Cartesian3):', positions);
|
//坐标转换
|
const cartographics = Cesium.Ellipsoid.WGS84.cartesianArrayToCartographicArray(positions);
|
const latLngPositions = cartographics.map(cartographic => ({
|
lng: Cesium.Math.toDegrees(cartographic.longitude),
|
lat: Cesium.Math.toDegrees(cartographic.latitude),
|
height: cartographic.height || 0,
|
}));
|
// console.log('转换后的经纬度坐标:', latLngPositions);
|
};
|
|
const throttleLoadPlanarRoute = throttle(loadPlanarRoute, 200);
|
drawPolygonExample.subscribe('getPolygonPositions', data => {
|
console.log('绘制的数据',data);
|
|
throttleLoadPlanarRoute(data);
|
});
|
|
// 地图销毁
|
const destroyMap = () => {
|
drawPolygonExample.destroy();
|
|
if (viewer) {
|
viewer.destroy();
|
viewer = null;
|
}
|
publicCesiumInstance = null;
|
};
|
// 阻止浏览器默认
|
const preventDefault = event => {
|
event.preventDefault();
|
return;
|
};
|
const cesiumContextMenu = (isAdd = true) => {
|
let cesium = document.getElementById('layMap');
|
if (!cesium) return;
|
if (isAdd) {
|
cesium.addEventListener('contextmenu', preventDefault);
|
} else {
|
cesium.removeEventListener('contextmenu', preventDefault);
|
}
|
};
|
onMounted(() => {
|
initMap();
|
cesiumContextMenu();
|
});
|
|
onBeforeUnmount(() => {
|
destroyMap();
|
cesiumContextMenu(false);
|
});
|
</script>
|
|
<style scoped lang="scss">
|
.layerContainer {
|
width: 100%;
|
height: 90vh;
|
}
|
.mapContainer {
|
position: relative;
|
width: 100%;
|
height: 80vh;
|
#layMap {
|
width: 100%;
|
height: 100%;
|
}
|
}
|
</style>
|