import type { FilterSpecification, GeoJSONSource, GeoJSONSourceSpecification, StyleSpecification } from "maplibre-gl"; import type { FeatureCollection, Geometry, LineString, Point, Polygon } from "geojson"; import { SOURCE_LAYERS } from "./sources"; import { MAP_STYLE_TOKENS } from "./map-colors"; import { WORKBENCH_INTERACTION_BEFORE_ID } from "./layers"; export const MEASUREMENT_SOURCE_ID = "workbench-measurement"; export const MEASUREMENT_SELECTED_PIPES_LAYER_ID = "workbench-measurement-selected-pipes"; export type MeasurementFeatureCollection = FeatureCollection; export const emptyMeasurementFeatureCollection: MeasurementFeatureCollection = { type: "FeatureCollection", features: [] }; export const measurementSource = { type: "geojson", data: emptyMeasurementFeatureCollection } satisfies GeoJSONSourceSpecification; export const measurementLayers: StyleSpecification["layers"] = [ { id: MEASUREMENT_SELECTED_PIPES_LAYER_ID, type: "line", source: "conduits", "source-layer": SOURCE_LAYERS.conduits, minzoom: 9, filter: ["in", ["get", "id"], ["literal", []]], paint: { "line-color": MAP_STYLE_TOKENS.state.selected, "line-width": ["interpolate", ["linear"], ["zoom"], 9, 4, 13, 7, 17, 11], "line-opacity": 0.68 } }, { id: "workbench-measurement-polygon-fill", type: "fill", source: MEASUREMENT_SOURCE_ID, filter: ["==", ["get", "kind"], "polygon"], paint: { "fill-color": MAP_STYLE_TOKENS.tool.geometry, "fill-opacity": 0.12 } }, { id: "workbench-measurement-line", type: "line", source: MEASUREMENT_SOURCE_ID, filter: ["any", ["==", ["get", "kind"], "line"], ["==", ["get", "kind"], "polygon"]], paint: { "line-color": MAP_STYLE_TOKENS.tool.geometry, "line-width": 2.5, "line-dasharray": [2, 1.5], "line-opacity": 0.9 } }, { id: "workbench-measurement-vertex", type: "circle", source: MEASUREMENT_SOURCE_ID, filter: ["==", ["get", "kind"], "vertex"], paint: { "circle-color": MAP_STYLE_TOKENS.state.selected, "circle-radius": 4, "circle-stroke-color": MAP_STYLE_TOKENS.canvas.casing, "circle-stroke-width": 1.5 } } ]; export function ensureMeasurementLayers(map: { getSource: (id: string) => unknown; addSource: (id: string, source: GeoJSONSourceSpecification) => void; getLayer: (id: string) => unknown; addLayer: (layer: NonNullable[number], beforeId?: string) => void; }) { if (!map.getSource(MEASUREMENT_SOURCE_ID)) { map.addSource(MEASUREMENT_SOURCE_ID, measurementSource); } measurementLayers.forEach((layer) => { if (!map.getLayer(layer.id)) { map.addLayer(layer, WORKBENCH_INTERACTION_BEFORE_ID); } }); } export function setMeasurementSourceData( map: { getSource: (id: string) => unknown }, data: MeasurementFeatureCollection ) { const source = map.getSource(MEASUREMENT_SOURCE_ID) as GeoJSONSource | undefined; source?.setData(data); } export function setSelectedMeasurementPipeIds( map: { getLayer: (id: string) => unknown; setFilter: (id: string, filter?: FilterSpecification) => void }, pipeIds: string[] ) { if (!map.getLayer(MEASUREMENT_SELECTED_PIPES_LAYER_ID)) { return; } map.setFilter(MEASUREMENT_SELECTED_PIPES_LAYER_ID, ["in", ["get", "id"], ["literal", pipeIds]]); } export function createMeasurementFeatureCollection( coordinates: [number, number][], mode: "distance" | "area" | "segment" ): MeasurementFeatureCollection { const features: MeasurementFeatureCollection["features"] = coordinates.map((coordinate, index) => ({ type: "Feature", id: `measurement-vertex-${index}`, properties: { kind: "vertex" }, geometry: { type: "Point", coordinates: coordinate } satisfies Point })); if (coordinates.length >= 2) { features.push({ type: "Feature", id: "measurement-line", properties: { kind: "line" }, geometry: { type: "LineString", coordinates } satisfies LineString }); } if (mode === "area" && coordinates.length >= 3) { features.push({ type: "Feature", id: "measurement-polygon", properties: { kind: "polygon" }, geometry: { type: "Polygon", coordinates: [[...coordinates, coordinates[0]]] } satisfies Polygon }); } return { type: "FeatureCollection", features }; }