System Architecture¶
Django Apps¶
apps.core: home, shared map config, root templatesapps.authentication: auth/profile/role supportapps.parcels: parcel domain model, views, APIs, upload/export, analytics
Frontend Module Layout (Parcels)¶
static/parcels/js/map/*: map init/data/render/controls/selectionstatic/parcels/js/map/raster-layers.js: basemap + DEM/landcover panel controls and overlay layeringstatic/parcels/js/analytics/*: statistics dashboard behaviorstatic/parcels/js/shared/*: selection store, form lock, style settings, icons, side panels
Selection Architecture¶
- Single tab-scoped selection store (
sessionStorage) - Shared by map and list/search workflows
- Emits change events to synchronize UI components
Map Data Architecture¶
parcel_geojson_viewhandles viewport requests- Uses
services.get_viewport_map_data(...) - At low zoom/high density: returns centroid-grid summaries
- At high zoom/lower density: returns individual parcel geometries
Spatial Computation Strategy¶
Preference is PostGIS-native operations where available:
- selection predicates (
intersects,within, centroid-in-shape) - area calculations (
ST_Area, transformed CRS) - centroid bucketing and grid summaries
Fallback Python paths exist for non-PostGIS local contexts.
Overlap Governance¶
apps.parcels.overlap_governance is the shared policy service for ordinary and
grouped workflows. It uses projected PostGIS candidates, symmetric topology
tolerances, validity intervals, declared-source exclusions, and bounded legacy
exemptions. Submit, review, approval, versioning, and the database trigger share
one transaction-scoped dataset advisory lock. Browser maps consume only WGS84
advisory geometry through map-overlap-conflicts.js; they never alter proposal
geometry.
Precision Coordinate Editing¶
ParcelApp.PrecisionCoordinates is a map-level controller shared by read-only
and editable Leaflet surfaces. It owns the coordinate toggle, label refresh,
selection, keyboard handling, projected-coordinate dialog, and generic
lmstool:geometrymodified event. Host workflows register layers and retain
their existing dirty-state, preview, validation, and persistence behavior.
Leaflet.draw private marker and active-handler access is confined to
leaflet-draw-precision-adapter.js. Treat that file as the upgrade boundary
when changing Leaflet.draw. The controller consumes the installation-provided
projected CRS and uses WGS84 only for transient Leaflet rendering.
The snapping controller owns manual coordinate pins. A precision write releases an ordinary anchor, refuses trace-bound vertices, invalidates pending canonicalization, and pins every resulting coordinate so automatic whole-layer canonicalization cannot silently replace it.
Leaflet.draw Upgrade Boundary¶
The precision adapter integrates with private Leaflet.draw APIs because
Leaflet.draw does not expose a public abstraction for selecting and rewriting
individual edit and in-progress drawing vertices. The application currently
pins Leaflet.draw 1.0.4 in:
templates/parcels/parcel_create.htmltemplates/parcels/parcel_map.htmltemplates/parcels/parcel_topology_request_detail.html
An upgrade must therefore be treated as a compatibility change, not as a
routine CDN version replacement. Private API access must remain confined to
apps/parcels/static/parcels/js/shared/leaflet-draw-precision-adapter.js.
Do not copy these assumptions into page scripts or patch Leaflet.draw
prototypes globally.
The adapter currently depends on the following Leaflet.draw implementation details:
- Existing-geometry edit markers are reached through
layer.editing._verticesHandlers, each handler's_markers, and each marker's_indexand_icon. - Leaflet.draw
1.0.4binds a vertex marker's ordinaryclickevent tohandler._onMarkerClick, which deletes that vertex. The adapter removes that listener while precision selection is active. An ordinary click selects the vertex, whileShift+ click delegates to the original deletion callback. layer.editing.updateMarkers()destroys and recreates the edit-marker objects and their event listeners. The adapter's handle-refresh notification must therefore rebind precision selection to the replacement markers and carry the selected ring/index onto the replacement handle. Otherwise, the first click after Apply invokes Leaflet.draw's restored deletion behavior.- An active Leaflet.draw editor retains references to the coordinate arrays and
LatLngobjects that existed when editing began. Calling LeafletsetLatLngs()during precision Apply replaces those arrays and can detach the refreshed handles from the polygon. Existing-geometry precision writes must mutate the selectedLatLngin place, rebuild the layer bounds, redraw, and only then refresh handles. The controller must snapshot the old coordinate before that in-place mutation so event payloads remain accurate. - The active polygon drawing handler is reached through
drawControl._toolbars.draw._modes.polygon.handler; its unfinished geometry and temporary handles are stored in_polyand_markers. - Active drawing coordinates are a flat
LatLng[]. A precision write must preserve that shape, update both_polyand the corresponding temporary marker, and must not serialize the unfinished polygon. - An active handler's
_poly.editingobject may exist before its private_verticesHandlershave been initialized. Consequently, refreshing completed-geometry edit handles is deliberately a no-op for active drawing. Calling the normal edit refresh at this stage can produce errors such asCannot read properties of undefined (reading 'length'). - First- and last-marker clicks retain Leaflet.draw's shape-completion semantics. Earlier temporary drawing vertices are selected by hover, and the newest vertex is selected automatically, so precision selection does not replace completion behavior.
Leaflet.draw Upgrade Procedure¶
Use this procedure for every Leaflet.draw version change:
- Record the current and proposed versions. Change the JavaScript and CSS URLs in all three templates together. Do not deploy a mixed-version state.
- Review the exact, unminified source for the proposed version. Confirm or
replace every private field and callback listed above, including the callback
context expected by
_onMarkerClick. - Compare the ordering and payloads of
draw:editstart,draw:editvertex,draw:drawvertex,draw:drawstop, anddraw:created. Also verify howEnterandEscapekeydown and keyup events interact with the active draw handler. - Confirm that edit-marker listener removal and restoration are symmetrical.
A plain click must select without deleting,
Shift+ click must delete exactly once, replacement markers created byupdateMarkers()must be rebound before user interaction, and disabling precision integration must restore Leaflet.draw's original behavior. - Confirm that active-handler discovery, temporary marker ordering, and marker indices still match the unfinished geometry. Test both new polygons and split lines rather than assuming they share identical handler state.
- Confirm that completed polygon coordinates and unfinished drawing coordinates retain their expected nested or flat array shapes after a precision write. On existing geometry, confirm Apply preserves the active editor's coordinate-array identity and that dragging the refreshed handle moves the corresponding polygon corner.
- Confirm that handle refresh is called only when edit handlers are fully
initialized. No path may dereference a missing
_verticesHandlersarray. - Run the focused browser-module tests:
bash
node --test \
tests/node/precision_coordinates_behavior.test.mjs \
tests/node/leaflet_draw_precision_adapter_behavior.test.mjs \
tests/node/snapping_drawing_adapter_behavior.test.mjs \
tests/node/topology_split_line_map_behavior.test.mjs \
tests/node/precision_coordinates_performance.test.mjs
- Run
tests/e2e/precision-coordinates.spec.tsthrough the guarded.env.e2eworkflow against the disposable database and a freshly started--noreloadDjango server. Do not rely on an already-running development server, which may serve stale static JavaScript. - Manually verify the supported Firefox ESR interaction matrix: existing-polygon select, delete, drag, coordinate apply, cancel, and nudge; active-polygon newest-vertex selection, earlier-vertex hover selection, apply, cancel, continue drawing, and finish drawing; split-line editing; coordinate-label toggle and zoom threshold; snapping pins; and rejection of trace-bound precision writes.
- Inspect the browser console throughout the manual checks. Acceptance
requires no vertex deletion on an ordinary selection click, no duplicate
deletion on
Shift+ click, no draw cancellation caused by a leakedEscapekeyup, no undefined private-handler errors, and no automatic snapping substitution after an accepted manual coordinate.
If the proposed version no longer exposes compatible internals, update only the adapter and its tests, or replace the integration with a supported public API. The adapter should fail closed: precision selection may become unavailable, but ordinary Leaflet.draw creation and editing must continue to work. Roll back the Leaflet.draw JavaScript, CSS, adapter, and related tests as one unit if this condition cannot be met. Do not release a partial upgrade that preserves the coordinate dialog but changes vertex selection, deletion, or geometry shape semantics.