6 Commits

Author SHA1 Message Date
adbf9ba211 Merge the Shapefile export fix and the startup-loading fix
Two branches from parallel sessions, merged in that order because the first
removes shp-write and with it JSZip, which the second was working around.

Conflicts resolved by hand in vite.config.js: the JSZip chunk rule the second
branch added is dropped, since nothing reaches JSZip any more — shapefiles are
written by src/shapefile-write.js and shpjs 6 unzips with but-unzip. Vite's
preload-helper rule stays, which is what keeps the geotiff chunk out of the
startup preload. dist/ was rebuilt from scratch rather than merged, its hashed
file names being a conflict on every file.

Service Worker v26 now records both changes.

Verified on the merged tree: a production build preloads only the entry,
vite-helpers, bootstrap, openlayers and ol-ext — geotiff, shpjs and pako are
gone from startup, and Analyse still fetches its chunk and turf on first use.
Shapefile export, driven through the Export GIS modal, writes a zip holding the
point and polygon sets with their .prj and .cpg.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-23 13:48:02 +02:00
8674ca4df2 Build: stop the lazy geotiff and shapefile chunks loading at startup
vite.config.js splits geotiff and shpjs out so they load only when a user opens
a Cloud-Optimized GeoTIFF or imports a shapefile, but the built index.html
preloaded both on every visit — about 428 kB (152 kB gzipped) nobody had asked
for.

Two separate causes, both visible in the entry chunk's first line:
- it imported Vite's preload helper, used by every dynamic import(), from the
  geotiff chunk. The helper is a virtual module with no node_modules path, so
  Rollup had parked it there; needing it at startup dragged the whole chunk in.
  It now has a chunk of its own, about 1 kB.
- it imported JSZip from the shpjs chunk. JSZip reached the bundle through
  shp-write, which gis-export.js imports for Shapefile export, so it is needed
  at startup whatever happens; filing it under shpjs pulled that chunk in too.
  It is now its own chunk, costing the 31 kB it actually is.

dist/ rebuilt: index.html now preloads vite-helpers and jszip instead.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-23 13:40:13 +02:00
8d1d874ad7 Shapefile export: write the format ourselves instead of shp-write
Choosing Shapefile in the Export GIS modal produced no file. shp-write@0.3.2
cannot run in this build, in three separate ways: its dbf dependency assigns an
undeclared variable, which was fine in the browserify bundle it was written for
but is a ReferenceError under the strict mode ES modules always use; its zip.js
branches on process.browser, which does not exist in a browser; and its
download.js delivers the archive by navigating to a data:application/zip URL,
which Chromium blocks in the top frame.

- src/shapefile-write.js writes .shp/.shx/.dbf/.prj/.cpg itself: geometries
  split by type into up to three layers, polygon rings re-wound clockwise as
  the format requires, DBF column types inferred per column, names sanitised
  to DBF's 10 characters and de-duplicated, UTF-8 declared via .cpg.
- src/zip-store.js holds the STORED-ZIP writer that was inside analysis/xlsx.js,
  so both writers share it. Pure-ASCII archives are byte-for-byte as before.
- shp-write, and with it dbf and JSZip 2.5.0, leave the dependency tree; the
  build no longer warns that node's "assert" was externalised.
- Service Worker v26 for the new bundle.

Verified in a browser: the Export GIS modal, driven through its own controls
with Web Mercator features, produced area_analysis.zip holding both layers with
WGS84 coordinates, file code 9994 and the right shape types. The archive reads
back through the application's own shpjs importer with geometries, numeric,
text and boolean attributes intact. The zonal-statistics .xlsx export, which now
shares the zip writer, still opens and reads correctly.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-23 13:40:00 +02:00
26c941b09a Analytical tools Stage 1 + drawing-tool fixes
Stage 1 of the GIS Analytical Tools concept — client-side spatial analysis:

- src/analysis/overlay.js: vector overlays (intersect / clip / difference /
  union-dissolve) on Turf.js, reprojecting to WGS84 and merging attributes
  (intersect keeps both layers', clip keeps only A's).
- src/analysis/zonal.js: vector-in-vector zonal statistics — count / sum /
  mean / min / max / total area per zone, with centroid-in-zone (default) or
  any-overlap membership.
- Bounding-box pre-filtering in both: only genuinely overlapping pairs reach
  the expensive geometry test. 43 zones x 25,004 parcels now completes in
  ~106 ms; previously it was refused as too large.
- src/analysis-modal.js + markup: Analysis panel with "Apply to" scoping —
  whole layer, current map view, selected features, or the catch of a drawn
  Circle/Area. Reached from a new "Analyse" dock button.
- MapView.addCOGLayer() for Cloud-Optimized GeoTIFF display (WebGLTile +
  GeoTIFF source, imported lazily); listVectorLayers(); getSelectedFeatures().
- Circle/Area analysis popup: one "Export" button (PDF folded into the export
  modal as a fourth format, field-rename table hidden for it) plus an
  "Analyse" button that opens the panel pre-scoped to the intersecting
  features.
