diff --git a/src/app/OlMap/Controls/Toolbar.tsx b/src/app/OlMap/Controls/Toolbar.tsx index 171c514..c40dd4e 100644 --- a/src/app/OlMap/Controls/Toolbar.tsx +++ b/src/app/OlMap/Controls/Toolbar.tsx @@ -347,7 +347,7 @@ const Toolbar: React.FC = ({ hiddenButtons, queryType }) => { // 从要素属性中提取属性面板需要的数据 const getFeatureProperties = useCallback(() => { if (!highlightFeature) return {}; - + const layer = highlightFeature?.getId()?.toString().split(".")[0]; const properties = highlightFeature.getProperties(); // 计算属性字段,增加 key 字段 const pipeComputedFields = [ @@ -367,7 +367,7 @@ const Toolbar: React.FC = ({ hiddenButtons, queryType }) => { { key: "quality", label: "水质", unit: "mg/L" }, ]; - if (properties.geometry.getType() === "LineString") { + if (layer === "geo_pipes_mat" || layer === "geo_pipes") { let result = { id: properties.id, type: "管道", @@ -400,7 +400,7 @@ const Toolbar: React.FC = ({ hiddenButtons, queryType }) => { } return result; } - if (properties.geometry.getType() === "Point") { + if (layer === "geo_junctions_mat" || layer === "geo_junctions") { let result = { id: properties.id, type: "节点", @@ -432,6 +432,183 @@ const Toolbar: React.FC = ({ hiddenButtons, queryType }) => { } return result; } + if (layer === "geo_tanks_mat" || layer === "geo_tanks") { + return { + id: properties.id, + type: "水池", + properties: [ + { + label: "海拔", + value: properties.elevation?.toFixed?.(1), + unit: "m", + }, + { + label: "初始水位", + value: properties.init_level?.toFixed?.(1), + unit: "m", + }, + { + label: "最低水位", + value: properties.min_level?.toFixed?.(1), + unit: "m", + }, + { + label: "最高水位", + value: properties.max_level?.toFixed?.(1), + unit: "m", + }, + { + label: "直径", + value: properties.diameter?.toFixed?.(1), + unit: "m", + }, + { + label: "最小容积", + value: properties.min_vol?.toFixed?.(1), + unit: "m³", + }, + // { + // label: "容积曲线", + // value: properties.vol_curve, + // }, + { + label: "溢出", + value: properties.overflow ? "是" : "否", + }, + ], + }; + } + if (layer === "geo_reservoirs_mat" || layer === "geo_reservoirs") { + return { + id: properties.id, + type: "水库", + properties: [ + { + label: "水头", + value: properties.head?.toFixed?.(1), + unit: "m", + }, + // { + // label: "模式", + // value: properties.pattern, + // }, + ], + }; + } + if (layer === "geo_pumps_mat" || layer === "geo_pumps") { + return { + id: properties.id, + type: "水泵", + properties: [ + { label: "起始节点 ID", value: properties.node1 }, + { label: "终点节点 ID", value: properties.node2 }, + { + label: "功率", + value: properties.power?.toFixed?.(1), + unit: "kW", + }, + { + label: "扬程", + value: properties.head?.toFixed?.(1), + unit: "m", + }, + { + label: "转速", + value: properties.speed?.toFixed?.(1), + unit: "rpm", + }, + { + label: "模式", + value: properties.pattern, + }, + ], + }; + } + if (layer === "geo_valves_mat" || layer === "geo_valves") { + return { + id: properties.id, + type: "阀门", + properties: [ + { label: "起始节点 ID", value: properties.node1 }, + { label: "终点节点 ID", value: properties.node2 }, + { + label: "直径", + value: properties.diameter?.toFixed?.(1), + unit: "mm", + }, + { + label: "阀门类型", + value: properties.v_type, + }, + // { + // label: "设置", + // value: properties.setting?.toFixed?.(2), + // }, + { + label: "局部损失", + value: properties.minor_loss?.toFixed?.(2), + }, + ], + }; + } + // 传输频率文字对应 + const getTransmissionFrequency = (transmission_frequency: string) => { + // 传输频率文本:00:01:00,00:05:00,00:10:00,00:30:00,01:00:00,转换为分钟数 + const parts = transmission_frequency.split(":"); + if (parts.length !== 3) return transmission_frequency; + const hours = parseInt(parts[0], 10); + const minutes = parseInt(parts[1], 10); + const seconds = parseInt(parts[2], 10); + const totalMinutes = hours * 60 + minutes + (seconds >= 30 ? 1 : 0); + return totalMinutes; + }; + // 可靠度文字映射 + const getReliability = (reliability: number) => { + switch (reliability) { + case 1: + return "高"; + case 2: + return "中"; + case 3: + return "低"; + default: + return "未知"; + } + }; + if (layer === "geo_scada_mat" || layer === "geo_scada") { + let result = { + id: properties.id, + type: "SCADA设备", + properties: [ + { + label: "类型", + value: + properties.type === "pipe_flow" ? "流量传感器" : "压力传感器", + }, + { + label: "关联节点 ID", + value: properties.associated_element_id, + }, + { + label: "传输模式", + value: + properties.transmission_mode === "non_realtime" + ? "定时传输" + : "实时传输", + }, + { + label: "传输频率", + value: getTransmissionFrequency(properties.transmission_frequency), + unit: "分钟", + }, + { + label: "可靠性", + value: getReliability(properties.reliability), + }, + ], + }; + return result; + } return {}; }, [highlightFeature, computedProperties]); @@ -449,7 +626,7 @@ const Toolbar: React.FC = ({ hiddenButtons, queryType }) => { {!hiddenButtons?.includes("draw") && ( } - name="矢量编辑" + name="标记绘制" isActive={activeTools.includes("draw")} onClick={() => handleToolClick("draw")} /> diff --git a/src/components/olmap/BurstPipeAnalysis/AnalysisParameters.tsx b/src/components/olmap/BurstPipeAnalysis/AnalysisParameters.tsx index c188fd6..25e5ee5 100644 --- a/src/components/olmap/BurstPipeAnalysis/AnalysisParameters.tsx +++ b/src/components/olmap/BurstPipeAnalysis/AnalysisParameters.tsx @@ -189,8 +189,13 @@ const AnalysisParameters: React.FC = () => { async (event: { coordinate: number[] }) => { if (!map) return; const feature = await mapClickSelectFeatures(event, map); + const layer = feature?.getId()?.toString().split(".")[0]; + if (!feature) return; - if (feature.getGeometry()?.getType() === "Point") { + if ( + feature.getGeometry()?.getType() === "Point" || + (layer !== "geo_pipes_mat" && layer !== "geo_pipes") + ) { // 点类型几何不处理 open?.({ type: "error", diff --git a/src/components/olmap/ZonePropsPanel.tsx b/src/components/olmap/ZonePropsPanel.tsx index d54313f..ba79b74 100644 --- a/src/components/olmap/ZonePropsPanel.tsx +++ b/src/components/olmap/ZonePropsPanel.tsx @@ -45,6 +45,10 @@ const ZonePropsPanel: React.FC = ({ } }); if (clickedFeature) { + const layer = clickedFeature?.getId()?.toString().split(".")[0]; + if (layer !== "network_zone") { + return; + } setHighlightedFeature(clickedFeature); setProps(clickedFeature.getProperties()); // 更新高亮图层 diff --git a/src/utils/mapQueryService.ts b/src/utils/mapQueryService.ts index 76752d8..4768128 100644 --- a/src/utils/mapQueryService.ts +++ b/src/utils/mapQueryService.ts @@ -17,6 +17,7 @@ import RenderFeature from "ol/render/Feature"; import Map from "ol/Map"; import WebGLVectorTileLayer from "ol/layer/WebGLVectorTile"; import VectorTileSource from "ol/source/VectorTile"; +import VectorLayer from "ol/layer/Vector"; // ========== 类型定义 ========== @@ -389,55 +390,71 @@ const handleMapClickSelectFeatures = async ( // 获取缩放级别并确保为整数 let z = Math.floor(view.getZoom() || 0) - 1; - // 获取所有 VectorTileSource - const vectorTileSources = getVectorTileSources(map); - if (!vectorTileSources.length) { - return null; - } - // 存储所有选中的要素 const allSelectedFeatures: Feature[] = []; + let isFromVectorLayer = false; - // 遍历所有 VectorTileSource - for (const vectorTileSource of vectorTileSources) { - const tileGrid = vectorTileSource.getTileGrid(); - if (!tileGrid) { - continue; + // 1. 优先处理 VectorLayer - 使用 forEachFeatureAtPixel + const pixel = map.getPixelFromCoordinate(coord); + map.forEachFeatureAtPixel( + pixel, + (feature) => { + // 只处理标准 Feature,排除 RenderFeature + // 使用更宽松的检查:检查是否有 getGeometry 方法且不是 RenderFeature + if (feature && !(feature instanceof RenderFeature)) { + allSelectedFeatures.push(feature as Feature); + isFromVectorLayer = true; + } else return false; // 继续遍历所有要素 + }, + { + hitTolerance: MAP_CONFIG.hitTolerance, + layerFilter: (layer) => layer instanceof VectorLayer, } + ); - // 调整缩放级别到有效范围 - const minZoom = tileGrid.getMinZoom(); - const maxZoom = tileGrid.getMaxZoom(); - z = clampZoomLevel(z, minZoom, maxZoom); + // 2. 如果 VectorLayer 中没有找到要素,再处理 VectorTileSource + if (allSelectedFeatures.length === 0) { + const vectorTileSources = getVectorTileSources(map); + for (const vectorTileSource of vectorTileSources) { + const tileGrid = vectorTileSource.getTileGrid(); + if (!tileGrid) { + continue; + } - // 获取瓦片坐标 - const tileCoord = tileGrid.getTileCoordForCoordAndZ(coord, z); - const resolution = tileGrid.getResolution(tileCoord[0]); + // 调整缩放级别到有效范围 + const minZoom = tileGrid.getMinZoom(); + const maxZoom = tileGrid.getMaxZoom(); + z = clampZoomLevel(z, minZoom, maxZoom); - // 创建点击点的缓冲区 - const hitPoint = point(toLonLat(coord)); - const buffered = buffer(hitPoint, resolution * MAP_CONFIG.hitTolerance, { - units: MAP_CONFIG.bufferUnits, - }); + // 获取瓦片坐标 + const tileCoord = tileGrid.getTileCoordForCoordAndZ(coord, z); + const resolution = tileGrid.getResolution(tileCoord[0]); - // 获取矢量瓦片 - const vectorRenderTile = vectorTileSource.getTile( - tileCoord[0], - tileCoord[1], - tileCoord[2], - pixelRatio, - projection - ); + // 创建点击点的缓冲区 + const hitPoint = point(toLonLat(coord)); + const buffered = buffer(hitPoint, resolution * MAP_CONFIG.hitTolerance, { + units: MAP_CONFIG.bufferUnits, + }); - const vectorTiles = vectorTileSource.getSourceTiles( - pixelRatio, - projection, - vectorRenderTile - ); + // 获取矢量瓦片 + const vectorRenderTile = vectorTileSource.getTile( + tileCoord[0], + tileCoord[1], + tileCoord[2], + pixelRatio, + projection + ); - // 提取选中的要素 - const selectedFeatures = extractSelectedFeatures(vectorTiles, buffered); - allSelectedFeatures.push(...selectedFeatures); + const vectorTiles = vectorTileSource.getSourceTiles( + pixelRatio, + projection, + vectorRenderTile + ); + + // 提取选中的要素 + const selectedFeatures = extractSelectedFeatures(vectorTiles, buffered); + allSelectedFeatures.push(...selectedFeatures); + } } // 按几何类型优先级排序:点 > 线 > 其他 @@ -445,12 +462,18 @@ const handleMapClickSelectFeatures = async ( classifyFeaturesByGeometry(allSelectedFeatures); const prioritizedFeatures = [...points, ...lines, ...others]; - // 获取第一个要素的 ID 并查询完整信息 + // 获取第一个要素 const firstFeature = prioritizedFeatures[0]; if (!firstFeature) { return null; } + // 如果要素来自 VectorLayer,直接返回,不需要通过 WFS 查询 + if (isFromVectorLayer) { + return firstFeature; + } + + // 如果要素来自 VectorTileSource,需要通过 WFS 查询完整信息 const queryId = firstFeature.getProperties().id; if (!queryId) { return null;