2 Commits

Author SHA1 Message Date
7d26c740df Contours layer via new API; Analyse guards; measurements read-only
Contours hillshade layer
- Wired to the new endpoint POST /api/contours/get_by_district.php. It sits in
  its own /contours module rather than /spatial_planning, so remotedb gained a
  resolveEndpoint() helper: a bare filename still resolves inside
  /spatial_planning, a path resolves against the API root. The endpoint also
  names the district parameter `districtid` (numeric, no underscore) unlike
  every other endpoint, which is an easy trap — documented at the call site.
- Each contour is labelled with its `elev` value. Labels are decluttered
  (addGeoJSONLayer gained a declutter passthrough, since OpenLayers only allows
  it at construction time) and gated by resolution, so the map shows an
  occasional elevation value rather than one per segment.
- Verified against the live endpoint: district 1 returns 2,676 features, 11
  elevation values at 10 m intervals, median feature ~50 m. The response uses
  the standard {success, data:[…]} envelope; the reader also tolerates a bare
  array.

Analyse: fail loudly instead of silently
- Overlays are polygon-only, but line inputs were being filtered out silently,
  producing an empty result and the misleading "the layers may not overlap".
  They now raise a message naming the geometry actually found and pointing at
  Zonal statistics. Union checks each layer separately, so a line layer paired
  with a polygon layer can no longer be dropped unnoticed.
- Zonal statistics returns warnings alongside the result — shown in the panel
  and written into the Excel workbook — for figures that are valid but easy to
  misread: total area over non-polygon inputs (always 0), and mean over line
  inputs, which is not an area-weighted average of the surface. The mean
  warning is restricted to lines; averaging over points is a normal statistic.

Measurements are read-only
- The Measurements layer carries selectable:false, honoured by both the Select
  interaction and box-select. Previously a measured circle showed vertex
  handles while the Digitise tool was active, and dragging one reshaped the
  measurement: the intersection test followed the new shape while the reported
  radius and area did not. Double-click analysis is unaffected — it hit-tests
  the map directly rather than going through Select.
- Circle Analysis now reads the radius from the geometry instead of the
  `_radius` snapshot taken at draw time, so the reported figure cannot drift
  from the area analysed (that figure also feeds the PDF export). The property
  is kept but marked as a snapshot, not a source of truth.

Documents
- New Buffer (Circle) tool user guide: the tool is named Circle rather than
  Buffer, it takes two clicks rather than a drag, and the analysis only appears
  on a double-click — the three things users were getting stuck on.
- New technician training plan: 12 topics over 60 days, derived from the
  technologies actually used in this code base, including Docker.
- Analyse guide: elevation per zone from the contours layer, with the
  min/max-versus-mean caveat.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-11 12:20:26 +02:00
da6f968725 Zonal-statistics Excel export, COG layer entry point, DE Africa ETL
Excel export (from TWG feedback)
- src/analysis/xlsx.js: a dependency-free XLSX writer — an .xlsx is a ZIP of
  XML, so this packs the required parts with a small stored-ZIP writer. Avoids
  SheetJS (stale npm package with advisories) and ExcelJS (heavy for an
  offline-first field app), and does not rely on the JSZip that only reaches us
  transitively via shp-write. Lazy-loaded as a ~5.6 kB chunk.
- After a zonal run the Analysis panel offers "Export table (Excel)", writing a
  two-sheet workbook: Results (figures as real numbers) and Parameters (zone
  and input layers with feature counts, the "Apply to" scope, membership rule,
  statistics, numeric field and export time) — so a table can be verified or
  reproduced later rather than being an unattributed set of numbers.
  The button is hidden for overlay runs and cleared when the mode changes.
- Verified against two independent readers: openpyxl loads it with zero
  warnings and correct numeric types, and LibreOffice Calc opens it as a
  spreadsheet. Also exercised end-to-end through the real zonal pipeline.

COG raster entry point
- Add External Layer gains a COG type alongside WMS/WFS/XYZ, with a URL
  pre-flight check that distinguishes a web page, a 404, a CORS block and a
  server without byte-range support — geotiff.js otherwise reports these only
  as an opaque "AggregateError: Request failed".

Digital Earth Africa ETL
- etl/deafrica_dem_to_minio.py exports a DE Africa DEM for a district as a COG
  and uploads it to the LUSPA MinIO bucket raster-objects, then verifies the
  object is anonymously readable and range-capable. Credentials come from the
  environment; the existing PHP integration hardcodes them and an earlier key
  pair reached Gitea. NOTE: not yet run against the Sandbox — start with
  --list-products to confirm the DEM product name.

Documents
- Concept note: Digital Earth Africa added as a raster source (§6.3), separating
  the live WMS route from the batch Sandbox export; corrected the in-house
  contour table's provenance to OpenTopography (gdal_contour over an SRTM 30 m /
  Copernicus 30 m DEM), and added the ToR §2.4.2 / FS §2 alignment chapter.
- TWG presentation, architecture and two integration workflow charts (SVG
  sources kept in the repo so they stay editable), and a user guide for the
  Analyse tools.

Service worker v13 -> v14. .gitignore: exclude Python bytecode from etl/.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 16:28:45 +02:00