完善爆管分析面板;整合地图查询函数

This commit is contained in:
JIANG
2025-10-23 11:59:45 +08:00
parent 720f8a5dc2
commit ad893ac19d
7 changed files with 1185 additions and 899 deletions

View File

@@ -10,19 +10,11 @@ import DrawPanel from "./DrawPanel"; // 引入绘图面板组件
import VectorSource from "ol/source/Vector";
import VectorLayer from "ol/layer/Vector";
import { Style, Stroke, Fill, Circle } from "ol/style";
import { Geometry } from "ol/geom";
import { Point, LineString, Polygon } from "ol/geom";
import { FeatureLike } from "ol/Feature";
import Feature from "ol/Feature";
import GeoJSON from "ol/format/GeoJSON";
import StyleEditorPanel from "./StyleEditorPanel";
import WebGLVectorTileLayer from "ol/layer/WebGLVectorTile";
import VectorTileSource from "ol/source/VectorTile";
import TileState from "ol/TileState";
import { toLonLat } from "ol/proj";
import { booleanIntersects, buffer, point, toWgs84 } from "@turf/turf";
// import { handleMapClickSelectFeatures as mapClickSelectFeatures } from "@/utils/mapQueryService";
import RenderFeature from "ol/render/Feature";
import { handleMapClickSelectFeatures as mapClickSelectFeatures } from "@/utils/mapQueryService";
import { config } from "@/config/config";
const backendUrl = config.backendUrl;
@@ -98,245 +90,15 @@ const Toolbar: React.FC<ToolbarProps> = ({ hiddenButtons }) => {
source.addFeature(highlightFeature);
}
}, [highlightFeature]);
// 将 RenderFeature 转换为 Feature
const renderFeature2Feature = (renderFeature: RenderFeature) => {
if (renderFeature) {
const geometry = renderFeature.getGeometry();
if (geometry) {
try {
let clonedGeometry;
if (geometry instanceof Geometry) {
// 标准 Feature 的几何体
clonedGeometry = geometry;
} else {
// RenderFeature 或其他类型的几何体
const type = geometry.getType();
const flatCoordinates = geometry.getFlatCoordinates();
let coordinates: number[] | number[][] | number[][][];
switch (type) {
case "Point":
// Point: [x, y]
coordinates = [flatCoordinates[0], flatCoordinates[1]];
clonedGeometry = new Point(coordinates as number[]);
break;
case "LineString":
// LineString: [[x1, y1], [x2, y2], ...]
const lineCoords: number[][] = [];
for (let i = 0; i < flatCoordinates.length; i += 2) {
lineCoords.push([flatCoordinates[i], flatCoordinates[i + 1]]);
}
clonedGeometry = new LineString(lineCoords);
break;
case "Polygon":
// Polygon: [[[x1, y1], [x2, y2], ...]]
// 需要获取环的结束位置
const ends = (
geometry as { getEnds?: () => number[] }
).getEnds?.() || [flatCoordinates.length];
const rings: number[][][] = [];
let start = 0;
for (const end of ends) {
const ring: number[][] = [];
for (let i = start; i < end; i += 2) {
ring.push([flatCoordinates[i], flatCoordinates[i + 1]]);
}
rings.push(ring);
start = end;
}
clonedGeometry = new Polygon(rings);
break;
default:
console.log("不支持的几何体类型:", type);
return;
}
}
const feature = new Feature({
geometry: clonedGeometry,
...renderFeature.getProperties(),
});
return feature;
} catch (error) {
console.error("RenderFeature转换Feature时出错:", error);
}
}
}
};
// 根据 IDs通过 Geoserver WFS 服务查询要素
const queryFeaturesByIds = async (ids: string[], layer?: string) => {
if (!ids.length) return [];
const geoserverUrl = "http://127.0.0.1:8080/geoserver";
const network = "TJWater";
const layers = ["geo_pipes_mat", "geo_junctions_mat"];
const orFilter = ids.map((id) => `id=${id}`).join(" OR ");
try {
if (!layer) {
// 遍历所有图层
const promises = layers.map(async (layer) => {
try {
const url =
`${geoserverUrl}/${network}/ows?` +
`service=WFS&version=1.0.0&request=GetFeature&` +
`typeName=${network}:${layer}&outputFormat=application/json&` +
`CQL_FILTER=${encodeURIComponent(orFilter)}`;
const response = await fetch(url);
if (!response.ok) {
throw new Error(`请求失败: ${response.statusText}`);
}
return await response.json();
} catch (error) {
console.error(`图层 ${layer} 查询失败:`, error);
return null; // 返回 null 表示该图层查询失败
}
});
const results = await Promise.all(promises);
const features = results
.filter((json) => json !== null) // 过滤掉失败的请求
.flatMap((json) => new GeoJSON().readFeatures(json));
// console.log("查询到的要素:", features);
return features;
} else {
// 查询指定图层
const url =
`${geoserverUrl}/${network}/ows?` +
`service=WFS&version=1.0.0&request=GetFeature&` +
`typeName=${network}:${layer}&outputFormat=application/json&` +
`CQL_FILTER=${encodeURIComponent(orFilter)}`;
const response = await fetch(url);
if (!response.ok) {
throw new Error(`请求失败: ${response.statusText}`);
}
const json = await response.json();
const features = new GeoJSON().readFeatures(json);
// console.log("查询到的要素:", features);
return features;
}
} catch (error) {
console.error("根据 IDs 查询要素时出错:", error);
return [];
}
};
// 处理地图点击选择要素
// 地图点击选择要素事件处理函数
const handleMapClickSelectFeatures = useCallback(
(event: { coordinate: number[] }) => {
async (event: { coordinate: number[] }) => {
if (!map) return;
const coord = event.coordinate;
let z = Math.floor(map.getView().getZoom() || 0) - 1; // 确保 z 是整数
const projection = map.getView().getProjection(); // 获取地图的投影
const pixelRatio = window.devicePixelRatio; // 获取设备像素比率
const [x, y] = coord;
// 遍历所有的 VectorTileSources
const vectorTileSources = map
.getAllLayers()
.filter((layer) => layer instanceof WebGLVectorTileLayer)
.map((layer) => layer.getSource() as VectorTileSource)
.filter((source) => source);
if (!vectorTileSources.length) return;
// 按几何类型分类,优先处理级别
const points: any[] = [];
const lines: any[] = [];
const others: any[] = [];
vectorTileSources.forEach((vectorTileSource) => {
const tileGrid = vectorTileSource.getTileGrid();
if (tileGrid) {
const minZoom = tileGrid.getMinZoom(); // 最小缩放级别
const maxZoom = tileGrid.getMaxZoom(); // 最大缩放级别
// 确保 z 在有效范围内
if (z < minZoom) z = minZoom;
if (z > maxZoom) z = maxZoom;
} else {
return;
}
const tileCoord = tileGrid.getTileCoordForCoordAndZ([x, y], z);
// 设置 resolution 用于基于屏幕像素的 buffer 容差计算
const resolution = tileGrid.getResolution(tileCoord[0]);
const hitTolerance = 5; // 像素容差
const hitPoint = point(toLonLat(coord));
const buffered = buffer(hitPoint, resolution * hitTolerance, {
units: "meters",
});
// 获取 VectorRenderTile
const vectorRenderTile = vectorTileSource.getTile(
tileCoord[0],
tileCoord[1],
tileCoord[2],
pixelRatio,
projection
);
// 获取 SourceTiles
const vectorTiles = vectorTileSource.getSourceTiles(
pixelRatio,
projection,
vectorRenderTile
);
vectorTiles.forEach((vectorTile) => {
if (vectorTile.getState() === TileState.LOADED) {
const renderFeatures = vectorTile.getFeatures();
const selectedFeatures = renderFeatures
.map(
(renderFeature) =>
renderFeature2Feature(renderFeature) as Feature<any>
)
.filter((feature) => {
if (feature && buffered) {
const geoJSONGeometry = new GeoJSON().writeGeometryObject(
feature.getGeometry()
);
const bufferedGeometry = buffered.geometry;
return booleanIntersects(
toWgs84(geoJSONGeometry),
bufferedGeometry
);
}
return false;
});
selectedFeatures.forEach((selectedFeature) => {
const geometryType = selectedFeature.getGeometry()?.getType();
if (geometryType === "Point") {
points.push(selectedFeature);
} else if (geometryType === "LineString") {
lines.push(selectedFeature);
} else {
others.push(selectedFeature);
}
});
}
});
});
// 按优先级处理:点 > 线 > 其他
const selectedFeatures = [...points, ...lines, ...others];
const firstFeature = selectedFeatures[0] as Feature<any>;
const queryId = firstFeature?.getProperties().id;
// console.log(queryId, "queryId");
if (queryId) {
queryFeaturesByIds([queryId]).then((features) => {
// console.log("查询到的要素:", features);
setHighlightFeature(features[0]);
});
} else {
setHighlightFeature(null);
}
const feature = await mapClickSelectFeatures(event, map); // 调用导入的函数
setHighlightFeature(feature);
},
[map, highlightLayer, setHighlightFeature]
[map, setHighlightFeature]
);
// const handleMapClickSelectFeatures = useCallback(
// (event: { coordinate: number[] }) => {
// if (!map) return;
// mapClickSelectFeatures(event, map, setHighlightFeature); // 调用导入的函数
// },
// [map, setHighlightFeature]
// );
// 添加矢量属性查询事件监听器
useEffect(() => {
if (!activeTools.includes("info") || !map) return;