Files
next-tjwater-frontend/src/features/workbench/peripheral-sample/event-location-marker.ts
T

57 lines
2.2 KiB
TypeScript

import { Marker, type Map as MapLibreMap } from "maplibre-gl";
import type { EventGpsCoordinate } from "./event-location-coordinate";
const arrowDirections: Record<string, [number, number]> = {
ArrowLeft: [-1, 0], ArrowRight: [1, 0], ArrowUp: [0, -1], ArrowDown: [0, 1]
};
export function createEventLocationMarker({ map, element, coordinate, disabled, onChange, onDragChange }: {
map: MapLibreMap;
element: HTMLButtonElement;
coordinate: EventGpsCoordinate;
disabled: boolean;
onChange: (coordinate: EventGpsCoordinate) => void;
onDragChange: (dragging: boolean) => void;
}) {
const marker = new Marker({ element, anchor: "bottom", draggable: !disabled })
.setLngLat(coordinate)
.addTo(map);
element.setAttribute("aria-label", "事件位置标记,可拖拽调整");
element.setAttribute("title", "拖拽调整事件位置,方向键微调,Shift 加速");
element.setAttribute("aria-keyshortcuts", "ArrowUp ArrowDown ArrowLeft ArrowRight");
marker.on("dragstart", () => {
element.dataset.dragging = "true";
onDragChange(true);
});
marker.on("dragend", () => {
element.dataset.dragging = "false";
onDragChange(false);
const position = marker.getLngLat();
onChange([normalizeLongitude(position.lng), clampLatitude(position.lat)]);
});
// Custom MapLibre markers own their keyboard interaction. Move in screen
// pixels and consume arrows so adjusting the pin cannot also pan the map.
element.onkeydown = (event) => {
if (!marker.isDraggable() || event.altKey || event.ctrlKey || event.metaKey) return;
const direction = arrowDirections[event.key];
if (!direction) return;
event.preventDefault();
event.stopPropagation();
const point = map.project(marker.getLngLat());
const step = event.shiftKey ? 10 : 1;
const position = map.unproject([point.x + direction[0] * step, point.y + direction[1] * step]);
const next: EventGpsCoordinate = [normalizeLongitude(position.lng), clampLatitude(position.lat)];
marker.setLngLat(next);
onChange(next);
};
return marker;
}
export function normalizeLongitude(longitude: number) {
return ((longitude + 180) % 360 + 360) % 360 - 180;
}
export function clampLatitude(latitude: number) {
return Math.max(-90, Math.min(90, latitude));
}