102 lines
2.6 KiB
TypeScript
102 lines
2.6 KiB
TypeScript
import maplibregl from "maplibre-gl";
|
|
import { Geometry } from "@/lib/useEditorState";
|
|
|
|
type ModeGetter = () => "idle" | "draw" | "select" | "add-point" | "add-line" | "add-path" | "add-circle";
|
|
|
|
export function initDrawing(
|
|
map: maplibregl.Map,
|
|
getMode: ModeGetter,
|
|
onComplete: (geometry: Geometry) => void
|
|
) {
|
|
let coords: [number, number][] = [];
|
|
|
|
/**
|
|
* Close polygon ring if not closed.
|
|
*/
|
|
function closePolygon(c: [number, number][]) {
|
|
if (c.length < 3) return c;
|
|
const first = c[0];
|
|
const last = c[c.length - 1];
|
|
|
|
if (first[0] !== last[0] || first[1] !== last[1]) {
|
|
return [...c, first];
|
|
}
|
|
return c;
|
|
}
|
|
|
|
/**
|
|
* Update preview layer while drawing.
|
|
*/
|
|
function update(c: [number, number][]) {
|
|
const closed = closePolygon(c);
|
|
|
|
(map.getSource("draw-preview") as maplibregl.GeoJSONSource)?.setData({
|
|
type: "FeatureCollection",
|
|
features: [
|
|
{
|
|
type: "Feature",
|
|
properties: {},
|
|
geometry: {
|
|
type: "Polygon",
|
|
coordinates: [closed],
|
|
},
|
|
},
|
|
],
|
|
});
|
|
}
|
|
|
|
function onClick(e: maplibregl.MapLayerMouseEvent) {
|
|
if (getMode() !== "draw") return;
|
|
|
|
coords.push([e.lngLat.lng, e.lngLat.lat] as [number, number]);
|
|
update(coords);
|
|
}
|
|
|
|
function onMove(e: maplibregl.MapLayerMouseEvent) {
|
|
if (getMode() !== "draw" || coords.length === 0) return;
|
|
|
|
const preview: [number, number][] = [
|
|
...coords,
|
|
[e.lngLat.lng, e.lngLat.lat] as [number, number],
|
|
];
|
|
update(preview);
|
|
}
|
|
|
|
/**
|
|
* Finalize polygon, emit geometry to caller, reset preview.
|
|
*/
|
|
function finishDrawing() {
|
|
if (getMode() !== "draw" || coords.length < 3) return;
|
|
|
|
const geometry: Geometry = {
|
|
type: "Polygon",
|
|
coordinates: [closePolygon(coords)],
|
|
};
|
|
|
|
onComplete(geometry);
|
|
|
|
coords = [];
|
|
|
|
(map.getSource("draw-preview") as maplibregl.GeoJSONSource | undefined)?.setData({
|
|
type: "FeatureCollection",
|
|
features: [],
|
|
});
|
|
}
|
|
|
|
function onKeyDown(e: KeyboardEvent) {
|
|
if (e.key === "Enter") {
|
|
finishDrawing();
|
|
}
|
|
}
|
|
|
|
map.on("click", onClick);
|
|
map.on("mousemove", onMove);
|
|
document.addEventListener("keydown", onKeyDown);
|
|
|
|
return () => {
|
|
map.off("click", onClick);
|
|
map.off("mousemove", onMove);
|
|
document.removeEventListener("keydown", onKeyDown);
|
|
};
|
|
}
|