feat(map): refine SCADA feature experience

This commit is contained in:
2026-07-14 10:48:24 +08:00
parent 0b7e827024
commit 1c85d938a6
35 changed files with 1020 additions and 842 deletions
+84 -487
View File
@@ -1,493 +1,90 @@
import type { StyleSpecification } from "maplibre-gl";
import {
CLOSED_CONDUIT_COLOR,
DRAINAGE_SELECTED_OUTLINE_COLORS,
DRAINAGE_SOURCE_COLORS,
DRAINAGE_SOURCE_OPACITIES
} from "./map-colors";
import type { ExpressionSpecification, StyleSpecification } from "maplibre-gl";
import { DRAINAGE_LAYER_VISUALS } from "./map-layer-visuals";
import { MAP_STYLE_TOKENS } from "./map-colors";
import { SOURCE_LAYERS } from "./sources";
export { DRAINAGE_SOURCE_COLORS } from "./map-colors";
export const INTERACTIVE_HIT_LAYER_IDS = [
"conduits-hit",
"junctions-hit",
"orifices-hit",
"pumps-hit",
"outfalls-hit"
];
"conduits-hit", "junctions-hit", "orifices-hit", "pumps-hit", "outfalls-hit"
] as const;
export const waterNetworkLayers: StyleSpecification["layers"] = [
{
id: "pipes-casing",
type: "line",
source: "conduits",
"source-layer": SOURCE_LAYERS.conduits,
minzoom: DRAINAGE_LAYER_VISUALS.conduits.minzoom,
paint: {
"line-color": "rgba(255,255,255,0.86)",
"line-width": DRAINAGE_LAYER_VISUALS.conduits.casingWidth,
"line-opacity": 0.9
}
},
{
id: "pipes-flow",
type: "line",
source: "conduits",
"source-layer": SOURCE_LAYERS.conduits,
minzoom: DRAINAGE_LAYER_VISUALS.conduits.minzoom,
paint: {
"line-color": [
"case",
["==", ["get", "status"], "Closed"],
CLOSED_CONDUIT_COLOR,
DRAINAGE_SOURCE_COLORS.conduits
],
"line-width": DRAINAGE_LAYER_VISUALS.conduits.width,
"line-opacity": DRAINAGE_SOURCE_OPACITIES.conduits
}
},
{
id: "pipes-risk-glow",
type: "line",
source: "conduits",
"source-layer": SOURCE_LAYERS.conduits,
minzoom: 12,
filter: [">=", ["coalesce", ["get", "diameter"], 0], 600],
paint: {
"line-color": DRAINAGE_SOURCE_COLORS.conduits,
"line-width": DRAINAGE_LAYER_VISUALS.conduits.riskGlowWidth,
"line-blur": 2.5,
"line-opacity": 0.22
}
},
{
id: "pipes-hover-outline",
type: "line",
source: "conduits",
"source-layer": SOURCE_LAYERS.conduits,
minzoom: DRAINAGE_LAYER_VISUALS.conduits.minzoom,
filter: ["==", ["get", "id"], ""],
paint: {
"line-color": "#ffffff",
"line-width": DRAINAGE_LAYER_VISUALS.conduits.hoverOutlineWidth,
"line-opacity": 0.92
}
},
{
id: "pipes-hover",
type: "line",
source: "conduits",
"source-layer": SOURCE_LAYERS.conduits,
minzoom: DRAINAGE_LAYER_VISUALS.conduits.minzoom,
filter: ["==", ["get", "id"], ""],
paint: {
"line-color": DRAINAGE_SOURCE_COLORS.conduits,
"line-width": DRAINAGE_LAYER_VISUALS.conduits.hoverWidth,
"line-opacity": 1
}
},
{
id: "pipes-selected-halo",
type: "line",
source: "conduits",
"source-layer": SOURCE_LAYERS.conduits,
filter: ["==", ["get", "id"], ""],
paint: {
"line-color": "#ffffff",
"line-width": DRAINAGE_LAYER_VISUALS.conduits.selectedHaloWidth,
"line-opacity": 1
}
},
{
id: "pipes-selected-outline",
type: "line",
source: "conduits",
"source-layer": SOURCE_LAYERS.conduits,
filter: ["==", ["get", "id"], ""],
paint: {
"line-color": DRAINAGE_SELECTED_OUTLINE_COLORS.conduits,
"line-width": DRAINAGE_LAYER_VISUALS.conduits.selectedOutlineWidth,
"line-opacity": 1
}
},
{
id: "pipes-selected",
type: "line",
source: "conduits",
"source-layer": SOURCE_LAYERS.conduits,
filter: ["==", ["get", "id"], ""],
paint: {
"line-color": DRAINAGE_SOURCE_COLORS.conduits,
"line-width": DRAINAGE_LAYER_VISUALS.conduits.selectedWidth,
"line-opacity": 1
}
},
{
id: "junctions-halo",
type: "circle",
source: "junctions",
"source-layer": SOURCE_LAYERS.junctions,
minzoom: DRAINAGE_LAYER_VISUALS.junctions.minzoom,
paint: {
"circle-color": "#ffffff",
"circle-radius": DRAINAGE_LAYER_VISUALS.junctions.haloRadius,
"circle-opacity": 0.76,
"circle-stroke-color": "rgba(15,23,42,0.22)",
"circle-stroke-width": 1
}
},
{
id: "junctions",
type: "circle",
source: "junctions",
"source-layer": SOURCE_LAYERS.junctions,
minzoom: DRAINAGE_LAYER_VISUALS.junctions.minzoom,
paint: {
"circle-color": DRAINAGE_SOURCE_COLORS.junctions,
"circle-radius": DRAINAGE_LAYER_VISUALS.junctions.radius,
"circle-opacity": DRAINAGE_SOURCE_OPACITIES.junctions
}
},
{
id: "junctions-hover",
type: "circle",
source: "junctions",
"source-layer": SOURCE_LAYERS.junctions,
minzoom: DRAINAGE_LAYER_VISUALS.junctions.minzoom,
filter: ["==", ["get", "id"], ""],
paint: {
"circle-color": DRAINAGE_SOURCE_COLORS.junctions,
"circle-radius": DRAINAGE_LAYER_VISUALS.junctions.hoverRadius,
"circle-opacity": 0.24,
"circle-stroke-color": DRAINAGE_SOURCE_COLORS.junctions,
"circle-stroke-width": 2
}
},
{
id: "junctions-selected-halo",
type: "circle",
source: "junctions",
"source-layer": SOURCE_LAYERS.junctions,
filter: ["==", ["get", "id"], ""],
paint: {
"circle-color": "#ffffff",
"circle-radius": DRAINAGE_LAYER_VISUALS.junctions.selectedHaloRadius,
"circle-opacity": 0.92
}
},
{
id: "junctions-selected",
type: "circle",
source: "junctions",
"source-layer": SOURCE_LAYERS.junctions,
filter: ["==", ["get", "id"], ""],
paint: {
"circle-color": DRAINAGE_SOURCE_COLORS.junctions,
"circle-radius": DRAINAGE_LAYER_VISUALS.junctions.selectedRadius,
"circle-opacity": 1,
"circle-stroke-color": DRAINAGE_SELECTED_OUTLINE_COLORS.junctions,
"circle-stroke-width": 3
}
},
{
id: "orifices-casing",
type: "line",
source: "orifices",
"source-layer": SOURCE_LAYERS.orifices,
minzoom: DRAINAGE_LAYER_VISUALS.orifices.minzoom,
paint: {
"line-color": "#ffffff",
"line-width": DRAINAGE_LAYER_VISUALS.orifices.casingWidth,
"line-opacity": 0.9
}
},
{
id: "orifices",
type: "line",
source: "orifices",
"source-layer": SOURCE_LAYERS.orifices,
minzoom: DRAINAGE_LAYER_VISUALS.orifices.minzoom,
paint: {
"line-color": DRAINAGE_SOURCE_COLORS.orifices,
"line-width": DRAINAGE_LAYER_VISUALS.orifices.width,
"line-opacity": DRAINAGE_SOURCE_OPACITIES.orifices
}
},
{
id: "orifices-hover-outline",
type: "line",
source: "orifices",
"source-layer": SOURCE_LAYERS.orifices,
minzoom: DRAINAGE_LAYER_VISUALS.orifices.minzoom,
filter: ["==", ["get", "id"], ""],
paint: {
"line-color": "#ffffff",
"line-width": DRAINAGE_LAYER_VISUALS.orifices.hoverOutlineWidth,
"line-opacity": 0.92
}
},
{
id: "orifices-hover",
type: "line",
source: "orifices",
"source-layer": SOURCE_LAYERS.orifices,
minzoom: DRAINAGE_LAYER_VISUALS.orifices.minzoom,
filter: ["==", ["get", "id"], ""],
paint: {
"line-color": DRAINAGE_SOURCE_COLORS.orifices,
"line-width": DRAINAGE_LAYER_VISUALS.orifices.hoverWidth,
"line-opacity": 1
}
},
{
id: "orifices-selected-halo",
type: "line",
source: "orifices",
"source-layer": SOURCE_LAYERS.orifices,
filter: ["==", ["get", "id"], ""],
paint: {
"line-color": "#ffffff",
"line-width": DRAINAGE_LAYER_VISUALS.orifices.selectedHaloWidth,
"line-opacity": 1
}
},
{
id: "orifices-selected-outline",
type: "line",
source: "orifices",
"source-layer": SOURCE_LAYERS.orifices,
filter: ["==", ["get", "id"], ""],
paint: {
"line-color": DRAINAGE_SELECTED_OUTLINE_COLORS.orifices,
"line-width": DRAINAGE_LAYER_VISUALS.orifices.selectedOutlineWidth,
"line-opacity": 1
}
},
{
id: "orifices-selected",
type: "line",
source: "orifices",
"source-layer": SOURCE_LAYERS.orifices,
filter: ["==", ["get", "id"], ""],
paint: {
"line-color": DRAINAGE_SOURCE_COLORS.orifices,
"line-width": DRAINAGE_LAYER_VISUALS.orifices.selectedWidth,
"line-opacity": 1
}
},
{
id: "pumps-casing",
type: "line",
source: "pumps",
"source-layer": SOURCE_LAYERS.pumps,
minzoom: DRAINAGE_LAYER_VISUALS.pumps.minzoom,
paint: {
"line-color": "#ffffff",
"line-width": DRAINAGE_LAYER_VISUALS.pumps.casingWidth,
"line-opacity": 0.9
}
},
{
id: "pumps",
type: "line",
source: "pumps",
"source-layer": SOURCE_LAYERS.pumps,
minzoom: DRAINAGE_LAYER_VISUALS.pumps.minzoom,
paint: {
"line-color": DRAINAGE_SOURCE_COLORS.pumps,
"line-width": DRAINAGE_LAYER_VISUALS.pumps.width,
"line-opacity": DRAINAGE_SOURCE_OPACITIES.pumps
}
},
{
id: "pumps-hover-outline",
type: "line",
source: "pumps",
"source-layer": SOURCE_LAYERS.pumps,
minzoom: DRAINAGE_LAYER_VISUALS.pumps.minzoom,
filter: ["==", ["get", "id"], ""],
paint: {
"line-color": "#ffffff",
"line-width": DRAINAGE_LAYER_VISUALS.pumps.hoverOutlineWidth,
"line-opacity": 0.92
}
},
{
id: "pumps-hover",
type: "line",
source: "pumps",
"source-layer": SOURCE_LAYERS.pumps,
minzoom: DRAINAGE_LAYER_VISUALS.pumps.minzoom,
filter: ["==", ["get", "id"], ""],
paint: {
"line-color": DRAINAGE_SOURCE_COLORS.pumps,
"line-width": DRAINAGE_LAYER_VISUALS.pumps.hoverWidth,
"line-opacity": 1
}
},
{
id: "pumps-selected-halo",
type: "line",
source: "pumps",
"source-layer": SOURCE_LAYERS.pumps,
filter: ["==", ["get", "id"], ""],
paint: {
"line-color": "#ffffff",
"line-width": DRAINAGE_LAYER_VISUALS.pumps.selectedHaloWidth,
"line-opacity": 1
}
},
{
id: "pumps-selected-outline",
type: "line",
source: "pumps",
"source-layer": SOURCE_LAYERS.pumps,
filter: ["==", ["get", "id"], ""],
paint: {
"line-color": DRAINAGE_SELECTED_OUTLINE_COLORS.pumps,
"line-width": DRAINAGE_LAYER_VISUALS.pumps.selectedOutlineWidth,
"line-opacity": 1
}
},
{
id: "pumps-selected",
type: "line",
source: "pumps",
"source-layer": SOURCE_LAYERS.pumps,
filter: ["==", ["get", "id"], ""],
paint: {
"line-color": DRAINAGE_SOURCE_COLORS.pumps,
"line-width": DRAINAGE_LAYER_VISUALS.pumps.selectedWidth,
"line-opacity": 1
}
},
{
id: "outfalls-halo",
type: "circle",
source: "outfalls",
"source-layer": SOURCE_LAYERS.outfalls,
minzoom: DRAINAGE_LAYER_VISUALS.outfalls.minzoom,
paint: {
"circle-color": "#ffffff",
"circle-radius": DRAINAGE_LAYER_VISUALS.outfalls.haloRadius,
"circle-opacity": 0.9
}
},
{
id: "outfalls",
type: "circle",
source: "outfalls",
"source-layer": SOURCE_LAYERS.outfalls,
minzoom: DRAINAGE_LAYER_VISUALS.outfalls.minzoom,
paint: {
"circle-color": DRAINAGE_SOURCE_COLORS.outfalls,
"circle-radius": DRAINAGE_LAYER_VISUALS.outfalls.radius,
"circle-stroke-color": "#7F1D2D",
"circle-stroke-width": 1,
"circle-opacity": DRAINAGE_SOURCE_OPACITIES.outfalls
}
},
{
id: "outfalls-hover",
type: "circle",
source: "outfalls",
"source-layer": SOURCE_LAYERS.outfalls,
minzoom: DRAINAGE_LAYER_VISUALS.outfalls.minzoom,
filter: ["==", ["get", "id"], ""],
paint: {
"circle-color": DRAINAGE_SOURCE_COLORS.outfalls,
"circle-radius": DRAINAGE_LAYER_VISUALS.outfalls.hoverRadius,
"circle-opacity": 0.24,
"circle-stroke-color": DRAINAGE_SOURCE_COLORS.outfalls,
"circle-stroke-width": 2
}
},
{
id: "outfalls-selected-halo",
type: "circle",
source: "outfalls",
"source-layer": SOURCE_LAYERS.outfalls,
filter: ["==", ["get", "id"], ""],
paint: {
"circle-color": "#ffffff",
"circle-radius": DRAINAGE_LAYER_VISUALS.outfalls.selectedHaloRadius,
"circle-opacity": 0.92
}
},
{
id: "outfalls-selected",
type: "circle",
source: "outfalls",
"source-layer": SOURCE_LAYERS.outfalls,
filter: ["==", ["get", "id"], ""],
paint: {
"circle-color": DRAINAGE_SOURCE_COLORS.outfalls,
"circle-radius": DRAINAGE_LAYER_VISUALS.outfalls.selectedRadius,
"circle-opacity": 1,
"circle-stroke-color": DRAINAGE_SELECTED_OUTLINE_COLORS.outfalls,
"circle-stroke-width": 3
}
},
{
id: "conduits-hit",
type: "line",
source: "conduits",
"source-layer": SOURCE_LAYERS.conduits,
minzoom: DRAINAGE_LAYER_VISUALS.conduits.minzoom,
paint: {
"line-color": "#000000",
"line-width": DRAINAGE_LAYER_VISUALS.conduits.hitWidth,
"line-opacity": 0
}
},
{
id: "junctions-hit",
type: "circle",
source: "junctions",
"source-layer": SOURCE_LAYERS.junctions,
minzoom: DRAINAGE_LAYER_VISUALS.junctions.minzoom,
paint: {
"circle-color": "#000000",
"circle-radius": DRAINAGE_LAYER_VISUALS.junctions.hitRadius,
"circle-opacity": 0
}
},
{
id: "orifices-hit",
type: "line",
source: "orifices",
"source-layer": SOURCE_LAYERS.orifices,
minzoom: DRAINAGE_LAYER_VISUALS.orifices.minzoom,
paint: {
"line-color": "#000000",
"line-width": DRAINAGE_LAYER_VISUALS.orifices.hitWidth,
"line-opacity": 0
}
},
{
id: "pumps-hit",
type: "line",
source: "pumps",
"source-layer": SOURCE_LAYERS.pumps,
minzoom: DRAINAGE_LAYER_VISUALS.pumps.minzoom,
paint: {
"line-color": "#000000",
"line-width": DRAINAGE_LAYER_VISUALS.pumps.hitWidth,
"line-opacity": 0
}
},
{
id: "outfalls-hit",
type: "circle",
source: "outfalls",
"source-layer": SOURCE_LAYERS.outfalls,
minzoom: DRAINAGE_LAYER_VISUALS.outfalls.minzoom,
paint: {
"circle-color": "#000000",
"circle-radius": DRAINAGE_LAYER_VISUALS.outfalls.hitRadius,
"circle-opacity": 0
}
}
const hoveredOpacity: ExpressionSpecification = [
"case",
["all", ["boolean", ["feature-state", "hovered"], false], ["!", ["boolean", ["feature-state", "selected"], false]]],
1,
0
];
const selectedOpacity: ExpressionSpecification = [
"case", ["boolean", ["feature-state", "selected"], false], 1, 0
];
const stateColor = (sourceColor: string): ExpressionSpecification => [
"case",
["in", ["downcase", ["to-string", ["coalesce", ["get", "status"], ""]]], ["literal", ["closed", "off", "inactive"]]],
MAP_STYLE_TOKENS.state.inactive,
sourceColor
];
const opacityTransition = { duration: 120, delay: 0 } as const;
type Layer = NonNullable<StyleSpecification["layers"]>[number];
type AuthoredLayer = { id: string; type: string; [key: string]: unknown };
const layers: AuthoredLayer[] = [];
// Business sources: white casing, then category-colored core.
layers.push(
{ id: "pipes-casing", type: "line", source: "conduits", "source-layer": SOURCE_LAYERS.conduits, minzoom: DRAINAGE_LAYER_VISUALS.conduits.minzoom, paint: { "line-color": MAP_STYLE_TOKENS.canvas.casing, "line-width": DRAINAGE_LAYER_VISUALS.conduits.casingWidth, "line-opacity": MAP_STYLE_TOKENS.opacity.casing } },
{ id: "pipes-flow", type: "line", source: "conduits", "source-layer": SOURCE_LAYERS.conduits, minzoom: DRAINAGE_LAYER_VISUALS.conduits.minzoom, paint: { "line-color": stateColor(MAP_STYLE_TOKENS.asset.conduit), "line-width": DRAINAGE_LAYER_VISUALS.conduits.width, "line-opacity": MAP_STYLE_TOKENS.opacity.core } },
{ id: "junctions-halo", type: "circle", source: "junctions", "source-layer": SOURCE_LAYERS.junctions, minzoom: DRAINAGE_LAYER_VISUALS.junctions.minzoom, paint: { "circle-color": MAP_STYLE_TOKENS.canvas.casing, "circle-radius": DRAINAGE_LAYER_VISUALS.junctions.haloRadius, "circle-opacity": DRAINAGE_LAYER_VISUALS.junctions.haloOpacity } },
{ id: "junctions", type: "circle", source: "junctions", "source-layer": SOURCE_LAYERS.junctions, minzoom: DRAINAGE_LAYER_VISUALS.junctions.minzoom, paint: { "circle-color": stateColor(MAP_STYLE_TOKENS.asset.junction), "circle-radius": DRAINAGE_LAYER_VISUALS.junctions.radius, "circle-opacity": MAP_STYLE_TOKENS.opacity.core } },
{ id: "orifices-casing", type: "line", source: "orifices", "source-layer": SOURCE_LAYERS.orifices, minzoom: DRAINAGE_LAYER_VISUALS.orifices.minzoom, paint: { "line-color": MAP_STYLE_TOKENS.canvas.casing, "line-width": DRAINAGE_LAYER_VISUALS.orifices.casingWidth, "line-opacity": 1 } },
{ id: "orifices", type: "line", source: "orifices", "source-layer": SOURCE_LAYERS.orifices, minzoom: DRAINAGE_LAYER_VISUALS.orifices.minzoom, paint: { "line-color": stateColor(MAP_STYLE_TOKENS.asset.orifice), "line-width": DRAINAGE_LAYER_VISUALS.orifices.width, "line-dasharray": [4, 2], "line-opacity": MAP_STYLE_TOKENS.opacity.core } },
{ id: "pumps-casing", type: "line", source: "pumps", "source-layer": SOURCE_LAYERS.pumps, minzoom: DRAINAGE_LAYER_VISUALS.pumps.minzoom, paint: { "line-color": MAP_STYLE_TOKENS.canvas.casing, "line-width": DRAINAGE_LAYER_VISUALS.pumps.casingWidth, "line-opacity": 1 } },
{ id: "pumps", type: "line", source: "pumps", "source-layer": SOURCE_LAYERS.pumps, minzoom: DRAINAGE_LAYER_VISUALS.pumps.minzoom, paint: { "line-color": stateColor(MAP_STYLE_TOKENS.asset.pump), "line-width": DRAINAGE_LAYER_VISUALS.pumps.width, "line-dasharray": [6, 2, 1.5, 2], "line-opacity": MAP_STYLE_TOKENS.opacity.core } },
{ id: "outfalls-halo", type: "circle", source: "outfalls", "source-layer": SOURCE_LAYERS.outfalls, minzoom: DRAINAGE_LAYER_VISUALS.outfalls.minzoom, paint: { "circle-color": MAP_STYLE_TOKENS.canvas.casing, "circle-radius": DRAINAGE_LAYER_VISUALS.outfalls.haloRadius, "circle-opacity": 1 } },
{ id: "outfalls", type: "circle", source: "outfalls", "source-layer": SOURCE_LAYERS.outfalls, minzoom: DRAINAGE_LAYER_VISUALS.outfalls.minzoom, paint: { "circle-color": MAP_STYLE_TOKENS.canvas.casing, "circle-radius": DRAINAGE_LAYER_VISUALS.outfalls.radius, "circle-opacity": 1, "circle-stroke-color": stateColor(MAP_STYLE_TOKENS.asset.outfall), "circle-stroke-width": 2 } }
);
const lineInteractions = [
["pipes", "conduits", SOURCE_LAYERS.conduits, DRAINAGE_LAYER_VISUALS.conduits, MAP_STYLE_TOKENS.asset.conduit],
["orifices", "orifices", SOURCE_LAYERS.orifices, DRAINAGE_LAYER_VISUALS.orifices, MAP_STYLE_TOKENS.asset.orifice],
["pumps", "pumps", SOURCE_LAYERS.pumps, DRAINAGE_LAYER_VISUALS.pumps, MAP_STYLE_TOKENS.asset.pump]
] as const;
lineInteractions.forEach(([prefix, source, sourceLayer, visual, color]) => {
layers.push(
{ id: `${prefix}-hover-outline`, type: "line", source, "source-layer": sourceLayer, minzoom: visual.minzoom, paint: { "line-color": MAP_STYLE_TOKENS.canvas.casing, "line-width": visual.hoverOutlineWidth, "line-opacity": hoveredOpacity, "line-opacity-transition": opacityTransition } },
{ id: `${prefix}-hover`, type: "line", source, "source-layer": sourceLayer, minzoom: visual.minzoom, paint: { "line-color": stateColor(color), "line-width": visual.hoverWidth, "line-opacity": hoveredOpacity, "line-opacity-transition": opacityTransition } },
{ id: `${prefix}-selected-outer`, type: "line", source, "source-layer": sourceLayer, minzoom: visual.minzoom, paint: { "line-color": MAP_STYLE_TOKENS.canvas.casing, "line-gap-width": visual.selectedWidth, "line-width": 4.5, "line-opacity": selectedOpacity, "line-opacity-transition": opacityTransition } },
{ id: `${prefix}-selected-outline`, type: "line", source, "source-layer": sourceLayer, minzoom: visual.minzoom, paint: { "line-color": MAP_STYLE_TOKENS.state.selected, "line-gap-width": visual.selectedWidth, "line-width": visual.selectedBorderWidth, "line-opacity": selectedOpacity, "line-opacity-transition": opacityTransition } },
{ id: `${prefix}-selected-separator`, type: "line", source, "source-layer": sourceLayer, minzoom: visual.minzoom, paint: { "line-color": MAP_STYLE_TOKENS.canvas.casing, "line-gap-width": visual.selectedWidth, "line-width": 1, "line-offset": -1.5, "line-opacity": selectedOpacity, "line-opacity-transition": opacityTransition } }
);
});
const pointInteractions = [
["junctions", "junctions", SOURCE_LAYERS.junctions, DRAINAGE_LAYER_VISUALS.junctions, MAP_STYLE_TOKENS.asset.junction],
["outfalls", "outfalls", SOURCE_LAYERS.outfalls, DRAINAGE_LAYER_VISUALS.outfalls, MAP_STYLE_TOKENS.asset.outfall]
] as const;
pointInteractions.forEach(([prefix, source, sourceLayer, visual, color]) => {
layers.push(
{ id: `${prefix}-hover`, type: "circle", source, "source-layer": sourceLayer, minzoom: visual.minzoom, paint: { "circle-color": stateColor(color), "circle-radius": visual.hoverRadius, "circle-opacity": hoveredOpacity, "circle-stroke-color": MAP_STYLE_TOKENS.canvas.casing, "circle-stroke-width": 2, "circle-opacity-transition": opacityTransition, "circle-stroke-opacity": hoveredOpacity, "circle-stroke-opacity-transition": opacityTransition } },
{ id: `${prefix}-selected-outer`, type: "circle", source, "source-layer": sourceLayer, minzoom: visual.minzoom, paint: { "circle-color": MAP_STYLE_TOKENS.canvas.transparent, "circle-radius": visual.selectedRadius, "circle-opacity": selectedOpacity, "circle-stroke-color": MAP_STYLE_TOKENS.canvas.casing, "circle-stroke-width": 6, "circle-stroke-opacity": selectedOpacity, "circle-opacity-transition": opacityTransition, "circle-stroke-opacity-transition": opacityTransition } },
{ id: `${prefix}-selected`, type: "circle", source, "source-layer": sourceLayer, minzoom: visual.minzoom, paint: { "circle-color": MAP_STYLE_TOKENS.canvas.transparent, "circle-radius": visual.selectedRadius, "circle-opacity": selectedOpacity, "circle-stroke-color": MAP_STYLE_TOKENS.state.selected, "circle-stroke-width": 3, "circle-stroke-opacity": selectedOpacity, "circle-opacity-transition": opacityTransition, "circle-stroke-opacity-transition": opacityTransition } }
);
});
// Transparent hit layers stay last so pointer targeting is independent of paint order.
layers.push(
{ id: "conduits-hit", type: "line", source: "conduits", "source-layer": SOURCE_LAYERS.conduits, minzoom: DRAINAGE_LAYER_VISUALS.conduits.minzoom, paint: { "line-color": MAP_STYLE_TOKENS.canvas.transparent, "line-width": DRAINAGE_LAYER_VISUALS.conduits.hitWidth, "line-opacity": 0 } },
{ id: "junctions-hit", type: "circle", source: "junctions", "source-layer": SOURCE_LAYERS.junctions, minzoom: DRAINAGE_LAYER_VISUALS.junctions.minzoom, paint: { "circle-color": MAP_STYLE_TOKENS.canvas.transparent, "circle-radius": DRAINAGE_LAYER_VISUALS.junctions.hitRadius, "circle-opacity": 0 } },
{ id: "orifices-hit", type: "line", source: "orifices", "source-layer": SOURCE_LAYERS.orifices, minzoom: DRAINAGE_LAYER_VISUALS.orifices.minzoom, paint: { "line-color": MAP_STYLE_TOKENS.canvas.transparent, "line-width": DRAINAGE_LAYER_VISUALS.orifices.hitWidth, "line-opacity": 0 } },
{ id: "pumps-hit", type: "line", source: "pumps", "source-layer": SOURCE_LAYERS.pumps, minzoom: DRAINAGE_LAYER_VISUALS.pumps.minzoom, paint: { "line-color": MAP_STYLE_TOKENS.canvas.transparent, "line-width": DRAINAGE_LAYER_VISUALS.pumps.hitWidth, "line-opacity": 0 } },
{ id: "outfalls-hit", type: "circle", source: "outfalls", "source-layer": SOURCE_LAYERS.outfalls, minzoom: DRAINAGE_LAYER_VISUALS.outfalls.minzoom, paint: { "circle-color": MAP_STYLE_TOKENS.canvas.transparent, "circle-radius": DRAINAGE_LAYER_VISUALS.outfalls.hitRadius, "circle-opacity": 0 } }
);
export const waterNetworkLayers: StyleSpecification["layers"] = layers as Layer[];
const interactionIndex = layers.findIndex((layer) => layer.id === "pipes-hover-outline");
const hitIndex = layers.findIndex((layer) => layer.id === INTERACTIVE_HIT_LAYER_IDS[0]);
export const waterNetworkBusinessLayers = layers.slice(0, interactionIndex) as Layer[];
export const waterNetworkInteractionLayers = layers.slice(interactionIndex, hitIndex) as Layer[];
export const waterNetworkHitLayers = layers.slice(hitIndex) as Layer[];
export const WORKBENCH_INTERACTION_BEFORE_ID = "pipes-hover-outline";