Files
next-tjwater-drainage-frontend/features/workbench/map/drawing-layers.ts
T

294 lines
7.8 KiB
TypeScript

import type {
FilterSpecification,
GeoJSONSource,
GeoJSONSourceSpecification,
StyleSpecification
} from "maplibre-gl";
import type { FeatureCollection, Geometry, LineString, Point, Polygon } from "geojson";
import { createCircleCoordinates } from "./geo";
import { MAP_STYLE_TOKENS } from "./map-colors";
import { WORKBENCH_INTERACTION_BEFORE_ID } from "./layers";
export const DRAWING_SOURCE_IDS = {
features: "workbench-drawing-features",
draft: "workbench-drawing-draft"
} as const;
export const DRAWING_INTERACTIVE_LAYER_IDS = [
"workbench-drawing-selected-polygon-outline",
"workbench-drawing-selected-line",
"workbench-drawing-selected-point",
"workbench-drawing-polygon-fill",
"workbench-drawing-polygon-outline",
"workbench-drawing-line",
"workbench-drawing-point"
];
export type DrawingFeatureProperties = {
id: string;
label: string;
kind: "point" | "line" | "polygon" | "circle";
createdAt: string;
};
export type DrawingFeatureCollection = FeatureCollection<Geometry, DrawingFeatureProperties>;
export const emptyDrawingFeatureCollection: DrawingFeatureCollection = {
type: "FeatureCollection",
features: []
};
export const drawingSources = {
features: {
type: "geojson",
data: emptyDrawingFeatureCollection
} satisfies GeoJSONSourceSpecification,
draft: {
type: "geojson",
data: emptyDrawingFeatureCollection
} satisfies GeoJSONSourceSpecification
};
export const drawingLayers: StyleSpecification["layers"] = [
{
id: "workbench-drawing-polygon-fill",
type: "fill",
source: DRAWING_SOURCE_IDS.features,
filter: ["==", ["geometry-type"], "Polygon"],
paint: {
"fill-color": MAP_STYLE_TOKENS.tool.geometry,
"fill-opacity": 0.16
}
},
{
id: "workbench-drawing-polygon-outline",
type: "line",
source: DRAWING_SOURCE_IDS.features,
filter: ["==", ["geometry-type"], "Polygon"],
paint: {
"line-color": MAP_STYLE_TOKENS.tool.geometry,
"line-width": 2,
"line-opacity": 0.82
}
},
{
id: "workbench-drawing-line",
type: "line",
source: DRAWING_SOURCE_IDS.features,
filter: ["==", ["geometry-type"], "LineString"],
paint: {
"line-color": MAP_STYLE_TOKENS.tool.geometry,
"line-width": 3,
"line-opacity": 0.88
}
},
{
id: "workbench-drawing-point",
type: "circle",
source: DRAWING_SOURCE_IDS.features,
filter: ["==", ["geometry-type"], "Point"],
paint: {
"circle-color": MAP_STYLE_TOKENS.tool.geometry,
"circle-radius": 6,
"circle-stroke-color": MAP_STYLE_TOKENS.canvas.casing,
"circle-stroke-width": 2
}
},
{
id: "workbench-drawing-selected-polygon-outline",
type: "line",
source: DRAWING_SOURCE_IDS.features,
filter: ["all", ["==", ["geometry-type"], "Polygon"], ["==", ["get", "id"], ""]],
paint: {
"line-color": MAP_STYLE_TOKENS.state.selected,
"line-width": 4,
"line-opacity": 0.86
}
},
{
id: "workbench-drawing-selected-line",
type: "line",
source: DRAWING_SOURCE_IDS.features,
filter: ["all", ["==", ["geometry-type"], "LineString"], ["==", ["get", "id"], ""]],
paint: {
"line-color": MAP_STYLE_TOKENS.state.selected,
"line-width": 6,
"line-opacity": 0.72
}
},
{
id: "workbench-drawing-selected-point",
type: "circle",
source: DRAWING_SOURCE_IDS.features,
filter: ["all", ["==", ["geometry-type"], "Point"], ["==", ["get", "id"], ""]],
paint: {
"circle-color": MAP_STYLE_TOKENS.state.selected,
"circle-radius": 10,
"circle-opacity": 0.2,
"circle-stroke-color": MAP_STYLE_TOKENS.state.selected,
"circle-stroke-width": 2
}
},
{
id: "workbench-drawing-draft-polygon-fill",
type: "fill",
source: DRAWING_SOURCE_IDS.draft,
filter: ["==", ["geometry-type"], "Polygon"],
paint: {
"fill-color": MAP_STYLE_TOKENS.tool.geometry,
"fill-opacity": 0.12
}
},
{
id: "workbench-drawing-draft-line",
type: "line",
source: DRAWING_SOURCE_IDS.draft,
filter: ["any", ["==", ["geometry-type"], "LineString"], ["==", ["geometry-type"], "Polygon"]],
paint: {
"line-color": MAP_STYLE_TOKENS.tool.geometry,
"line-width": 2,
"line-dasharray": [2, 2],
"line-opacity": 0.9
}
},
{
id: "workbench-drawing-draft-vertex",
type: "circle",
source: DRAWING_SOURCE_IDS.draft,
filter: ["==", ["geometry-type"], "Point"],
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 ensureDrawingLayers(map: {
getSource: (id: string) => unknown;
addSource: (id: string, source: GeoJSONSourceSpecification) => void;
getLayer: (id: string) => unknown;
addLayer: (layer: NonNullable<StyleSpecification["layers"]>[number], beforeId?: string) => void;
}) {
if (!map.getSource(DRAWING_SOURCE_IDS.features)) {
map.addSource(DRAWING_SOURCE_IDS.features, drawingSources.features);
}
if (!map.getSource(DRAWING_SOURCE_IDS.draft)) {
map.addSource(DRAWING_SOURCE_IDS.draft, drawingSources.draft);
}
drawingLayers.forEach((layer) => {
if (!map.getLayer(layer.id)) {
map.addLayer(layer, WORKBENCH_INTERACTION_BEFORE_ID);
}
});
}
export function setDrawingSourceData(
map: { getSource: (id: string) => unknown },
sourceId: string,
data: DrawingFeatureCollection
) {
const source = map.getSource(sourceId) as GeoJSONSource | undefined;
source?.setData(data);
}
export function setSelectedDrawingFeatureId(
map: { getLayer: (id: string) => unknown; setFilter: (id: string, filter?: FilterSpecification) => void },
featureId: string | null
) {
const id = featureId ?? "";
[
["workbench-drawing-selected-polygon-outline", "Polygon"],
["workbench-drawing-selected-line", "LineString"],
["workbench-drawing-selected-point", "Point"]
].forEach(([layerId, geometryType]) => {
if (map.getLayer(layerId)) {
map.setFilter(layerId, ["all", ["==", ["geometry-type"], geometryType], ["==", ["get", "id"], id]]);
}
});
}
export function createPointFeature(
id: string,
coordinates: [number, number],
label: string
): DrawingFeatureCollection["features"][number] {
return {
type: "Feature",
id,
properties: createDrawingProperties(id, label, "point"),
geometry: {
type: "Point",
coordinates
} satisfies Point
};
}
export function createLineFeature(
id: string,
coordinates: [number, number][],
label: string
): DrawingFeatureCollection["features"][number] {
return {
type: "Feature",
id,
properties: createDrawingProperties(id, label, "line"),
geometry: {
type: "LineString",
coordinates
} satisfies LineString
};
}
export function createPolygonFeature(
id: string,
coordinates: [number, number][],
label: string
): DrawingFeatureCollection["features"][number] {
return {
type: "Feature",
id,
properties: createDrawingProperties(id, label, "polygon"),
geometry: {
type: "Polygon",
coordinates: [[...coordinates, coordinates[0]]]
} satisfies Polygon
};
}
export function createCircleFeature(
id: string,
center: [number, number],
radiusMeters: number,
label: string
): DrawingFeatureCollection["features"][number] {
const coordinates = createCircleCoordinates(center, radiusMeters);
return {
type: "Feature",
id,
properties: createDrawingProperties(id, label, "circle"),
geometry: {
type: "Polygon",
coordinates: [coordinates]
} satisfies Polygon
};
}
function createDrawingProperties(
id: string,
label: string,
kind: DrawingFeatureProperties["kind"]
): DrawingFeatureProperties {
return {
id,
label,
kind,
createdAt: new Date().toISOString()
};
}