One way to add a point: the Draw tool, not a separate dock button

There were two ways to put a point on the map — the Add Location dock button
and Draw > Point — and they produced different things: one a Location record,
the other a sketch in the Drawings layer. Officers had to know which was which.

Draw > Point now raises the Add Location form at the coordinate placed, and the
Add Location button and its mode are gone.

The sketch is taken back out again. The marker created by the form is the
record, so leaving the sketch would show the same place twice, and it would
linger if the officer cancelled the form. That removal needs care: ol adds the
sketch to the source *after* drawend returns, so the add and the removal
straddle a tick and undo recording has to be suppressed across both. Suppressing
only the removal leaves the add on the stack, and Undo then resurrects a point
the officer never kept. Verified that the stack is untouched and that recording
is switched back on afterwards.

Tapping a marker to see its details used to require Add Location mode. With that
mode gone it now works in the neutral mode, where a click previously did nothing
at all. Clicks on empty space still do nothing — a stray tap should not open a
form.

The dock keeps its six remaining buttons. Below 576px they divide the width
evenly rather than relying on a 60px minimum each, which no longer fitted once
padded (6 x 60 + 32 = 392px against 390px on common iPhones). Checked from 360px
up.

Service Worker v19 -> v20.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
ekke 2026-08-20 17:43:02 +02:00
parent 1c0d4319b7
commit b28a34bc38
11 changed files with 256 additions and 151 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
dist/assets/index-CF9fisV-.js.map vendored Normal file

File diff suppressed because one or more lines are too long

33
dist/index.html vendored
View File

@ -875,6 +875,30 @@
font-family: var(--font-body); font-family: var(--font-body);
} }
/* Narrow screens: share the width evenly rather than relying on each
button's 60px minimum, which no longer fits once the dock is padded
(6 x 60 + 32 = 392px, against 390px on the common iPhone widths).
Equal division also keeps the row visually regular now that Add Location
has gone and the count changed. */
@media (max-width: 576px) {
.bottom-dock {
justify-content: space-between;
gap: 4px;
padding-left: 8px;
padding-right: 8px;
}
.dock-btn {
flex: 1 1 0;
min-width: 0;
padding: 6px 2px;
font-size: 1.25rem;
}
.dock-btn-label {
font-size: 0.58rem;
letter-spacing: -0.01em;
}
}
/* EditBar colour picker inline styling */ /* EditBar colour picker inline styling */
.ol-editbar .ol-colorpicker button { .ol-editbar .ol-colorpicker button {
padding: 2px; padding: 2px;
@ -1893,7 +1917,7 @@
} }
</style> </style>
<script type="module" crossorigin src="/assets/index-BM5P5Dc7.js"></script> <script type="module" crossorigin src="/assets/index-CF9fisV-.js"></script>
<link rel="modulepreload" crossorigin href="/assets/openlayers-J9qS6Th1.js"> <link rel="modulepreload" crossorigin href="/assets/openlayers-J9qS6Th1.js">
<link rel="modulepreload" crossorigin href="/assets/pako-Xa-UToif.js"> <link rel="modulepreload" crossorigin href="/assets/pako-Xa-UToif.js">
<link rel="modulepreload" crossorigin href="/assets/geotiff-BaoeLn6q.js"> <link rel="modulepreload" crossorigin href="/assets/geotiff-BaoeLn6q.js">
@ -1996,10 +2020,9 @@
<!-- Bottom Dock --> <!-- Bottom Dock -->
<div class="bottom-dock"> <div class="bottom-dock">
<button class="dock-btn" type="button" id="dock-btn-add-location" title="Add Location Mode"> <!-- The Add Location button used to sit here. Adding a point is now
<span>📍</span> the Draw > Point tool inside Digitise, so there is one gesture
<span class="dock-btn-label">Add</span> for it rather than two. -->
</button>
<button class="dock-btn" type="button" id="dock-btn-measure-circle" title="Measure Circle"> <button class="dock-btn" type="button" id="dock-btn-measure-circle" title="Measure Circle">
<span></span> <span></span>
<span class="dock-btn-label">Circle</span> <span class="dock-btn-label">Circle</span>

8
dist/sw.js vendored
View File

