diff --git a/public/icons/pump.svg b/public/icons/pump.svg
index 66319cf..98b55c9 100644
--- a/public/icons/pump.svg
+++ b/public/icons/pump.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/public/icons/reservior.svg b/public/icons/reservior.svg
new file mode 100644
index 0000000..3962a95
--- /dev/null
+++ b/public/icons/reservior.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/icons/reservoirs.svg b/public/icons/reservoirs.svg
deleted file mode 100644
index d5ed17f..0000000
--- a/public/icons/reservoirs.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/public/icons/scada_flow.svg b/public/icons/scada_flow.svg
index 75baf92..ea7cda3 100644
--- a/public/icons/scada_flow.svg
+++ b/public/icons/scada_flow.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/public/icons/scada_pressure.svg b/public/icons/scada_pressure.svg
index 3adb45a..0ac4d80 100644
--- a/public/icons/scada_pressure.svg
+++ b/public/icons/scada_pressure.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/public/icons/tank.svg b/public/icons/tank.svg
index b9562af..751c98a 100644
--- a/public/icons/tank.svg
+++ b/public/icons/tank.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/public/icons/valve.svg b/public/icons/valve.svg
new file mode 100644
index 0000000..84c3055
--- /dev/null
+++ b/public/icons/valve.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/app/OlMap/MapComponent.tsx b/src/app/OlMap/MapComponent.tsx
index 539c84b..2e294fb 100644
--- a/src/app/OlMap/MapComponent.tsx
+++ b/src/app/OlMap/MapComponent.tsx
@@ -18,7 +18,7 @@ import WebGLVectorTileLayer from "ol/layer/WebGLVectorTile";
import MVT from "ol/format/MVT";
import { FlatStyleLike } from "ol/style/flat";
import { toLonLat } from "ol/proj";
-import { along, bearing, lineString, length } from "@turf/turf";
+import { along, bearing, lineString, length, toMercator } from "@turf/turf";
import { Deck } from "@deck.gl/core";
import { TextLayer } from "@deck.gl/layers";
import { TripsLayer } from "@deck.gl/geo-layers";
@@ -26,7 +26,9 @@ import { CollisionFilterExtension } from "@deck.gl/extensions";
import VectorSource from "ol/source/Vector";
import GeoJson from "ol/format/GeoJSON";
import VectorLayer from "ol/layer/Vector";
-import { Style, Icon } from "ol/style";
+import { Icon, Style } from "ol/style.js";
+import { FeatureLike } from "ol/Feature";
+import { Point } from "ol/geom";
interface MapComponentProps {
children?: React.ReactNode;
@@ -189,6 +191,52 @@ const MapComponent: React.FC = ({ children }) => {
}),
});
};
+ // 定义 reservoirs 图层的样式函数,使用固定图标
+ const reservoirsStyle = () => {
+ const reserviorIcon = "/icons/reservior.svg";
+ return new Style({
+ image: new Icon({
+ src: reserviorIcon,
+ scale: 0.1, // 根据需要调整图标大小
+ anchor: [0.5, 0.5], // 图标锚点居中
+ }),
+ });
+ };
+ // 定义 valves 图层的样式函数,使用固定图标
+ const valvesStyle = function (feature: FeatureLike) {
+ const styles = [];
+ const valveIcon = "/icons/valve.svg";
+
+ const geometry = feature.getGeometry();
+ const lineCoords =
+ geometry?.getType() === "LineString"
+ ? (geometry as any).getCoordinates()
+ : null;
+ if (geometry) {
+ const lineCoordsWGS84 = lineCoords.map((coord: []) => {
+ const [lon, lat] = toLonLat(coord);
+ return [lon, lat];
+ });
+ // 计算中点
+ const lineStringFeature = lineString(lineCoordsWGS84);
+ const lineLength = length(lineStringFeature);
+ const midPoint = along(lineStringFeature, lineLength / 2).geometry
+ .coordinates;
+ // 在中点添加 icon 样式
+ const midPointMercator = toMercator(midPoint);
+ styles.push(
+ new Style({
+ geometry: new Point(midPointMercator),
+ image: new Icon({
+ src: valveIcon,
+ scale: 0.12,
+ anchor: [0.5, 0.5],
+ }),
+ })
+ );
+ }
+ return styles;
+ };
// 矢量瓦片数据源和图层
const junctionSource = new VectorTileSource({
url: `${mapUrl}/gwc/service/tms/1.0.0/TJWater:geo_junctions_mat@WebMercatorQuad@pbf/{z}/{x}/{-y}.pbf`, // 替换为你的 MVT 瓦片服务 URL
@@ -204,6 +252,14 @@ const MapComponent: React.FC = ({ children }) => {
url: `${mapUrl}/TJWater/ows?service=WFS&version=1.0.0&request=GetFeature&typeName=TJWater:geo_scada&outputFormat=application/json`,
format: new GeoJson(),
});
+ const reservoirsSource = new VectorSource({
+ url: `${mapUrl}/TJWater/ows?service=WFS&version=1.0.0&request=GetFeature&typeName=TJWater:geo_reservoirs&outputFormat=application/json`,
+ format: new GeoJson(),
+ });
+ const valvesSource = new VectorSource({
+ url: `${mapUrl}/TJWater/ows?service=WFS&version=1.0.0&request=GetFeature&typeName=TJWater:geo_valves&outputFormat=application/json`,
+ format: new GeoJson(),
+ });
// WebGL 渲染优化显示
const junctionLayer = new WebGLVectorTileLayer({
source: junctionSource as any, // 使用 WebGL 渲染
@@ -212,7 +268,7 @@ const MapComponent: React.FC = ({ children }) => {
maxZoom: 24,
minZoom: 12,
properties: {
- name: "节点图层", // 设置图层名称
+ name: "节点", // 设置图层名称
value: "junctions",
type: "point",
properties: [
@@ -232,7 +288,7 @@ const MapComponent: React.FC = ({ children }) => {
maxZoom: 24,
minZoom: 12,
properties: {
- name: "管道图层", // 设置图层名称
+ name: "管道", // 设置图层名称
value: "pipes",
type: "linestring",
properties: [
@@ -263,6 +319,32 @@ const MapComponent: React.FC = ({ children }) => {
properties: [],
},
});
+ const reservoirsLayer = new VectorLayer({
+ source: reservoirsSource,
+ style: reservoirsStyle,
+ extent: extent, // 设置图层范围
+ maxZoom: 24,
+ minZoom: 12,
+ properties: {
+ name: "水库", // 设置图层名称
+ value: "reservoirs",
+ type: "point",
+ properties: [],
+ },
+ });
+ const valvesLayer = new VectorLayer({
+ source: valvesSource,
+ style: valvesStyle,
+ extent: extent, // 设置图层范围
+ maxZoom: 24,
+ minZoom: 12,
+ properties: {
+ name: "阀门", // 设置图层名称
+ value: "valves",
+ type: "linestring",
+ properties: [],
+ },
+ });
useEffect(() => {
if (!mapRef.current) return;
// 缓存 junction、pipe 数据,提供给 deck.gl 显示标签使用
@@ -396,10 +478,17 @@ const MapComponent: React.FC = ({ children }) => {
const map = new OlMap({
target: mapRef.current,
view: new View({
+ maxZoom: 24,
projection: "EPSG:3857",
}),
// 图层依面、线、点、标注次序添加
- layers: [pipeLayer, junctionLayer, scadaLayer],
+ layers: [
+ pipeLayer,
+ junctionLayer,
+ valvesLayer,
+ scadaLayer,
+ reservoirsLayer,
+ ],
controls: [],
});
setMap(map);
diff --git a/src/components/olmap/SCADADeviceList.tsx b/src/components/olmap/SCADADeviceList.tsx
index 4e7a533..e099b5a 100644
--- a/src/components/olmap/SCADADeviceList.tsx
+++ b/src/components/olmap/SCADADeviceList.tsx
@@ -44,12 +44,25 @@ import { useMap } from "@app/OlMap/MapComponent";
import { GeoJSON } from "ol/format";
import { Point } from "ol/geom";
import config from "@/config/config";
+
+const STATUS_OPTIONS: {
+ value: "online" | "offline" | "warning" | "error";
+ name: "在线" | "离线" | "警告" | "错误";
+}[] = [
+ { value: "online", name: "在线" },
+ { value: "offline", name: "离线" },
+ { value: "warning", name: "警告" },
+ { value: "error", name: "错误" },
+];
interface SCADADevice {
id: string;
name: string;
type: string;
coordinates: [number, number];
- status: "在线" | "离线" | "警告" | "错误";
+ status: {
+ value: "online" | "offline" | "warning" | "error";
+ name: "在线" | "离线" | "警告" | "错误";
+ };
properties?: Record;
}
@@ -127,9 +140,7 @@ const SCADADeviceList: React.FC = ({
id: feature.get("id") || feature.getId(),
name: feature.get("id") || feature.getId(),
type: feature.get("type") === "pipe_flow" ? "流量" : "压力",
- status: ["在线", "离线", "警告", "错误"][
- Math.floor(Math.random() * 4)
- ] as "在线" | "离线" | "警告" | "错误",
+ status: STATUS_OPTIONS[Math.floor(Math.random() * 4)],
coordinates: (feature.getGeometry() as Point)?.getCoordinates() as [
number,
number
@@ -137,7 +148,6 @@ const SCADADeviceList: React.FC = ({
properties: feature.getProperties(),
}));
setInternalDevices(data);
- console.log("Fetched SCADA devices:", data);
} catch (error) {
console.error("Error fetching SCADA devices:", error);
} finally {
@@ -158,12 +168,7 @@ const SCADADeviceList: React.FC = ({
}, [effectiveDevices]);
// 获取设备状态列表
- const deviceStatuses = useMemo(() => {
- const statuses = Array.from(
- new Set(effectiveDevices.map((device) => device.status))
- );
- return statuses.sort();
- }, [effectiveDevices]);
+ const deviceStatuses = STATUS_OPTIONS;
// 创建设备索引 Map,使用设备 ID 作为键
const deviceIndex = useMemo(() => {
@@ -176,29 +181,20 @@ const SCADADeviceList: React.FC = ({
// 过滤设备列表
const filteredDevices = useMemo(() => {
- if (
- searchQuery === "" &&
- selectedType === "all" &&
- selectedStatus === "all"
- ) {
- return effectiveDevices;
- }
-
- const searchLower = searchQuery.toLowerCase();
return effectiveDevices.filter((device) => {
- if (searchQuery === "") return true;
-
+ const searchLower = searchQuery.toLowerCase();
const nameLower = device.name.toLowerCase();
const idLower = device.id.toLowerCase();
const matchesSearch =
+ searchQuery === "" ||
nameLower.indexOf(searchLower) !== -1 ||
idLower.indexOf(searchLower) !== -1;
const matchesType =
selectedType === "all" || device.type === selectedType;
const matchesStatus =
- selectedStatus === "all" || device.status === selectedStatus;
+ selectedStatus === "all" || device.status.value === selectedStatus;
return matchesSearch && matchesType && matchesStatus;
});
@@ -396,8 +392,8 @@ const SCADADeviceList: React.FC = ({
>
{deviceStatuses.map((status) => (
-