完善爆管分析面板;整合地图查询函数
This commit is contained in:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user