点击暴雨模拟按钮时候显示降雨图层
This commit is contained in:
@@ -65,6 +65,14 @@
|
|||||||
useStatusStore().mapLayers.riskPointShow.loading
|
useStatusStore().mapLayers.riskPointShow.loading
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<!-- 降雨栅格图层组件(暴雨模拟) -->
|
||||||
|
<RainfallGridComponent
|
||||||
|
v-if="
|
||||||
|
useStatusStore().appLoadingCompleted &&
|
||||||
|
useStatusStore().weatherLayers.showRainfallGrid.loading
|
||||||
|
"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -76,6 +84,7 @@
|
|||||||
import DebrisFlowComponent from '@/component/rain-earthquake/basic/DebrisFlowComponent.vue';
|
import DebrisFlowComponent from '@/component/rain-earthquake/basic/DebrisFlowComponent.vue';
|
||||||
import WaterLoggingComponent from '@/component/rain-earthquake/basic/WaterLoggingComponent.vue';
|
import WaterLoggingComponent from '@/component/rain-earthquake/basic/WaterLoggingComponent.vue';
|
||||||
import FlashFloodComponent from '@/component/rain-earthquake/basic/FlashFloodComponent.vue';
|
import FlashFloodComponent from '@/component/rain-earthquake/basic/FlashFloodComponent.vue';
|
||||||
|
import RainfallGridComponent from '@/component/rain-earthquake/detail-panels/RainfallGridComponent.vue';
|
||||||
import { useStatusStore } from '@/stores/useStatusStore';
|
import { useStatusStore } from '@/stores/useStatusStore';
|
||||||
|
|
||||||
// 获取父组件传递德数据
|
// 获取父组件传递德数据
|
||||||
|
|||||||
@@ -0,0 +1,200 @@
|
|||||||
|
<!-- 降雨栅格图层组件 -->
|
||||||
|
<template>
|
||||||
|
<div></div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { useStatusStore } from '@/stores/useStatusStore';
|
||||||
|
import { CesiumUtilsSingleton } from '@/utils/cesium/CesiumUtils';
|
||||||
|
import { watch, onMounted, onBeforeUnmount } from 'vue';
|
||||||
|
import { $api } from '@/api/api';
|
||||||
|
import {
|
||||||
|
Color,
|
||||||
|
ColorGeometryInstanceAttribute,
|
||||||
|
GeometryInstance,
|
||||||
|
PolygonGeometry,
|
||||||
|
PolygonHierarchy,
|
||||||
|
Cartesian3,
|
||||||
|
PerInstanceColorAppearance,
|
||||||
|
GroundPrimitive,
|
||||||
|
} from 'cesium';
|
||||||
|
import type { RainfallFeature } from '@/types/rainstorm/RainfallGridResponse';
|
||||||
|
|
||||||
|
const statusStore = useStatusStore();
|
||||||
|
let primitiveCollection: GroundPrimitive | null = null;
|
||||||
|
let isLoaded = false; // 标记数据是否已加载完成
|
||||||
|
let pendingShow = false; // 标记是否有待显示的请求
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 颜色缓存
|
||||||
|
*/
|
||||||
|
const colorCache = new Map<string, Color>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析颜色字符串
|
||||||
|
*/
|
||||||
|
const parseColor = (colorStr: string): Color | null => {
|
||||||
|
if (colorCache.has(colorStr)) {
|
||||||
|
return colorCache.get(colorStr)!;
|
||||||
|
}
|
||||||
|
|
||||||
|
const match = colorStr.match(
|
||||||
|
/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/
|
||||||
|
);
|
||||||
|
if (!match) return null;
|
||||||
|
|
||||||
|
const r = parseInt(match[1]) / 255;
|
||||||
|
const g = parseInt(match[2]) / 255;
|
||||||
|
const b = parseInt(match[3]) / 255;
|
||||||
|
const a = match[4] ? parseFloat(match[4]) : 1.0;
|
||||||
|
|
||||||
|
const color = new Color(r, g, b, a);
|
||||||
|
colorCache.set(colorStr, color);
|
||||||
|
return color;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载降雨栅格数据
|
||||||
|
*/
|
||||||
|
const loadRainfallGrid = async () => {
|
||||||
|
try {
|
||||||
|
const res = await $api.meteorology.getRainfallGrid({
|
||||||
|
time: '2025-08-20T12:00:00',
|
||||||
|
resolution: 0.01,
|
||||||
|
});
|
||||||
|
|
||||||
|
const geoJsonData = res.data;
|
||||||
|
|
||||||
|
if (geoJsonData && geoJsonData.type === 'FeatureCollection') {
|
||||||
|
// 立即让出主线程,避免阻塞渲染
|
||||||
|
await new Promise((resolve) => requestAnimationFrame(resolve));
|
||||||
|
|
||||||
|
// 分批处理,避免长时间阻塞主线程
|
||||||
|
const features = geoJsonData.features;
|
||||||
|
const batchSize = 500; // 每批处理500个
|
||||||
|
const instances: GeometryInstance[] = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < features.length; i += batchSize) {
|
||||||
|
const batch = features.slice(i, i + batchSize);
|
||||||
|
|
||||||
|
batch.forEach((feature: RainfallFeature) => {
|
||||||
|
if (!feature.geometry || !feature.geometry.coordinates) return;
|
||||||
|
|
||||||
|
// 获取颜色
|
||||||
|
let color = Color.WHITE;
|
||||||
|
if (feature.properties?.color) {
|
||||||
|
const parsedColor = parseColor(feature.properties.color);
|
||||||
|
if (parsedColor) {
|
||||||
|
color = parsedColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 转换坐标
|
||||||
|
const coordinates = feature.geometry.coordinates[0];
|
||||||
|
const positions = coordinates.map((coord: number[]) =>
|
||||||
|
Cartesian3.fromDegrees(coord[0], coord[1], 0)
|
||||||
|
);
|
||||||
|
|
||||||
|
// 创建几何实例
|
||||||
|
const instance = new GeometryInstance({
|
||||||
|
geometry: new PolygonGeometry({
|
||||||
|
polygonHierarchy: new PolygonHierarchy(positions),
|
||||||
|
vertexFormat: PerInstanceColorAppearance.VERTEX_FORMAT,
|
||||||
|
}),
|
||||||
|
attributes: {
|
||||||
|
color: ColorGeometryInstanceAttribute.fromColor(color),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
instances.push(instance);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 每批之间让出主线程
|
||||||
|
if (i + batchSize < features.length) {
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 让出主线程
|
||||||
|
await new Promise((resolve) => requestAnimationFrame(resolve));
|
||||||
|
|
||||||
|
// 创建 Primitive(初始状态为隐藏)
|
||||||
|
primitiveCollection = new GroundPrimitive({
|
||||||
|
geometryInstances: instances,
|
||||||
|
appearance: new PerInstanceColorAppearance({
|
||||||
|
flat: true, // 禁用光照计算
|
||||||
|
translucent: true, // 支持半透明
|
||||||
|
}),
|
||||||
|
asynchronous: true, // 异步创建
|
||||||
|
allowPicking: false, // 禁用拾取
|
||||||
|
releaseGeometryInstances: true, // 释放几何实例内存
|
||||||
|
interleave: true, // 交错顶点数据,提升GPU性能
|
||||||
|
show: false, // 初始隐藏
|
||||||
|
});
|
||||||
|
|
||||||
|
// 添加到场景
|
||||||
|
const viewer = CesiumUtilsSingleton.getViewer();
|
||||||
|
if (viewer) {
|
||||||
|
await viewer.scene.primitives.add(primitiveCollection);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 标记为已加载
|
||||||
|
isLoaded = true;
|
||||||
|
|
||||||
|
// 如果有待显示的请求,立即显示
|
||||||
|
if (pendingShow) {
|
||||||
|
primitiveCollection.show = true;
|
||||||
|
pendingShow = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载降雨栅格数据失败:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 组件挂载时立即检查并加载
|
||||||
|
onMounted(async () => {
|
||||||
|
// 始终预加载数据,无论当前 show 状态如何
|
||||||
|
const layerIds = CesiumUtilsSingleton.getGeoJsonLayerIds('custom');
|
||||||
|
if (!layerIds.has('rainfall-grid-layer')) {
|
||||||
|
await loadRainfallGrid();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 监听显示隐藏状态
|
||||||
|
watch(
|
||||||
|
() => statusStore.weatherLayers.showRainfallGrid.show,
|
||||||
|
async (newValue: boolean) => {
|
||||||
|
if (newValue) {
|
||||||
|
// 需要显示
|
||||||
|
if (isLoaded && primitiveCollection) {
|
||||||
|
// 数据已加载完成,直接显示
|
||||||
|
primitiveCollection.show = true;
|
||||||
|
} else if (!isLoaded) {
|
||||||
|
// 数据还在加载中,标记为待显示
|
||||||
|
pendingShow = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 需要隐藏
|
||||||
|
if (primitiveCollection) {
|
||||||
|
primitiveCollection.show = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// 组件卸载时清理资源
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
if (primitiveCollection) {
|
||||||
|
const viewer = CesiumUtilsSingleton.getViewer();
|
||||||
|
if (viewer) {
|
||||||
|
viewer.scene.primitives.remove(primitiveCollection);
|
||||||
|
}
|
||||||
|
primitiveCollection = null;
|
||||||
|
}
|
||||||
|
isLoaded = false;
|
||||||
|
pendingShow = false;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
@@ -149,6 +149,13 @@ export const useLayerControl = () => {
|
|||||||
useStatusStore().mapLayers.faultShow.loading = true;
|
useStatusStore().mapLayers.faultShow.loading = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示降雨栅格
|
||||||
|
*/
|
||||||
|
const clickRainfallGrid = () => {
|
||||||
|
useStatusStore().weatherLayers.showRainfallGrid.loading = true;
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
clickRiskPoint,
|
clickRiskPoint,
|
||||||
clickFault,
|
clickFault,
|
||||||
@@ -170,5 +177,6 @@ export const useLayerControl = () => {
|
|||||||
clickDebrisFlowHiddenPoint,
|
clickDebrisFlowHiddenPoint,
|
||||||
clickWaterLoggingHiddenPoint,
|
clickWaterLoggingHiddenPoint,
|
||||||
clickFlashFloodHiddenPoint,
|
clickFlashFloodHiddenPoint,
|
||||||
|
clickRainfallGrid,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import { useStatusStore } from '@/stores/useStatusStore.ts';
|
|||||||
import { CesiumUtilsSingleton } from '@/utils/cesium/CesiumUtils.ts';
|
import { CesiumUtilsSingleton } from '@/utils/cesium/CesiumUtils.ts';
|
||||||
import config from '@/config/config.json';
|
import config from '@/config/config.json';
|
||||||
import { useButtonSelectedIdStore } from '@/stores/useButtonSelectedIdStore';
|
import { useButtonSelectedIdStore } from '@/stores/useButtonSelectedIdStore';
|
||||||
import { $api } from '@/api/api';
|
|
||||||
|
|
||||||
export const useRightHandle = () => {
|
export const useRightHandle = () => {
|
||||||
/**
|
/**
|
||||||
@@ -11,15 +10,12 @@ export const useRightHandle = () => {
|
|||||||
*/
|
*/
|
||||||
const rainstormSimulation = (status: unknown) => {
|
const rainstormSimulation = (status: unknown) => {
|
||||||
if (status as boolean) {
|
if (status as boolean) {
|
||||||
// 获取降雨栅格
|
// 开启暴雨模拟:显示降雨栅格图层
|
||||||
$api.meteorology
|
useStatusStore().weatherLayers.showRainfallGrid.loading = true;
|
||||||
.getRainfallGrid({
|
useStatusStore().weatherLayers.showRainfallGrid.show = true;
|
||||||
startTime: '2025-08-20T00:00:00',
|
} else {
|
||||||
endTime: '2025-08-20T00:00:00',
|
// 关闭暴雨模拟:隐藏降雨栅格图层
|
||||||
})
|
useStatusStore().weatherLayers.showRainfallGrid.show = false;
|
||||||
.then((res) => {
|
|
||||||
console.log(res);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,11 @@ import { LoadingResource } from '@/types/common/LoadingResourceType.ts';
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const useRainDisasterChain = () => {
|
export const useRainDisasterChain = () => {
|
||||||
|
// 初始化暴雨模拟状态(因为右侧按钮默认选中)
|
||||||
|
const statusStore = useStatusStore();
|
||||||
|
statusStore.weatherLayers.showRainfallGrid.loading = true;
|
||||||
|
statusStore.weatherLayers.showRainfallGrid.show = true;
|
||||||
|
|
||||||
// ================灾害链影响点列表================================
|
// ================灾害链影响点列表================================
|
||||||
/**
|
/**
|
||||||
* 搜索条件
|
* 搜索条件
|
||||||
@@ -168,7 +173,6 @@ export const useRainDisasterChain = () => {
|
|||||||
name: '暴雨模拟',
|
name: '暴雨模拟',
|
||||||
callback: (status: unknown) =>
|
callback: (status: unknown) =>
|
||||||
useRightHandle().rainstormSimulation(status),
|
useRightHandle().rainstormSimulation(status),
|
||||||
selected: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '暴雨触发',
|
name: '暴雨触发',
|
||||||
|
|||||||
@@ -168,6 +168,17 @@ export const useStatusStore = defineStore('status', () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 气象图层显示状态
|
||||||
|
*/
|
||||||
|
const weatherLayers = reactive({
|
||||||
|
/** 显示降雨栅格 */
|
||||||
|
showRainfallGrid: {
|
||||||
|
show: false,
|
||||||
|
loading: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
// ============================ 地图功能显示状态 ================================
|
// ============================ 地图功能显示状态 ================================
|
||||||
const functionStatus = reactive({
|
const functionStatus = reactive({
|
||||||
aroundAnalysis: {
|
aroundAnalysis: {
|
||||||
@@ -294,6 +305,12 @@ export const useStatusStore = defineStore('status', () => {
|
|||||||
loading: false,
|
loading: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 气象图层显示状态重置
|
||||||
|
weatherLayers.showRainfallGrid = {
|
||||||
|
show: false,
|
||||||
|
loading: true,
|
||||||
|
};
|
||||||
|
|
||||||
// 功能显示状态重置
|
// 功能显示状态重置
|
||||||
functionStatus.aroundAnalysis = {
|
functionStatus.aroundAnalysis = {
|
||||||
show: false,
|
show: false,
|
||||||
@@ -307,6 +324,7 @@ export const useStatusStore = defineStore('status', () => {
|
|||||||
mapLayers,
|
mapLayers,
|
||||||
poiLayers,
|
poiLayers,
|
||||||
infrastructureLayers,
|
infrastructureLayers,
|
||||||
|
weatherLayers,
|
||||||
functionStatus,
|
functionStatus,
|
||||||
reset,
|
reset,
|
||||||
resetScene,
|
resetScene,
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
export interface RainfallGridRequest {
|
export interface RainfallGridRequest {
|
||||||
// 开始时间,默认为当前时间
|
// 时间,默认为当前时间
|
||||||
startTime?: string;
|
time?: string;
|
||||||
// 结束时间,默认为当前时间
|
// 分辨率,默认为0.01,最小为0,最大为0.1
|
||||||
endTime?: string;
|
|
||||||
// 区域id,默认为1
|
|
||||||
districtId?: number;
|
|
||||||
// 分辨率,默认为0.01,最小为0,最大为1
|
|
||||||
resolution?: number;
|
resolution?: number;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
interface RainfallFeature {
|
export interface RainfallFeature {
|
||||||
type: 'Feature';
|
type: 'Feature';
|
||||||
geometry: {
|
geometry: {
|
||||||
type: 'Polygon';
|
type: 'Polygon';
|
||||||
@@ -11,20 +11,16 @@ interface RainfallFeature {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface RainfallGridResponse {
|
export interface RainfallGridResponse {
|
||||||
code: number;
|
type: 'FeatureCollection';
|
||||||
message: string;
|
features: RainfallFeature[];
|
||||||
data: {
|
metadata: {
|
||||||
type: 'FeatureCollection';
|
resolution: number;
|
||||||
features: RainfallFeature[];
|
grid_size: number[];
|
||||||
metadata: {
|
bounds: {
|
||||||
resolution: number;
|
min_lon: number;
|
||||||
grid_size: number[];
|
max_lon: number;
|
||||||
bounds: {
|
min_lat: number;
|
||||||
min_lon: number;
|
max_lat: number;
|
||||||
max_lon: number;
|
|
||||||
min_lat: number;
|
|
||||||
max_lat: number;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
} | null;
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user