@ -107,7 +107,13 @@
// base-map buttons on the right and clipped by the dock. It now sits above // base-map buttons on the right and clipped by the dock. It now sits above
// that whole right-edge stack (bottom: 196px = the switcher's 144px anchor // that whole right-edge stack (bottom: 196px = the switcher's 144px anchor
// + its 44px height + an 8px gap). // + its 44px height + an 8px gap).
const CACHE_VERSION = 'v19'; // v20: One way to add a point instead of two. The Add Location dock button is
// gone; placing a point with Draw > Point now opens the same Add Location
// form. The sketch is discarded so the marker is the only record, and the
// add/remove pair is kept off the undo stack. Marker details, which used
// to need Add Location mode, now open in the neutral mode. The dock keeps
// its six remaining buttons and divides them evenly on narrow screens.
const CACHE_VERSION = 'v20';
const SHELL_CACHE = `shell-${CACHE_VERSION}`; const SHELL_CACHE = `shell-${CACHE_VERSION}`;
const MODULES_CACHE = `modules-${CACHE_VERSION}`; const MODULES_CACHE = `modules-${CACHE_VERSION}`;
const API_CACHE = `api-${CACHE_VERSION}`; const API_CACHE = `api-${CACHE_VERSION}`;

View File

@ -875,6 +875,30 @@
font-family: var(--font-body); font-family: var(--font-body);
} }
/* Narrow screens: share the width evenly rather than relying on each
button's 60px minimum, which no longer fits once the dock is padded
(6 x 60 + 32 = 392px, against 390px on the common iPhone widths).
Equal division also keeps the row visually regular now that Add Location
has gone and the count changed. */
@media (max-width: 576px) {
.bottom-dock {
justify-content: space-between;
gap: 4px;
padding-left: 8px;
padding-right: 8px;
}
.dock-btn {
flex: 1 1 0;
min-width: 0;
padding: 6px 2px;
font-size: 1.25rem;
}
.dock-btn-label {
font-size: 0.58rem;
letter-spacing: -0.01em;
}
}
/* EditBar colour picker inline styling */ /* EditBar colour picker inline styling */
.ol-editbar .ol-colorpicker button { .ol-editbar .ol-colorpicker button {
padding: 2px; padding: 2px;
@ -1985,10 +2009,9 @@
<!-- Bottom Dock --> <!-- Bottom Dock -->
<div class="bottom-dock"> <div class="bottom-dock">
<button class="dock-btn" type="button" id="dock-btn-add-location" title="Add Location Mode"> <!-- The Add Location button used to sit here. Adding a point is now
<span>📍</span> the Draw > Point tool inside Digitise, so there is one gesture
<span class="dock-btn-label">Add</span> for it rather than two. -->
</button>
<button class="dock-btn" type="button" id="dock-btn-measure-circle" title="Measure Circle"> <button class="dock-btn" type="button" id="dock-btn-measure-circle" title="Measure Circle">
<span></span> <span></span>
<span class="dock-btn-label">Circle</span> <span class="dock-btn-label">Circle</span>

62
main.js
View File

@ -143,13 +143,12 @@ let embedBridge = null;
const EMBED_CONFIG = (typeof window !== 'undefined' && window.LUPMIS_EMBED) || null; const EMBED_CONFIG = (typeof window !== 'undefined' && window.LUPMIS_EMBED) || null;
const IS_EMBED_PERMIT = !!(EMBED_CONFIG && EMBED_CONFIG.mode === 'permit'); const IS_EMBED_PERMIT = !!(EMBED_CONFIG && EMBED_CONFIG.mode === 'permit');
// Current interaction mode: 'none' | 'addLocation' | 'measureCircle' | // Current interaction mode: 'none' | 'measureCircle' | 'measureLine' |
// 'measureLine' | 'measureArea' | 'draw' | 'embed-permit'. // 'measureArea' | 'draw' | 'embed-permit'.
// Default is 'none' — a neutral state where map clicks do not trigger // Default is 'none': map clicks select a marker and show its details, and
// the Add-Location popup. Users explicitly opt into Add-Location by // nothing else. Adding a location is the Draw > Point tool inside Digitise,
// pressing the Add button in the bottom dock; pressing it again toggles // not a mode of its own. Measurement and Draw tools return to 'none' when
// the mode back off. Measurement / Draw tools also return to 'none' // toggled off.
// when toggled off, rather than implicitly re-entering Add-Location.
let currentMode = IS_EMBED_PERMIT ? 'embed-permit' : 'none'; let currentMode = IS_EMBED_PERMIT ? 'embed-permit' : 'none';
// ============================================================================ // ============================================================================
@ -319,21 +318,22 @@ async function initApp() {
return; return;
} }
// Non-parcel clicks (markers, empty space) only in addLocation mode // Tapping a marker opens its details. This used to require Add Location
if (currentMode !== 'addLocation') { // mode, which no longer exists — adding a location is now the Draw ▸ Point
// tool — so it happens in the neutral mode instead. The measurement and
// Digitise modes are excluded because there a click means something else.
if (currentMode !== 'none') {
return; return;
} }
if (feature) { if (feature) {
// Clicked on existing marker - select it and show details
console.log('[MapClick] Clicked on marker:', feature.getId()); console.log('[MapClick] Clicked on marker:', feature.getId());
mapView.selectMarker(feature); mapView.selectMarker(feature);
showLocationDetails(feature); showLocationDetails(feature);
} else { } else {
// Clicked on empty space - show add location popup at click position // Empty space in the neutral mode does nothing: a stray tap on the map
console.log('[MapClick] Empty space → Add Location popup'); // should not open a form.
mapView.clearSelection(); mapView.clearSelection();
mapView.showAddLocationPopup(evt.coordinate);
} }
}); });
@ -374,6 +374,17 @@ async function initApp() {
} }
}); });
// Placing a point with the Draw ▸ Point tool opens the Add Location form at
// that coordinate. This is the single route to a new point: the tool replaced
// the separate Add Location dock button, so there is one gesture rather than
// two ways to put a point on the map that behaved differently.
mapView.onDrawnPoint((coordinate) => {
if (IS_EMBED_PERMIT) return;
console.log('[App] Point drawn → Add Location popup');
mapView.clearSelection();
mapView.showAddLocationPopup(coordinate);
});
// Set up handler for the map add location popup form // Set up handler for the map add location popup form
mapView.onAddLocation(async (data) => { mapView.onAddLocation(async (data) => {
console.log('[App] Add location from map popup:', data); console.log('[App] Add location from map popup:', data);
@ -660,7 +671,6 @@ function initUI() {
// Mode Selector & Measurement Tools (Bottom Dock) // Mode Selector & Measurement Tools (Bottom Dock)
// ============================================ // ============================================
const addLocationBtn = document.getElementById('dock-btn-add-location');
const measureCircleBtn = document.getElementById('dock-btn-measure-circle'); const measureCircleBtn = document.getElementById('dock-btn-measure-circle');
const measureLineBtn = document.getElementById('dock-btn-measure-line'); const measureLineBtn = document.getElementById('dock-btn-measure-line');
const measureAreaBtn = document.getElementById('dock-btn-measure-area'); const measureAreaBtn = document.getElementById('dock-btn-measure-area');
@ -685,7 +695,6 @@ function initUI() {
// Debug: Check if buttons are found // Debug: Check if buttons are found
console.log('[initUI] Buttons found:', { console.log('[initUI] Buttons found:', {
addLocation: !!addLocationBtn,
measureCircle: !!measureCircleBtn, measureCircle: !!measureCircleBtn,
measureLine: !!measureLineBtn, measureLine: !!measureLineBtn,
measureArea: !!measureAreaBtn, measureArea: !!measureAreaBtn,
@ -694,7 +703,7 @@ function initUI() {
}); });
// All mode buttons (mutually exclusive) // All mode buttons (mutually exclusive)
const modeButtons = [addLocationBtn, measureCircleBtn, measureLineBtn, measureAreaBtn, drawBtn]; const modeButtons = [measureCircleBtn, measureLineBtn, measureAreaBtn, drawBtn];
// Helper to set active mode and update button states // Helper to set active mode and update button states
// Note: This updates the module-level currentMode variable // Note: This updates the module-level currentMode variable
@ -716,8 +725,9 @@ function initUI() {
mapView?.setEditMode(false); mapView?.setEditMode(false);
} }
// Hide add location popup when leaving addLocation mode // The Add Location form is raised by the Draw ▸ Point tool, so it belongs
if (mode !== 'addLocation') { // to Digitise mode and is dismissed on the way out of it.
if (mode !== 'draw') {
mapView?.hideAddLocationPopup(); mapView?.hideAddLocationPopup();
} }
@ -735,25 +745,9 @@ function initUI() {
case 'draw': case 'draw':
mapView?.setEditMode(true); mapView?.setEditMode(true);
break; break;
// addLocation mode doesn't need tool activation
} }
}; };
// Add Location mode button — toggles. Clicking it once activates the
// mode (subsequent map-clicks open the Add-Location popup); clicking it
// again returns to the neutral 'none' mode so accidental map taps don't
// trigger the popup.
if (addLocationBtn) {
addLocationBtn.addEventListener('click', () => {
console.log('[Button] Add Location clicked, currentMode is:', currentMode);
if (currentMode === 'addLocation') {
setMode('none', null); // toggle off
} else {
setMode('addLocation', addLocationBtn); // activate
}
});
}
// Circle measurement button // Circle measurement button
if (measureCircleBtn) { if (measureCircleBtn) {
measureCircleBtn.addEventListener('click', () => { measureCircleBtn.addEventListener('click', () => {

View File

@ -107,7 +107,13 @@
// base-map buttons on the right and clipped by the dock. It now sits above // base-map buttons on the right and clipped by the dock. It now sits above
// that whole right-edge stack (bottom: 196px = the switcher's 144px anchor // that whole right-edge stack (bottom: 196px = the switcher's 144px anchor
// + its 44px height + an 8px gap). // + its 44px height + an 8px gap).
const CACHE_VERSION = 'v19'; // v20: One way to add a point instead of two. The Add Location dock button is
// gone; placing a point with Draw > Point now opens the same Add Location
// form. The sketch is discarded so the marker is the only record, and the
// add/remove pair is kept off the undo stack. Marker details, which used
// to need Add Location mode, now open in the neutral mode. The dock keeps
// its six remaining buttons and divides them evenly on narrow screens.
const CACHE_VERSION = 'v20';
const SHELL_CACHE = `shell-${CACHE_VERSION}`; const SHELL_CACHE = `shell-${CACHE_VERSION}`;
const MODULES_CACHE = `modules-${CACHE_VERSION}`; const MODULES_CACHE = `modules-${CACHE_VERSION}`;
const API_CACHE = `api-${CACHE_VERSION}`; const API_CACHE = `api-${CACHE_VERSION}`;

View File

@ -523,6 +523,46 @@ export class MapView {
}); });
this.map.addControl(this.editBar); this.map.addControl(this.editBar);
// 5a. Draw ▸ Point is the single way to add a Location.
// There used to be two ways to put a point on the map — the Add Location
// dock button and this tool — which produced two different kinds of
// point from the officer's perspective. The tool now raises the same
// event the dock button did, and the sketch it leaves behind is taken
// straight back out again: the marker created by the Add Location form
// is the record, so keeping the sketch as well would show the same place
// twice, and would linger if the officer cancelled the form.
const drawPoint = this.editBar.getInteraction('DrawPoint');
if (drawPoint) {
drawPoint.on('drawend', (evt) => {
const coordinate = evt.feature?.getGeometry?.()?.getCoordinates?.();
if (!coordinate) return;
// ol adds the sketch to the source *after* this handler returns, so the
// add and its removal straddle a tick. Recording has to be suppressed
// across both: suppressing only the removal leaves the add on the undo
// stack, and pressing Undo would then resurrect a point the officer
// never kept.
const undoRedo = this._undoRedo;
const wasRecording = undoRedo?._record;
if (undoRedo) undoRedo._record = false;
setTimeout(() => {
try {
this.drawingsSource.removeFeature(evt.feature);
} catch {
// Already gone (e.g. the layer was cleared) — nothing to undo.
} finally {
if (undoRedo) undoRedo._record = wasRecording;
}
}, 0);
for (const cb of this._drawnPointCallbacks || []) {
try { cb(coordinate); } catch (err) {
console.warn('[MapView] onDrawnPoint callback failed:', err);
}
}
});
}
// 5b. Persistent vertex overlay — when edit mode is active and the user // 5b. Persistent vertex overlay — when edit mode is active and the user
// selects a polygon (or line) for modification, render a small dot // selects a polygon (or line) for modification, render a small dot
// at every vertex so the user can see all editable nodes at a glance. // at every vertex so the user can see all editable nodes at a glance.
@ -2732,6 +2772,19 @@ export class MapView {
this._featureModifiedCallbacks.push(callback); this._featureModifiedCallbacks.push(callback);
} }
/**
* Register a callback fired when the officer places a point with the
* Draw Point tool. Callback receives the map coordinate; the sketch itself
* has already been discarded, so the callback owns what happens next in
* this application, opening the Add Location form.
*
* @param {Function} callback - (coordinate) => void
*/
onDrawnPoint(callback) {
if (!this._drawnPointCallbacks) this._drawnPointCallbacks = [];
this._drawnPointCallbacks.push(callback);
}
/** /**
* Register a double-click callback. * Register a double-click callback.
* Callback receives (lon, lat, feature, event). * Callback receives (lon, lat, feature, event).