System Architecture

Django Apps

  • apps.core: home, shared map config, root templates
  • apps.authentication: auth/profile/role support
  • apps.parcels: parcel domain model, views, APIs, upload/export, analytics

Frontend Module Layout (Parcels)

  • static/parcels/js/map/*: map init/data/render/controls/selection
  • static/parcels/js/map/raster-layers.js: basemap + DEM/landcover panel controls and overlay layering
  • static/parcels/js/analytics/*: statistics dashboard behavior
  • static/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_view handles 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.html
  • templates/parcels/parcel_map.html
  • templates/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 _index and _icon.
  • Leaflet.draw 1.0.4 binds a vertex marker's ordinary click event to handler._onMarkerClick, which deletes that vertex. The adapter removes that listener while precision selection is active. An ordinary click selects the vertex, while Shift + 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 LatLng objects that existed when editing began. Calling Leaflet setLatLngs() during precision Apply replaces those arrays and can detach the refreshed handles from the polygon. Existing-geometry precision writes must mutate the selected LatLng in 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 _poly and _markers.
  • Active drawing coordinates are a flat LatLng[]. A precision write must preserve that shape, update both _poly and the corresponding temporary marker, and must not serialize the unfinished polygon.
  • An active handler's _poly.editing object may exist before its private _verticesHandlers have 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 as Cannot 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:

  1. Record the current and proposed versions. Change the JavaScript and CSS URLs in all three templates together. Do not deploy a mixed-version state.
  2. 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.
  3. Compare the ordering and payloads of draw:editstart, draw:editvertex, draw:drawvertex, draw:drawstop, and draw:created. Also verify how Enter and Escape keydown and keyup events interact with the active draw handler.
  4. 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 by updateMarkers() must be rebound before user interaction, and disabling precision integration must restore Leaflet.draw's original behavior.
  5. 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.
  6. 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.
  7. Confirm that handle refresh is called only when edit handlers are fully initialized. No path may dereference a missing _verticesHandlers array.
  8. 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

  1. Run tests/e2e/precision-coordinates.spec.ts through the guarded .env.e2e workflow against the disposable database and a freshly started --noreload Django server. Do not rely on an already-running development server, which may serve stale static JavaScript.
  2. 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.
  3. 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 leaked Escape keyup, 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.