新增多个图层;更新图层图标;修复SCADA设备列表状态功能
This commit is contained in:
@@ -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<MapComponentProps> = ({ 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<MapComponentProps> = ({ 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<MapComponentProps> = ({ children }) => {
|
||||
maxZoom: 24,
|
||||
minZoom: 12,
|
||||
properties: {
|
||||
name: "节点图层", // 设置图层名称
|
||||
name: "节点", // 设置图层名称
|
||||
value: "junctions",
|
||||
type: "point",
|
||||
properties: [
|
||||
@@ -232,7 +288,7 @@ const MapComponent: React.FC<MapComponentProps> = ({ children }) => {
|
||||
maxZoom: 24,
|
||||
minZoom: 12,
|
||||
properties: {
|
||||
name: "管道图层", // 设置图层名称
|
||||
name: "管道", // 设置图层名称
|
||||
value: "pipes",
|
||||
type: "linestring",
|
||||
properties: [
|
||||
@@ -263,6 +319,32 @@ const MapComponent: React.FC<MapComponentProps> = ({ 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<MapComponentProps> = ({ 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);
|
||||
|
||||
@@ -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<string, any>;
|
||||
}
|
||||
|
||||
@@ -127,9 +140,7 @@ const SCADADeviceList: React.FC<SCADADeviceListProps> = ({
|
||||
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<SCADADeviceListProps> = ({
|
||||
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<SCADADeviceListProps> = ({
|
||||
}, [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<SCADADeviceListProps> = ({
|
||||
|
||||
// 过滤设备列表
|
||||
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<SCADADeviceListProps> = ({
|
||||
>
|
||||
<MenuItem value="all">全部状态</MenuItem>
|
||||
{deviceStatuses.map((status) => (
|
||||
<MenuItem key={status} value={status}>
|
||||
{status}
|
||||
<MenuItem key={status.value} value={status.value}>
|
||||
{status.name}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
@@ -486,12 +482,14 @@ const SCADADeviceList: React.FC<SCADADeviceListProps> = ({
|
||||
<Typography
|
||||
variant="caption"
|
||||
sx={{
|
||||
color: `${getStatusColor(device.status)}.main`,
|
||||
color: `${getStatusColor(
|
||||
device.status.value
|
||||
)}.main`,
|
||||
fontWeight: "bold",
|
||||
fontSize: 16,
|
||||
}}
|
||||
>
|
||||
{getStatusIcon(device.status)}
|
||||
{getStatusIcon(device.status.value)}
|
||||
</Typography>
|
||||
</ListItemIcon>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user