| | |
| | | <template> |
| | | <Left @start="startSimulate" @end="endSimulate" /> |
| | | <echartInfo :isDynamicMode="isDynamicMode" :isFinish="isFinish" /> |
| | | <TimeLine |
| | | v-if="showWaterSimulate" |
| | | @time-update="timeUpdate" |
| | | @is-playing="isPlaying" |
| | | :waterSimulateParams="waterSimulateParams" |
| | | @playbackFinished="playbackFinished" |
| | | @end="endSimulate" |
| | | /> |
| | | <DebuffDetail |
| | | v-if="showDebuffDetail" |
| | | @open="openDetail" |
| | | @close="showDebuffDetail = false" |
| | | /> |
| | | <echartInfo :isDynamicMode="isDynamicMode" :isFinish="isFinish" v-if="loadingSim" /> |
| | | <TimeLine v-if="showWaterSimulate" @time-update="timeUpdate" @is-playing="isPlaying" |
| | | :waterSimulateParams="waterSimulateParams" @playbackFinished="playbackFinished" @end="endSimulate" /> |
| | | <DebuffDetail v-if="showDebuffDetail" @open="openDetail" @close="showDebuffDetail = false" /> |
| | | <DebuffTable v-if="showDebuffTable" @close="closeDebuffTable" /> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { EventBus } from "@/eventBus"; // 引入事件总线 |
| | | import { ref, onMounted, onUnmounted, provide } from "vue"; |
| | | import TimeLine from "@/components/menu/TimeLine.vue"; |
| | | import Left from "./left/Left.vue"; |
| | |
| | | import DebuffTable from "@/components/tools/DebuffTable.vue"; |
| | | import { getMaxInfluenceArea } from "@/api/index"; |
| | | import { createPoint, geomToGeoJSON } from "@/utils/map.js"; |
| | | import { loadAreaPolygon, clearAreaPolygon } from "@/utils/area"; |
| | | |
| | | import colors from "@/assets/img/left/colors3.png"; |
| | | import danger from "@/assets/img/left/danger.png"; |
| | |
| | | const showDebuffTable = ref(false); |
| | | const isDynamicMode = ref(false); |
| | | const isFinish = ref(true); |
| | | const loadingSim = ref(false); |
| | | |
| | | const treeMap = new Map(); |
| | | |
| | | // 提供方法给所有子组件 |
| | | provide("simulateActions", { |
| | |
| | | function startSimulate(form) { |
| | | // console.log("form", form); |
| | | showWaterSimulate.value = true; |
| | | loadingSim.value = true |
| | | waterSimulateParams.value = form; |
| | | } |
| | | function endSimulate() { |
| | | |
| | | loadingSim.value = false |
| | | showDebuffDetail.value = false |
| | | clearTrailLine(); |
| | | removeEmergencyPoints() |
| | | removeDataSources(); |
| | | setTimeout(() => { |
| | | showWaterSimulate.value = false; |
| | | isDynamicMode.value = false; |
| | | }, 2000); |
| | | // 清除威胁对象表格内容 |
| | | EventBus.emit("reset-table"); |
| | | // 清除降雨数据内容 |
| | | EventBus.emit("clear-echart"); |
| | | }, 1000); |
| | | } |
| | | const MaxInfluenceAreaList = ref([]); |
| | | // 初始化 dataSources 全局数组 |
| | | const dataSources = []; |
| | | function getTimeMarkers() { |
| | | // 将 list 数据的 geom EPSG:4326 坐标转换为 WGS84 坐标系的 GeoJSON 数据 |
| | | async function getTimeMarkers() { |
| | | const list = MaxInfluenceAreaList.value; |
| | | list.forEach((item, index) => { |
| | | const geojson = JSON.parse(item.geom); // 解析 geom 字段为 GeoJSON 对象 |
| | | Cesium.GeoJsonDataSource.load(geojson).then((dataSource) => { |
| | | // 设置样式,将颜色改为红色 |
| | | try { |
| | | const loadPromises = list.map(async (item) => { |
| | | const geojson = JSON.parse(item.geom); |
| | | const dataSource = await Cesium.GeoJsonDataSource.load(geojson); |
| | | dataSource.entities.values.forEach((entity) => { |
| | | // entity.polygon.material = new Cesium.Color(1.0, 0.0, 0.0, 0.6); // 红色,80% 不透明度 |
| | | // entity.polygon.material = new Cesium.Color.YELLOW; // 红色,80% 不透明度 |
| | | if (!entity.properties) { |
| | | entity.properties = new Cesium.PropertyBag(); |
| | | } |
| | | entity.properties.addProperty("id", item.id); |
| | | entity.properties.addProperty("warningLevel", item.warningLevel); |
| | | entity.properties.addProperty("zoneId", item.zoneId); |
| | | entity.polygon.outlineColor = Cesium.Color.YELLOW; |
| | | entity.polygon.outline = true; |
| | | |
| | | // 使用提供的 X, Y, Z 坐标作为图标位置 |
| | | const position = Cesium.Cartesian3.fromDegrees(item.X, item.Y, item.Z); |
| | | |
| | | // 在中心点上方添加一个图标实体 |
| | | const billboardEntity = viewer.entities.add({ |
| | | position: position, |
| | | billboard: { |
| | | image: danger, // 图标的路径 |
| | | scale: 1.0, // 图标缩放比例 |
| | | verticalOrigin: Cesium.VerticalOrigin.BOTTOM, // 图标底部对齐到中心点 |
| | | heightReference: Cesium.HeightReference.CLAMP_TO_GROUND, // 图标贴地 |
| | | distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0, 1500), // 控制可见距离范围 (0米到1500米) |
| | | image: danger, |
| | | scale: 1.0, |
| | | verticalOrigin: Cesium.VerticalOrigin.BOTTOM, |
| | | heightReference: Cesium.HeightReference.CLAMP_TO_GROUND, |
| | | distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0, 1500), |
| | | }, |
| | | }); |
| | | |
| | | // 将图标实体与当前数据源关联 |
| | | dataSource.entities.add(billboardEntity); |
| | | }); |
| | | |
| | | // 添加数据源到 viewer |
| | | viewer.dataSources.add(dataSource); |
| | | dataSources.push(dataSource); |
| | | }); |
| | | }); |
| | | await Promise.all(loadPromises); |
| | | setupRowClickListener(dataSources); |
| | | } catch (error) { |
| | | } |
| | | } |
| | | // 清除隐患点 |
| | | function removeDataSources() { |
| | | dataSources.forEach((dataSource) => { |
| | | dataSources.forEach((dataSource, index) => { |
| | | // 遍历当前数据源的所有实体 |
| | | const entities = dataSource.entities.values; |
| | | entities.forEach((entity, entityIndex) => { |
| | | if (entity.billboard) { |
| | | viewer.entities.remove(entity); |
| | | } |
| | | }); |
| | | viewer.dataSources.remove(dataSource); |
| | | }); |
| | | dataSources.length = 0; |
| | | } |
| | | // 避险点,绿色富文本 |
| | | function addTetrahedron(visible) { |
| | | const emergencyAreaList = []; |
| | | // 这里是添加避险点底层面片 |
| | | loadAreaPolygon("/json/emergency_area.geojson", true).then((entities) => { |
| | | emergencyAreaList.push(...entities); |
| | | }); |
| | | // console.log('polygon', polygon); |
| | | |
| | | let list = [ |
| | | { |
| | | name: "尹建华家", |
| | | longitude: 116.593517, |
| | | latitude: 40.568391, |
| | | altitude: 528.45, |
| | | }, |
| | | // { |
| | | // name: "范振江家", |
| | | // longitude: 116.591059, |
| | | // latitude: 40.574068, |
| | | // altitude: 528, |
| | | // }, |
| | | // { |
| | | // name: "后坡", |
| | | // longitude: 116.597975, |
| | | // latitude: 40.558199, |
| | | // altitude: 528, |
| | | // }, |
| | | ]; |
| | | // 这里是添加避险点富文本高亮显示 |
| | | list.forEach((item) => { |
| | | let point = earthCtrl.factory.createRichTextPoint("避险点", [item.longitude, item.latitude, item.altitude - 10], { |
| | | distanceDisplayCondition: new SmartEarth.Cesium.DistanceDisplayCondition(0, 2000), |
| | | fontColor: "#ffffff", |
| | | fontSize: 20 |
| | | }, "0"); |
| | | console.log("point", point); |
| | | emergencyAreaList.push(point); |
| | | }); |
| | | treeMap.set("避险点", emergencyAreaList); |
| | | } |
| | | // 删除避险点的富文本实体 |
| | | function removeEmergencyPoints() { |
| | | const emergencyAreaList = treeMap.get("避险点"); // 获取存储的避险点实体列表 |
| | | if (emergencyAreaList && emergencyAreaList.length > 0) { |
| | | emergencyAreaList.forEach((entity) => { |
| | | if (entity && typeof entity.deleteObject === 'function') { |
| | | // 如果有 deleteObject 方法,优先调用 |
| | | entity.deleteObject(); |
| | | } else if (entity && typeof entity.clear === 'function') { |
| | | // 如果有 clear 方法,调用 clear |
| | | entity.clear(); |
| | | } else if (entity && earthCtrl && earthCtrl.coreMap) { |
| | | // 如果是 Cesium 实体,使用 coreMap.entities.remove 移除 |
| | | earthCtrl.coreMap.entities.remove(entity); |
| | | } |
| | | }); |
| | | treeMap.set("避险点", []); // 清空存储的避险点列表 |
| | | } |
| | | } |
| | | |
| | | let TrailLine = []; |
| | | async function showLine() { |
| | | const position = [ |
| | |
| | | }); |
| | | TrailLine = []; |
| | | } |
| | | // function showLine() { |
| | | // earthCtrl.factory.createSimpleGraphic( |
| | | // "polyline", |
| | | // { clampToGround: true }, |
| | | // (entity) => { |
| | | // if (entity) { |
| | | // const position = [ |
| | | // { |
| | | // x: -2172540.8322597803, |
| | | // y: 4339064.62665997, |
| | | // z: 4126183.3895281963, |
| | | // }, |
| | | // { |
| | | // x: -2172480.18394144, |
| | | // y: 4339033.15167176, |
| | | // z: 4126240.9529584926, |
| | | // }, |
| | | // { |
| | | // x: -2172454.114348403, |
| | | // y: 4339020.0398392705, |
| | | // z: 4126261.946960697, |
| | | // }, |
| | | // { |
| | | // x: -2172377.9670952093, |
| | | // y: 4338976.609385458, |
| | | // z: 4126333.862357211, |
| | | // }, |
| | | // { |
| | | // x: -2172299.4142002705, |
| | | // y: 4338951.971578909, |
| | | // z: 4126397.5205803993, |
| | | // }, |
| | | // { |
| | | // x: -2172245.1703274297, |
| | | // y: 4338940.86037857, |
| | | // z: 4126436.276389208, |
| | | // }, |
| | | // { |
| | | // x: -2172176.7332184147, |
| | | // y: 4338930.525741544, |
| | | // z: 4126477.629952572, |
| | | // }, |
| | | // { |
| | | // x: -2172173.8151051304, |
| | | // y: 4338939.043883864, |
| | | // z: 4126469.336927342, |
| | | // }, |
| | | // { |
| | | // x: -2172173.7151194704, |
| | | // y: 4338939.023235937, |
| | | // z: 4126469.4107743693, |
| | | // }, |
| | | // ]; |
| | | // // console.log("positions", positions); |
| | | // addWall(position, [entity]); |
| | | |
| | | // earthCtrl.factory.SimpleGraphic.remove(entity.id); |
| | | // } |
| | | // } |
| | | // ); |
| | | // } |
| | | function timeUpdate(percentage) { |
| | | if (percentage > 99) { |
| | | if (showDebuffDetail.value) { |
| | |
| | | checkedKeys.value = ["避险点"]; |
| | | showDebuffDetail.value = true; |
| | | getTimeMarkers(); |
| | | addTetrahedron(); |
| | | showLine(); |
| | | } |
| | | } |
| | |
| | | function playbackFinished(val) { |
| | | isFinish.value = val; |
| | | } |
| | | // 定义全局变量存储当前正在闪动的面片 |
| | | let flashingPolygon = null; |
| | | // 添加事件监听器,接收来自表格组件的事件 |
| | | function setupRowClickListener(dataSources) { |
| | | if (!Array.isArray(dataSources) || dataSources.length === 0) { |
| | | console.error("Data sources array is undefined or empty!"); |
| | | return; |
| | | } |
| | | |
| | | EventBus.on("row-clicked", (id) => { |
| | | const clickedEntity = findEntityById(id, dataSources); |
| | | if (clickedEntity) { |
| | | if (flashingPolygon && flashingPolygon !== clickedEntity) { |
| | | stopFlashing(flashingPolygon); |
| | | } |
| | | |
| | | startFlashing(clickedEntity); |
| | | flashingPolygon = clickedEntity; |
| | | } else { |
| | | } |
| | | }); |
| | | } |
| | | function findEntityById(id, dataSources) { |
| | | if (!Array.isArray(dataSources) || dataSources.length === 0) { |
| | | console.error("Data sources array is undefined or empty!"); |
| | | return null; |
| | | } |
| | | |
| | | console.log("Searching for ID:", id); |
| | | for (const dataSource of dataSources) { |
| | | const entities = dataSource.entities.values; |
| | | for (const entity of entities) { |
| | | const entityId = entity.properties?.id?.getValue(); |
| | | if (String(entityId) === String(id)) { |
| | | return entity; |
| | | } |
| | | } |
| | | } |
| | | return null; |
| | | } |
| | | // 开始闪动效果 |
| | | function startFlashing(polygonEntity) { |
| | | // 存储原始颜色 |
| | | const originalColor = polygonEntity.polygon.material.color.getValue(); |
| | | polygonEntity._originalColor = originalColor; // 将原始颜色保存到实体中 |
| | | // 创建颜色变化的回调函数 |
| | | let isFlashing = true; // 标记是否正在闪动 |
| | | polygonEntity.polygon.material = new Cesium.ColorMaterialProperty( |
| | | new Cesium.CallbackProperty(() => { |
| | | if (!isFlashing) return originalColor; // 如果停止闪动,恢复原始颜色 |
| | | // 切换颜色(例如红色和原始颜色交替) |
| | | const currentTime = Date.now(); |
| | | const flashColor = Cesium.Color.RED.withAlpha(1); // 闪动颜色 |
| | | return Math.floor(currentTime / 500) % 2 === 0 ? flashColor : originalColor; |
| | | }, false) |
| | | ); |
| | | // 将闪动状态保存到实体上,便于后续控制 |
| | | polygonEntity._isFlashing = isFlashing; |
| | | } |
| | | |
| | | // 停止闪动效果 |
| | | function stopFlashing(polygonEntity) { |
| | | // 恢复原始颜色 |
| | | const originalColor = polygonEntity._originalColor || Cesium.Color.WHITE; // 如果没有原始颜色,默认使用白色 |
| | | polygonEntity.polygon.material = new Cesium.ColorMaterialProperty(originalColor); |
| | | // 清空闪动状态 |
| | | polygonEntity._isFlashing = false; |
| | | polygonEntity._originalColor = null; // 清除保存的原始颜色 |
| | | } |
| | | onMounted(() => { |
| | | setupRowClickListener() |
| | | getMaxInfluenceAreaData(); |
| | | }); |
| | | onUnmounted(() => { |