- vite.config.js: code-split turf, geotiff and pako so the eager bundle is
  unchanged (~283 kB). Giving pako its own chunk also fixes a circular chunk
  between jspdf and geotiff, which share it via fast-png.

Drawing-tool fixes carried in the same working tree:

- Delete/Backspace key deletes the selection via the EditBar's Delete
  interaction (same undoable block as the button).
- Multi-select: shift-click toggles, Ctrl/Cmd-drag box-selects; helper layers
  (vertex overlay, GPS) excluded.
- Undo: split/merge/divide wrapped in undo blocks so one press reverses the
  whole operation; vertex-overlay churn no longer pollutes the undo stack;
  sources are re-scanned and the stack cleared when edit mode is entered, so
  deletes on sub-grouped layers are undoable. NOTE: the undo behaviour is not
  yet confirmed on-device.
- ol-ext TouchCursor gated to touch-only devices, so hybrid touchscreen
  laptops keep the normal cursor.

Service worker v12 -> v13.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 14:09:06 +02:00
ef12e4477b Offline tile cache, polygon Divide, topographic layer integrations
Major feature batch covering drawing-tool improvements, layer additions,
and offline-first capabilities. Largest changes in MapView.js (+1700),
main.js (+1500), public/sw.js (+367), and new modules under src/.

Drawing & editing toolkit
  * Polygon Divide tool — sub-button under Split, divides a polygon into
    N equal-area pieces via binary search; user picks the cutting edge
  * UPN pick phase after Split and Divide — non-picked pieces have their
    identifier fields cleared automatically
  * Improved Merge algorithm — vertex-to-edge proximity (5 m tol.) with
    hybrid lockstep extension; bold A/B labels on selected polygons
  * Persistent vertex highlights — all vertices of the selected polygon
    rendered as dots while edit mode is on, without subclassing ol-ext
  * Toast notifications for merge/split/divide outcomes
  * Shapefile import — addGeoJSONLayer now includes an image style so
    Point features render (previously invisible)

Background & overlay layers
  * DEAfrica Coastlines v0.4 (WMS) in Biophysical Environment
  * DEAfrica Slope (SRTM 30m, style_slope) — semi-transparent background
  * Contours hillshade — get_contours_hillshade.php → local SQLite cache
  * OSM_roads — get_osm_roads.php → local SQLite cache, casing-stroke
    style (black 3.5 px outer, #F0F1F0 1.5 px inner)
  * External Source dialog — green + button in LayerSwitcher lets users
    add WMS / WFS / XYZ layers at runtime
  * Generic addWMSLayer / addXYZLayer with style, opacity, zIndex,
    legendUrl, onlineOnly options
  * TileWMS replaces ImageWMS (fixes 'Width exceeds 512' WMS errors)
  * Legend panel — bottom-right, auto-shown for visible layers that
    register a legendUrl
  * Default base map setting in Settings, persisted in localStorage;
    setBaseMap() on MapView

Offline tile cache (Phase 1 + 2)
  * Service worker: per-host tile caches (osm / topo / satellite /
    carto-light / carto-dark), counter-based eviction to prevent
    iOS Safari memory-pressure reloads, GET_TILE_STATS /
    CLEAR_TILE_CACHES message API
  * pwa.js helpers: getActiveServiceWorker, onServiceWorkerControllerChange,
    getTileCacheStats, clearTileCaches, getStorageEstimate
  * Settings: Offline Map Tiles card with per-provider stats + clear
  * Phase 2 download dialog: form to pick base map, area (current view /
    district / Ghana), zoom range; live tile-count + size estimate;
    progress bar with cancel; OfflineTileDownloader class with
    concurrency + throttling

Local database management
  * osm_roads table + saveOSMRoads / getLocalOSMRoads helpers
  * CACHED_LAYER_TABLES allow-list with clearTable / clearAllCachedLayers
  * Local Database Tables card: per-row Clear button (cached layers
    only) + 'Refresh cached layers' header button with reload prompt

Build & infrastructure
  * Shpjs lazy-loaded via dynamic import (saves ~140 kB from initial JS)
  * chunkSizeWarningLimit raised to 900 kB (openlayers + sqlite3.wasm
    can't be split further)
  * Toast notification module (src/toast.js)
  * Units module (src/units.js) for metric / imperial conversions
  * PDF export module (src/pdf-export.js)

Documentation & SQL
  * Topographic_Background_Layers_for_LUPMIS2.docx — research report
  * OpenTopography_Workflow.svg/.png — ETL pipeline diagram
  * LUPMIS2_Development_Status_Report.docx — April update section
  * sql/create_landuse_parcels.sql — PostgreSQL schema for the LUSPA
    land-use parcel specification (Feb 2026, revised), with PostGIS
    geometry column and standard indices

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 10:55:30 +02:00
3009a11b33 intial pwa LUPMIS2 commit 2026-03-04 12:59:40 +01:00