From d1c2a3e4f968eabd1a7baa5d1c5215563db27e0d Mon Sep 17 00:00:00 2001 From: Ian Jones Date: Mon, 28 Sep 2026 12:06:17 +0100 Subject: [PATCH 1/2] feat: Right click to delete point or polygon node --- src/components/my-map/drawing.ts | 7 +++ src/components/my-map/index.ts | 74 +++++++++++++++++++++++++++++--- 2 files changed, 75 insertions(+), 6 deletions(-) diff --git a/src/components/my-map/drawing.ts b/src/components/my-map/drawing.ts index 13e6548e..85bc274f 100644 --- a/src/components/my-map/drawing.ts +++ b/src/components/my-map/drawing.ts @@ -1,6 +1,7 @@ import { FeatureLike } from "ol/Feature"; import { MultiPoint, MultiPolygon, Polygon } from "ol/geom"; import { Type } from "ol/geom/Geometry"; +import { noModifierKeys, primaryAction } from "ol/events/condition"; import { Draw, Modify, Snap } from "ol/interaction"; import { Vector as VectorLayer } from "ol/layer"; import { Vector as VectorSource } from "ol/source"; @@ -176,6 +177,8 @@ export function configureDraw( return new Draw({ source: drawingSource, type: drawType, + // Reserve right-click for undoing and deleting, rather than drawing + condition: (event) => noModifierKeys(event) && primaryAction(event), style: configureDrawInteractionStyle(drawType, drawPointer, drawColor), }); } @@ -185,12 +188,16 @@ export const snap = new Snap({ pixelTolerance: 15, }); +// Distance (px) from a vertex within which Modify shows the draw pointer, also used for right-click deletion +export const modifyPixelTolerance = 10; + export function configureModify( drawPointer: DrawPointerEnum, drawColor: string, ) { return new Modify({ source: drawingSource, + pixelTolerance: modifyPixelTolerance, style: new Style({ image: configureDrawPointerImage(drawPointer, drawColor), }), diff --git a/src/components/my-map/index.ts b/src/components/my-map/index.ts index 864f441a..b84fed8b 100644 --- a/src/components/my-map/index.ts +++ b/src/components/my-map/index.ts @@ -31,6 +31,7 @@ import { drawingSource, DrawPointerEnum, DrawTypeEnum, + modifyPixelTolerance, snap, } from "./drawing"; import pinIcon from "./icons/poi-alt.svg"; @@ -607,6 +608,63 @@ export class MyMap extends LitElement { // Snap must be added after draw and modify map.addInteraction(snap); + // Track whether a polygon is part-way through being drawn + let isDrawing = false; + draw.on("drawstart", () => (isDrawing = true)); + draw.on(["drawend", "drawabort"], () => (isDrawing = false)); + + // Right-click to undo the last vertex while drawing, or delete the point/vertex under the draw pointer + map.getViewport().addEventListener("contextmenu", (event) => { + if (isDrawing) { + event.preventDefault(); + draw.removeLastPoint(); + return; + } + + const pixel = map.getEventPixel(event); + + if (this.drawType !== "Point") { + // Modify only removes a vertex within its pixelTolerance, and won't reduce a polygon below a triangle + if (modify.removePoint(map.getCoordinateFromPixel(pixel))) { + event.preventDefault(); + } + return; + } + + // Only delete when close enough to the point's centre for Modify to show the draw pointer + const feature = drawingSource.getClosestFeatureToCoordinate( + map.getCoordinateFromPixel(pixel), + ); + const geom = feature?.getGeometry(); + if (!feature || !(geom instanceof Point)) return; + const pointPixel = map.getPixelFromCoordinate(geom.getCoordinates()); + if ( + Math.hypot(pointPixel[0] - pixel[0], pointPixel[1] - pixel[1]) > + modifyPixelTolerance + ) + return; + + event.preventDefault(); + drawingSource.removeFeature(feature); + + // Re-number remaining labels so the next drawn point doesn't duplicate an existing label + drawingSource + .getFeatures() + .forEach((sketch, i) => sketch.set("label", `${i + 1}`)); + + // The 'change' listener below only dispatches when features remain + if (drawingSource.getFeatures().length === 0) { + this.dispatch("geojsonChange", {}); + + // If limited to a single point, allow drawing again (snap must be added after draw) + if (!this.drawMany) { + map.removeInteraction(snap); + map.addInteraction(draw); + map.addInteraction(snap); + } + } + }); + // 'change' listens for 'drawend' and modifications drawingSource.on("change", () => { const sketches = drawingSource.getFeatures(); @@ -905,9 +963,11 @@ export class MyMap extends LitElement {
` @@ -918,9 +978,11 @@ export class MyMap extends LitElement {
`; From fb2257070e4cd2ada1e35a679269e7fa1325971c Mon Sep 17 00:00:00 2001 From: Ian Jones Date: Tue, 29 Sep 2026 10:34:32 +0100 Subject: [PATCH 2/2] alt/opt click for deletion of single points --- src/components/my-map/index.ts | 51 ++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/src/components/my-map/index.ts b/src/components/my-map/index.ts index b84fed8b..ce556cf7 100644 --- a/src/components/my-map/index.ts +++ b/src/components/my-map/index.ts @@ -4,6 +4,7 @@ import apply from "ol-mapbox-style"; import { defaults as defaultControls, ScaleLine } from "ol/control"; import { containsCoordinate, Extent } from "ol/extent"; import { FeatureLike } from "ol/Feature"; +import { altKeyOnly } from "ol/events/condition"; import { GeoJSON } from "ol/format"; import { GeoJSONFeature, GeoJSONFeatureCollection } from "ol/format/GeoJSON"; import { Geometry, Point } from "ol/geom"; @@ -613,38 +614,21 @@ export class MyMap extends LitElement { draw.on("drawstart", () => (isDrawing = true)); draw.on(["drawend", "drawabort"], () => (isDrawing = false)); - // Right-click to undo the last vertex while drawing, or delete the point/vertex under the draw pointer - map.getViewport().addEventListener("contextmenu", (event) => { - if (isDrawing) { - event.preventDefault(); - draw.removeLastPoint(); - return; - } - - const pixel = map.getEventPixel(event); - - if (this.drawType !== "Point") { - // Modify only removes a vertex within its pixelTolerance, and won't reduce a polygon below a triangle - if (modify.removePoint(map.getCoordinateFromPixel(pixel))) { - event.preventDefault(); - } - return; - } - + // Delete the point under the draw pointer, returning whether one was deleted + const deletePointAtPixel = (pixel: number[]): boolean => { // Only delete when close enough to the point's centre for Modify to show the draw pointer const feature = drawingSource.getClosestFeatureToCoordinate( map.getCoordinateFromPixel(pixel), ); const geom = feature?.getGeometry(); - if (!feature || !(geom instanceof Point)) return; + if (!feature || !(geom instanceof Point)) return false; const pointPixel = map.getPixelFromCoordinate(geom.getCoordinates()); if ( Math.hypot(pointPixel[0] - pixel[0], pointPixel[1] - pixel[1]) > modifyPixelTolerance ) - return; + return false; - event.preventDefault(); drawingSource.removeFeature(feature); // Re-number remaining labels so the next drawn point doesn't duplicate an existing label @@ -663,8 +647,33 @@ export class MyMap extends LitElement { map.addInteraction(snap); } } + return true; + }; + + // Right-click to undo the last vertex while drawing, or delete the point/vertex under the draw pointer + map.getViewport().addEventListener("contextmenu", (event) => { + if (isDrawing) { + event.preventDefault(); + draw.removeLastPoint(); + return; + } + + const pixel = map.getEventPixel(event); + const deleted = + this.drawType === "Point" + ? deletePointAtPixel(pixel) + : // Modify only removes a vertex within its pixelTolerance, and won't reduce a polygon below a triangle + modify.removePoint(map.getCoordinateFromPixel(pixel)); + if (deleted) event.preventDefault(); }); + // Alt-click (Option-click on Mac) to delete a point, matching Modify's built-in vertex deletion for polygons + if (this.drawType === "Point") { + map.on("singleclick", (event) => { + if (altKeyOnly(event)) deletePointAtPixel(event.pixel); + }); + } + // 'change' listens for 'drawend' and modifications drawingSource.on("change", () => { const sketches = drawingSource.getFeatures();