277 lines
7.1 KiB
TypeScript
277 lines
7.1 KiB
TypeScript
import { config } from "@/config/config";
|
|
import { along, lineString, length, toMercator } from "@turf/turf";
|
|
import type { FeatureLike } from "ol/Feature";
|
|
import MVT from "ol/format/MVT";
|
|
import { Point } from "ol/geom";
|
|
import type BaseLayer from "ol/layer/Base";
|
|
import VectorLayer from "ol/layer/Vector";
|
|
import WebGLVectorTileLayer from "ol/layer/WebGLVectorTile";
|
|
import { toLonLat } from "ol/proj";
|
|
import GeoJSON from "ol/format/GeoJSON";
|
|
import VectorSource from "ol/source/Vector";
|
|
import VectorTileSource from "ol/source/VectorTile";
|
|
import { Icon, Style } from "ol/style";
|
|
import type { FlatStyleLike } from "ol/style/flat";
|
|
|
|
import { markMapResourcePersistent } from "./mapLifecycle";
|
|
|
|
type MapExtent = [number, number, number, number];
|
|
|
|
type CreateOperationalMapSourcesOptions = {
|
|
mapUrl: string;
|
|
workspace: string;
|
|
};
|
|
|
|
type CreateOperationalLayersOptions = CreateOperationalMapSourcesOptions & {
|
|
extent: MapExtent;
|
|
persistent?: boolean;
|
|
sources?: OperationalMapSources;
|
|
};
|
|
|
|
export type OperationalMapSources = {
|
|
junctions: VectorTileSource;
|
|
pipes: VectorTileSource;
|
|
valves: VectorTileSource;
|
|
reservoirs: VectorSource;
|
|
pumps: VectorSource;
|
|
tanks: VectorSource;
|
|
scada: VectorSource;
|
|
};
|
|
|
|
const defaultFlatStyle = config.MAP_DEFAULT_STYLE as FlatStyleLike;
|
|
const valveStyle = {
|
|
"icon-src": "/icons/valve.svg",
|
|
"icon-scale": 0.1,
|
|
};
|
|
|
|
const createIconStyle = (src: string, scale = 0.1) =>
|
|
new Style({ image: new Icon({ src, scale, anchor: [0.5, 0.5] }) });
|
|
|
|
const scadaStyle = (feature: FeatureLike) =>
|
|
createIconStyle(
|
|
feature.get("type") === "pipe_flow"
|
|
? "/icons/scada_flow.svg"
|
|
: "/icons/scada_pressure.svg",
|
|
);
|
|
|
|
const pumpStyle = (feature: FeatureLike) => {
|
|
const geometry = feature.getGeometry();
|
|
if (!geometry || geometry.getType() !== "LineString") return [];
|
|
|
|
const coordinates = (geometry as any)
|
|
.getCoordinates()
|
|
.map((coordinate: number[]) => toLonLat(coordinate));
|
|
if (coordinates.length < 2) return [];
|
|
|
|
const featureLine = lineString(coordinates);
|
|
const midpoint = along(featureLine, length(featureLine) / 2).geometry
|
|
.coordinates;
|
|
return [
|
|
new Style({
|
|
geometry: new Point(toMercator(midpoint)),
|
|
image: new Icon({
|
|
src: "/icons/pump.svg",
|
|
scale: 0.12,
|
|
anchor: [0.5, 0.5],
|
|
}),
|
|
}),
|
|
];
|
|
};
|
|
|
|
const pointProperties = [
|
|
{ name: "高程", value: "elevation" },
|
|
{ name: "实际需水量", value: "actual_demand" },
|
|
{ name: "水头", value: "total_head" },
|
|
{ name: "压力", value: "pressure" },
|
|
{ name: "水质", value: "quality" },
|
|
];
|
|
|
|
const pipeProperties = [
|
|
{ name: "管径", value: "diameter" },
|
|
{ name: "流量", value: "flow" },
|
|
{ name: "摩阻系数", value: "friction" },
|
|
{ name: "水头损失", value: "headloss" },
|
|
{ name: "单位水头损失", value: "unit_headloss" },
|
|
{ name: "水质", value: "quality" },
|
|
{ name: "反应速率", value: "reaction" },
|
|
{ name: "设置值", value: "setting" },
|
|
{ name: "状态", value: "status" },
|
|
{ name: "流速", value: "velocity" },
|
|
];
|
|
|
|
export const createOperationalMapSources = ({
|
|
mapUrl,
|
|
workspace,
|
|
}: CreateOperationalMapSourcesOptions): OperationalMapSources => {
|
|
const vectorTileUrl = (name: string) =>
|
|
`${mapUrl}/gwc/service/tms/1.0.0/${workspace}:${name}@WebMercatorQuad@pbf/{z}/{x}/{-y}.pbf`;
|
|
const vectorUrl = (name: string) =>
|
|
`${mapUrl}/${workspace}/ows?service=WFS&version=1.0.0&request=GetFeature&typeName=${workspace}:${name}&outputFormat=application/json`;
|
|
|
|
return {
|
|
junctions: new VectorTileSource({
|
|
url: vectorTileUrl("geo_junctions"),
|
|
format: new MVT(),
|
|
projection: "EPSG:3857",
|
|
}),
|
|
pipes: new VectorTileSource({
|
|
url: vectorTileUrl("geo_pipes"),
|
|
format: new MVT(),
|
|
projection: "EPSG:3857",
|
|
}),
|
|
valves: new VectorTileSource({
|
|
url: vectorTileUrl("geo_valves"),
|
|
format: new MVT(),
|
|
projection: "EPSG:3857",
|
|
}),
|
|
reservoirs: new VectorSource({
|
|
url: vectorUrl("geo_reservoirs"),
|
|
format: new GeoJSON(),
|
|
}),
|
|
pumps: new VectorSource({
|
|
url: vectorUrl("geo_pumps"),
|
|
format: new GeoJSON(),
|
|
}),
|
|
tanks: new VectorSource({
|
|
url: vectorUrl("geo_tanks"),
|
|
format: new GeoJSON(),
|
|
}),
|
|
scada: new VectorSource({
|
|
url: vectorUrl("geo_scada"),
|
|
format: new GeoJSON(),
|
|
}),
|
|
};
|
|
};
|
|
|
|
export const createOperationalMapResources = ({
|
|
mapUrl,
|
|
workspace,
|
|
extent,
|
|
persistent = false,
|
|
sources = createOperationalMapSources({ mapUrl, workspace }),
|
|
}: CreateOperationalLayersOptions) => {
|
|
|
|
const layers = {
|
|
junctions: new WebGLVectorTileLayer({
|
|
source: sources.junctions as any,
|
|
style: defaultFlatStyle,
|
|
extent,
|
|
maxZoom: 24,
|
|
minZoom: 11,
|
|
properties: {
|
|
name: "节点",
|
|
value: "junctions",
|
|
type: "point",
|
|
properties: pointProperties,
|
|
},
|
|
}),
|
|
pipes: new WebGLVectorTileLayer({
|
|
source: sources.pipes as any,
|
|
style: defaultFlatStyle,
|
|
extent,
|
|
maxZoom: 24,
|
|
minZoom: 11,
|
|
properties: {
|
|
name: "管道",
|
|
value: "pipes",
|
|
type: "linestring",
|
|
properties: pipeProperties,
|
|
},
|
|
}),
|
|
valves: new WebGLVectorTileLayer({
|
|
source: sources.valves as any,
|
|
style: valveStyle,
|
|
extent,
|
|
maxZoom: 24,
|
|
minZoom: 16,
|
|
properties: {
|
|
name: "阀门",
|
|
value: "valves",
|
|
type: "linestring",
|
|
properties: [],
|
|
},
|
|
}),
|
|
reservoirs: new VectorLayer({
|
|
source: sources.reservoirs,
|
|
style: () => createIconStyle("/icons/reservior.svg"),
|
|
extent,
|
|
maxZoom: 24,
|
|
minZoom: 11,
|
|
properties: {
|
|
name: "水库",
|
|
value: "reservoirs",
|
|
type: "point",
|
|
properties: [],
|
|
},
|
|
}),
|
|
pumps: new VectorLayer({
|
|
source: sources.pumps,
|
|
style: pumpStyle,
|
|
extent,
|
|
maxZoom: 24,
|
|
minZoom: 11,
|
|
properties: {
|
|
name: "水泵",
|
|
value: "pumps",
|
|
type: "linestring",
|
|
properties: [],
|
|
},
|
|
}),
|
|
tanks: new VectorLayer({
|
|
source: sources.tanks,
|
|
style: () => createIconStyle("/icons/tank.svg"),
|
|
extent,
|
|
maxZoom: 24,
|
|
minZoom: 11,
|
|
properties: {
|
|
name: "水箱",
|
|
value: "tanks",
|
|
type: "point",
|
|
properties: [],
|
|
},
|
|
}),
|
|
scada: new VectorLayer({
|
|
source: sources.scada,
|
|
style: scadaStyle,
|
|
extent,
|
|
maxZoom: 24,
|
|
minZoom: 11,
|
|
properties: {
|
|
name: "SCADA",
|
|
value: "scada",
|
|
type: "point",
|
|
properties: [],
|
|
},
|
|
}),
|
|
};
|
|
|
|
if (persistent) {
|
|
Object.values(layers).forEach(markMapResourcePersistent);
|
|
}
|
|
|
|
const layerById = layers as Record<string, BaseLayer>;
|
|
const enabledLayers = new Set(config.MAP_AVAILABLE_LAYERS);
|
|
const orderedLayers = [
|
|
"pipes",
|
|
"tanks",
|
|
"pumps",
|
|
"reservoirs",
|
|
"scada",
|
|
"junctions",
|
|
"valves",
|
|
]
|
|
.filter((id) => enabledLayers.has(id))
|
|
.map((id) => layerById[id]);
|
|
|
|
return {
|
|
sources,
|
|
layers,
|
|
orderedLayers,
|
|
resetStyles: () => {
|
|
layers.junctions.setStyle(defaultFlatStyle);
|
|
layers.pipes.setStyle(defaultFlatStyle);
|
|
layers.valves.setStyle(valveStyle);
|
|
},
|
|
};
|
|
};
|