feat: initialize TJWater WebGIS frontend

This commit is contained in:
2026-07-21 18:31:08 +08:00
commit ad5e5d3133
191 changed files with 37963 additions and 0 deletions
@@ -0,0 +1,114 @@
import type { Map as MapLibreMap } from "maplibre-gl";
import type { BaseLayerOption } from "@/features/map/core/components/base-layers-control";
import type { MapLayerControlItem } from "@/features/map/core/components/layer-control";
import type { MapLegendItem } from "@/features/map/core/components/legend";
import { GEOSERVER_WORKSPACE } from "../../../lib/config";
import { MAP_STYLE_TOKENS } from "./map-colors";
import {
AVAILABLE_WATER_NETWORK_SOURCE_IDS,
SUPPLY_LAYER_CATALOG,
type WaterNetworkSourceId
} from "./sources";
import { SCADA_ICON_PATHS } from "./scada";
import { SUPPLY_ASSET_ICON_PATHS } from "./supply-icons";
export const WORKBENCH_LAYER_GROUPS: Record<string, string[]> = {
simulation: [
"simulation-impact-fill",
"simulation-impact-outline",
"simulation-burst-halo",
"simulation-burst-point",
"simulation-pipe-label",
"simulation-impact-label",
"simulation-district-label",
"simulation-burst-label"
]
};
export const INITIAL_LAYER_VISIBILITY: Record<string, boolean> = {
...Object.fromEntries(AVAILABLE_WATER_NETWORK_SOURCE_IDS.map((sourceId) => [sourceId, true])),
simulation: false
};
const SOURCE_CONTROL_LABELS = Object.fromEntries(
SUPPLY_LAYER_CATALOG
.filter((layer) => layer.available)
.map((layer) => [
layer.id,
{
label: layer.label,
description: `${GEOSERVER_WORKSPACE}:${layer.sourceLayer}`
}
])
) as Record<WaterNetworkSourceId, { label: string; description: string }>;
export const MAP_LEGEND_ITEMS: MapLegendItem[] = [
{ id: "major-pipe", label: "DN600 及以上管线", color: MAP_STYLE_TOKENS.supply.pipeMajor, shape: "line" },
{ id: "medium-pipe", label: "DN300-DN599 管线", color: MAP_STYLE_TOKENS.supply.pipeMedium, shape: "line" },
{ id: "minor-pipe", label: "DN300 以下管线", color: MAP_STYLE_TOKENS.supply.pipeMinor, shape: "line" },
{ id: "inactive-pipe", label: "停运管线", color: MAP_STYLE_TOKENS.state.inactive, shape: "dashed-line" },
{ id: "junction", label: "节点", color: MAP_STYLE_TOKENS.supply.junction, shape: "dot" },
{ id: "valve", label: "阀门", color: MAP_STYLE_TOKENS.supply.valve, imageSrc: SUPPLY_ASSET_ICON_PATHS.valve },
{ id: "reservoir", label: "水库", color: MAP_STYLE_TOKENS.supply.reservoir, imageSrc: SUPPLY_ASSET_ICON_PATHS.reservoir },
{ id: "scada-flow", label: "SCADA 流量", color: MAP_STYLE_TOKENS.supply.scadaFlow, imageSrc: SCADA_ICON_PATHS.flow },
{ id: "scada-pressure", label: "SCADA 压力", color: MAP_STYLE_TOKENS.supply.scadaPressure, imageSrc: SCADA_ICON_PATHS.pressure },
{ id: "impact-area", label: "影响范围", color: MAP_STYLE_TOKENS.state.incident, shape: "square" }
];
export const BASE_LAYER_IDS = ["mapbox-light", "mapbox-satellite"] as const;
export const BASE_LAYER_OPTIONS: BaseLayerOption[] = [
{
id: "mapbox-light",
label: "浅色",
description: "道路底图",
swatchClassName: "bg-gradient-to-br from-slate-50 via-slate-100 to-blue-100"
},
{
id: "mapbox-satellite",
label: "影像",
description: "卫星街道",
swatchClassName: "bg-gradient-to-br from-emerald-950 via-emerald-700 to-yellow-200"
},
{
id: "none",
label: "无底图",
description: "仅业务图层",
swatchClassName: "bg-slate-100"
}
];
export function createLayerControlItems(layerVisibility: Record<string, boolean>): MapLayerControlItem[] {
return [
...AVAILABLE_WATER_NETWORK_SOURCE_IDS.map((sourceId) => ({
id: sourceId,
...SOURCE_CONTROL_LABELS[sourceId],
visible: layerVisibility[sourceId]
})),
{
id: "simulation",
label: "模拟结果",
description: "影响范围与事故标注",
visible: layerVisibility.simulation
}
];
}
export function getWorkbenchLayerIds(
map: Pick<MapLibreMap, "getLayer" | "getStyle">,
groupId: string
) {
if (isWaterNetworkSourceId(groupId)) {
return map
.getStyle()
.layers.filter((layer) => "source" in layer && layer.source === groupId)
.map((layer) => layer.id);
}
const layerIds = WORKBENCH_LAYER_GROUPS[groupId] ?? [];
return layerIds.filter((layerId) => map.getLayer(layerId));
}
function isWaterNetworkSourceId(value: string): value is WaterNetworkSourceId {
return (AVAILABLE_WATER_NETWORK_SOURCE_IDS as readonly string[]).includes(value);
}