1137 lines
46 KiB
Markdown
1137 lines
46 KiB
Markdown
# GeoIntel Backend (Sprint 3 foundation layer)
|
|
|
|
FastAPI backend for GeoIntel Kempen Foundation Sprints.
|
|
|
|
The map-first explorer uses the existing persisted vector selection endpoint. Its bounded GeoJSON preview reports `feature_count`, while `total_feature_count` reports the exact PostGIS intersection count before the 1,000-feature response cap. This keeps municipality-scale analysis honest without sending unbounded geometry to the browser.
|
|
|
|
## Scope implemented
|
|
- Project CRUD
|
|
- Area CRUD with PostGIS geometry
|
|
- Vector and raster dataset upload/registration
|
|
- Deterministic local storage metadata capture
|
|
- PostGIS migration and database foundation
|
|
- Job foundation for async-ready GIS operations
|
|
|
|
## Sprint 2 additions
|
|
- Dataset typing and lifecycle support:
|
|
- `uploaded`
|
|
- `validating`
|
|
- `ready`
|
|
- `failed`
|
|
- Vector metadata extraction:
|
|
- feature count
|
|
- geometry type summary
|
|
- bounds
|
|
- approximate area
|
|
- CRS and CRS assumption
|
|
- Raster metadata endpoint:
|
|
- returns raster profile when `rasterio` is available
|
|
- returns clear `RASTER_PROCESSING_UNAVAILABLE` error when dependency is missing
|
|
- Deterministic storage metadata capture:
|
|
- original filename
|
|
- stored filename
|
|
- MIME/content type
|
|
- size bytes
|
|
- checksum SHA-256
|
|
|
|
## Sprint 3 additions
|
|
- Lightweight job architecture:
|
|
- `jobs` table and migrations
|
|
- job create/list/read/status API
|
|
- synchronous execution behind job abstraction
|
|
- Vector operations foundation:
|
|
- inspect
|
|
- bbox
|
|
- stats
|
|
- clip by area
|
|
- buffer
|
|
- intersect
|
|
- invalid geometry rejection with typed errors
|
|
- Raster operation foundation:
|
|
- inspect
|
|
- metadata
|
|
- preview readiness
|
|
- clip by area (dependency-aware with unavailable fallback)
|
|
- tile generation with manifest output
|
|
- real preview image generation when dependencies are installed
|
|
|
|
## Sprint 4 additions
|
|
- Raster foundation is now implemented with real extraction and deterministic artifact outputs:
|
|
- metadata returns width, height, band count, CRS, bounds, resolution, dtype, nodata, transform
|
|
- preview endpoint generates and reuses PNG previews with width/height
|
|
- clip operation persists a derived raster dataset with:
|
|
- `source_dataset_id`
|
|
- `operation`
|
|
- `operation_parameters`
|
|
- tile operation writes deterministic raster tiles under `tiles/{project_id}/{source_dataset_id}/{tile_set_id}`
|
|
- tile manifest includes tile path, pixel window, bounds, transform, and count
|
|
- Dependency behavior:
|
|
- when `rasterio` is missing, raster processing returns `RASTER_PROCESSING_UNAVAILABLE`
|
|
- preview endpoint additionally requires numpy/pillow and returns `RASTER_PROCESSING_UNAVAILABLE` when missing
|
|
|
|
## Sprint 5 additions
|
|
- Raster analytics hardening:
|
|
- raster band statistics now include:
|
|
- min, max, mean, std
|
|
- nodata count and ratio
|
|
- valid pixel count
|
|
- dtype
|
|
- optional histogram bins (default 16 bins)
|
|
- raster reproject operation implemented (CRS transform + rasterio reprojection) using dependency-aware raster processing checks.
|
|
- reproject failures are explicit (`INVALID_PARAMETERS`, `INVALID_DATASET_CRS`, `RASTER_PROCESSING_UNAVAILABLE`).
|
|
- Raster clip and tile hardening:
|
|
- clip validates area presence and CRS alignment constraints.
|
|
- tile manifest records `tile_set_id`, `tile_size`, `overlap`, `source_dataset_id`, `source_raster_id`, bounds, parameters, count, tile paths, `ai_inference`, and `tile_server`.
|
|
- Job result persistence for raster ops:
|
|
- raster clip/reproject/tile job payloads persist derived dataset references when outputs are produced.
|
|
|
|
## Sprint 6 additions
|
|
- Added local spectral index operations:
|
|
- NDVI endpoint: `POST /raster/indices/ndvi`
|
|
- NDWI endpoint: `POST /raster/indices/ndwi`
|
|
- NDBI endpoint: `POST /raster/indices/ndbi`
|
|
- Spectral index input validation:
|
|
- band parameters must be positive integers
|
|
- band parameters must exist in source raster band count
|
|
- Dependency-aware execution:
|
|
- returns `RASTER_PROCESSING_UNAVAILABLE` when rasterio or numpy are unavailable
|
|
- Real index output handling:
|
|
- local windowed float32 GeoTIFF generation
|
|
- `NaN` strategy for invalid pixels / division by zero
|
|
- Provenance capture for derived index datasets:
|
|
- `source_dataset_id`, `operation`, `band_mapping`, `formula`
|
|
- `output_dtype`, `nodata_strategy`, `value_range_note`
|
|
- `output_dataset_id`, `created_at`, `path`
|
|
|
|
## Sprint 7B additions
|
|
- Added provider registry skeleton for `grb`, `osm`, `manual` and `fixture`.
|
|
- Added provider capability endpoints:
|
|
- `GET /api/v1/external/providers`
|
|
- `GET /api/v1/external/providers/{provider_name}`
|
|
- `GET /api/v1/external/providers/{provider_name}/layers`
|
|
- `GET /api/v1/external/providers/{provider_name}/status`
|
|
- `POST /api/v1/external/providers/{provider_name}/import`
|
|
- GRB and OSM imports return explicit `not_configured` responses; no live WFS or Overpass calls are made.
|
|
- Manual and fixture providers describe existing upload/fixture flows only.
|
|
- Added live PostGIS migration smoke script for environments with a real database:
|
|
|
|
```bash
|
|
bash scripts/live_migration_smoke.sh
|
|
```
|
|
|
|
## Sprint 8 additions
|
|
- Added Detection Lab foundation:
|
|
- `detections` ORM model and Alembic migration with PostGIS geometry storage.
|
|
- hardened `analysis_runs` for dataset/job/model/result metadata.
|
|
- model registry capability service for `yolo-placeholder` and `manual-fixture-detector`.
|
|
- detection service boundary for creating jobs, analysis runs and dependency-aware unavailable responses.
|
|
- Added detection endpoints:
|
|
- `GET /api/v1/detection/models`
|
|
- `GET /api/v1/detection/model-assets`
|
|
- `POST /api/v1/detection/run`
|
|
- `GET /api/v1/detection/runs/{analysis_run_id}`
|
|
- `GET /api/v1/detection/runs/{analysis_run_id}/detections`
|
|
- YOLO/PyTorch real inference is not enabled in Sprint 8.
|
|
- Fixture detector mode is test/demo-only and requires explicit `fixture_mode=true`.
|
|
|
|
## Sprint 8B additions
|
|
- Added optional configured YOLO integration foundation:
|
|
- `yolo-configured` model registry capability.
|
|
- import-safe adapter for local Ultralytics model files.
|
|
- raster tile manifest validation and tile limit enforcement.
|
|
- pixel bbox to EPSG:4326 detection polygon conversion.
|
|
- persisted detections through the existing detection/job/analysis-run path.
|
|
- YOLO dependencies are optional extras and are not required for backend startup.
|
|
- GeoIntel does not download YOLO model weights automatically.
|
|
|
|
## Sprint 8C additions
|
|
- Added detection visualization/review API support:
|
|
- list detection runs
|
|
- list detections by run or dataset with class/confidence filters
|
|
- get detection detail
|
|
- return persisted detections as GeoJSON FeatureCollections
|
|
- Added detection QA against reference vector datasets:
|
|
- compares persisted detection geometries against persisted `vector_features`
|
|
- persists `quality_checks` and `metrics`
|
|
- returns precision, recall, F1, mean IoU and false positive/negative counts
|
|
- configured-YOLO runs clip both QA populations to persisted tile-manifest
|
|
coverage before matching and fail closed on missing/mismatched coverage
|
|
provenance
|
|
- persists a diagnostic-only candidate-box versus reference-envelope pass so
|
|
box-to-footprint matching artifacts are visible without altering canonical
|
|
footprint-IoU metrics
|
|
- Segmentation, LiDAR, AI Copilot, Training Studio and Reports remain out of scope.
|
|
|
|
## Sprint 9 additions
|
|
- Added Segmentation Lab foundation:
|
|
- `segmentations` ORM model and Alembic migration with PostGIS MultiPolygon geometry storage.
|
|
- segmentation model registry capabilities for `segmentation-placeholder`, `fixture-segmenter`, `yolo-seg-configured` and `sam-configured`.
|
|
- segmentation service boundary for creating jobs, analysis runs and unavailable model responses.
|
|
- explicit fixture segmenter mode for tests/demo fixtures only.
|
|
- Added segmentation endpoints:
|
|
- `GET /api/v1/segmentation/models`
|
|
- `POST /api/v1/segmentation/run`
|
|
- `GET /api/v1/segmentation/runs`
|
|
- `GET /api/v1/segmentation/runs/{analysis_run_id}`
|
|
- `GET /api/v1/segmentation/runs/{analysis_run_id}/segmentations`
|
|
- `GET /api/v1/segmentation/runs/{analysis_run_id}/geojson`
|
|
- `POST /api/v1/segmentation/runs/{analysis_run_id}/qa/reference`
|
|
- Real SAM and YOLO-seg inference are not enabled in Sprint 9.
|
|
- Mask paths are provenance/debug artifacts; persisted PostGIS geometry is authoritative for QA, map display and GeoJSON.
|
|
|
|
## Sprint 17 additions
|
|
- Added export foundation backed by the existing `exports` table.
|
|
- GeoJSON exports now persist export records and write JSON artifacts for:
|
|
- vector datasets
|
|
- detection analysis runs
|
|
- segmentation analysis runs
|
|
- Added project metadata JSON export for project, dataset and QA/QC summary state.
|
|
- Added export read/list/content endpoints:
|
|
- `POST /api/v1/exports/geojson`
|
|
- `POST /api/v1/exports/metadata`
|
|
- `GET /api/v1/exports/projects/{project_id}/exports`
|
|
- `GET /api/v1/exports/{export_id}`
|
|
- `GET /api/v1/exports/{export_id}/content`
|
|
- Exported detection and segmentation GeoJSON is generated from persisted first-class geometry rows.
|
|
- No new migrations, product lines, live providers or AI dependencies are introduced by this export pass.
|
|
- Old offline demo export artifacts can be inspected with `python scripts/cleanup_demo_artifacts.py`
|
|
and removed only with an explicit `--apply`. The script keeps the newest exports
|
|
per demo project, refuses to delete files outside `STORAGE_ROOT`, and blocks
|
|
apply runs above `--max-delete` until the cap is raised after a dry-run review.
|
|
Use repeated `--export-type` values to target only specific artifact kinds.
|
|
In Docker, use `docker compose exec -T backend python scripts/cleanup_demo_artifacts.py`.
|
|
- Live cleanup validation is available with `bash scripts/verify_demo_cleanup_dry_run.sh`.
|
|
It runs the same maintenance path without `--apply` and fails if the summary
|
|
reports anything other than a dry-run with zero deleted exports/files.
|
|
|
|
## Run locally
|
|
|
|
### Prerequisites
|
|
|
|
- Python 3.11+
|
|
- PostgreSQL with PostGIS
|
|
|
|
### Install dependencies
|
|
|
|
```bash
|
|
cd backend
|
|
python -m pip install -e .[dev]
|
|
```
|
|
|
|
Optional AI dependencies for configured local YOLO inference:
|
|
|
|
```bash
|
|
cd backend
|
|
python -m pip install -e .[ai]
|
|
```
|
|
|
|
Docker and Unraid builds keep AI dependencies disabled by default. To build an
|
|
image with local PyTorch/Ultralytics support, set:
|
|
|
|
```bash
|
|
GEOINTEL_INSTALL_AI=true
|
|
```
|
|
|
|
The default remains `false` so normal GIS deployments do not install the large AI
|
|
runtime. GeoIntel still requires an explicit local model path and never downloads
|
|
weights automatically.
|
|
|
|
AI-enabled Docker images include the native OpenCV runtime libraries required by
|
|
Ultralytics. Dependency availability is checked with real `torch` and
|
|
`ultralytics` imports, so missing shared libraries are reported as
|
|
`dependency_unavailable` instead of being treated as configured.
|
|
Docker/Unraid runtimes set `YOLO_CONFIG_DIR` to a writable storage path so
|
|
Ultralytics does not attempt to write settings under the root user config
|
|
directory.
|
|
|
|
Configured YOLO requires:
|
|
|
|
```bash
|
|
YOLO_ENABLED=true
|
|
YOLO_MODELS_DIR=/absolute/path/to/models
|
|
YOLO_MODEL_PATH=/absolute/path/to/local-model.pt
|
|
```
|
|
|
|
Optional local model compatibility smoke:
|
|
|
|
```bash
|
|
python scripts/yolo_preflight.py --model-path /absolute/path/to/local-model.pt --tile-manifest-path /absolute/path/to/manifest.json --check-model-load --json
|
|
```
|
|
|
|
In Docker, run the same smoke through the backend container:
|
|
|
|
```bash
|
|
docker compose exec -T backend python scripts/yolo_preflight.py --model-path /absolute/path/to/local-model.pt --tile-manifest-path /absolute/path/to/manifest.json --check-model-load --json
|
|
```
|
|
|
|
In the all-in-one Unraid runtime, place model files under the configured models
|
|
directory, mounted as `/app/models` by default:
|
|
|
|
```bash
|
|
GEOINTEL_MODELS_PATH=/mnt/user/appdata/geointel/models
|
|
YOLO_ENABLED=true
|
|
YOLO_MODELS_DIR=/app/models
|
|
YOLO_MODEL_PATH=/app/models/local-model.pt
|
|
```
|
|
|
|
The root helper can write those values safely after a local model is placed:
|
|
|
|
```bash
|
|
python scripts/configure_yolo_model.py \
|
|
--models-dir /mnt/user/appdata/geointel/models \
|
|
--env-file /mnt/user/appdata/geointel/.env \
|
|
--apply
|
|
```
|
|
|
|
When a promotion report recommends an exact model/tile/threshold candidate,
|
|
prefer the guarded activation helper. It validates the report, checks the local
|
|
model asset and writes `.env` only when `--apply` is supplied:
|
|
|
|
```bash
|
|
python scripts/activate_promoted_yolo_candidate.py \
|
|
--promotion-report /mnt/user/appdata/geointel/artifacts/detection-model-promotion/split-aware/aoi1024bg512r3e50-high-threshold-split-20260710T222934Z/detection_model_promotion_report.json \
|
|
--candidate-key 'geointel-building-yolov8s-aoi1024bg512r3e50-pt|512|64|0.35' \
|
|
--models-dir /mnt/user/appdata/geointel/models \
|
|
--env-file /mnt/user/appdata/geointel/.env \
|
|
--json
|
|
```
|
|
|
|
Add `--apply` only after reviewing the emitted env updates. The smoke and
|
|
activation helpers load no model by default, run no inference and do not
|
|
download weights. Restart or rebuild the runtime after applying because the
|
|
active model is read from `YOLO_MODEL_PATH`.
|
|
|
|
Operator-only local training preparation is available when real public model
|
|
candidates are too weak for the target imagery. It is not a browser feature and
|
|
does not change API contracts:
|
|
|
|
```bash
|
|
docker exec -it geointel python3 /app/scripts/export_operator_yolo_dataset.py \
|
|
--manifest-path /app/storage/operator-data/operator_samples_manifest.json \
|
|
--output-dir /app/storage/operator-data/yolo-building-dataset \
|
|
--val-samples turnhout \
|
|
--force
|
|
```
|
|
|
|
In an AI-enabled runtime with an existing local base model:
|
|
|
|
```bash
|
|
docker exec \
|
|
-e OPERATOR_YOLO_DATASET_DIR=/app/storage/operator-data/yolo-building-dataset \
|
|
-e YOLO_BASE_MODEL_PATH=/app/models/yolov8n.pt \
|
|
-e TRAIN_MODEL_OUTPUT_PATH=/app/models/geointel-building-detector.pt \
|
|
-e TRAIN_EPOCHS=8 \
|
|
-e TRAIN_IMGSZ=512 \
|
|
-e TRAIN_BATCH=2 \
|
|
-e TRAIN_WORKERS=0 \
|
|
-e TRAIN_DEVICE=cpu \
|
|
-e PYTHON_BIN=python3 \
|
|
geointel bash /app/scripts/train_operator_yolo_detector.sh
|
|
```
|
|
|
|
The exporter creates a YOLO `dataset.yaml` plus image/label folders from the
|
|
explicit operator sample manifest. The training wrapper writes
|
|
`training_summary.json` and a local `.pt` artifact, which still must be
|
|
validated through model preflight and the real-data QA matrix before use.
|
|
|
|
For a larger tile-level training set, use overlapping windows instead of one
|
|
image per AOI:
|
|
|
|
```bash
|
|
docker exec -it geointel python3 /app/scripts/export_operator_yolo_tile_dataset.py \
|
|
--manifest-path /app/storage/operator-data/operator_samples_manifest.json \
|
|
--output-dir /app/storage/operator-data/yolo-building-tile-dataset \
|
|
--tile-size 192 \
|
|
--stride 96 \
|
|
--negative-keep-ratio 0.5 \
|
|
--val-samples turnhout \
|
|
--force
|
|
```
|
|
|
|
Then point `OPERATOR_YOLO_DATASET_DIR` at
|
|
`/app/storage/operator-data/yolo-building-tile-dataset` and keep the same
|
|
training wrapper. Tile-level output remains operator tooling outside the V1
|
|
browser product.
|
|
|
|
Use `--samples` (or `OPERATOR_YOLO_SAMPLES`) when an experiment needs a
|
|
deliberate manifest subset. The generated summary records the source manifest
|
|
count plus selected and excluded sample slugs. Unknown samples and any selected
|
|
manifest holdout that is omitted from `--val-samples` fail before files are
|
|
written.
|
|
|
|
The backend also exposes a read-only model asset catalog for the mounted model
|
|
directory:
|
|
|
|
```bash
|
|
curl http://localhost:1202/api/v1/detection/model-assets
|
|
```
|
|
|
|
The catalog lists local `.pt`, `.onnx` and `.engine` files with size, SHA-256
|
|
and active-model status. Detection runs may submit `model_asset_id` with
|
|
`model_id="yolo-configured"` to use a cataloged local model for that run. The
|
|
backend resolves the ID to a file inside `YOLO_MODELS_DIR`; browser clients do
|
|
not send arbitrary model paths.
|
|
|
|
Configured YOLO inference uses raster tile artifacts from the existing tile
|
|
manifest flow. Single-band or otherwise non-RGB tile images are converted to a
|
|
temporary RGB prediction image before inference; georeferencing still comes
|
|
from the persisted tile manifest transform/bounds metadata.
|
|
|
|
Optional tuning:
|
|
|
|
```bash
|
|
YOLO_MODEL_ID=yolo-configured
|
|
YOLO_MODEL_DISPLAY_NAME="Configured YOLO detector"
|
|
YOLO_MODEL_VERSION=local-v1
|
|
YOLO_MODELS_DIR=/app/models
|
|
YOLO_CONFIG_DIR=/app/storage/ultralytics
|
|
YOLO_DEVICE=cpu
|
|
YOLO_IMAGE_SIZE=640
|
|
YOLO_MAX_TILES=100
|
|
YOLO_MAX_DETECTIONS=1000
|
|
YOLO_DUPLICATE_IOU_THRESHOLD=0.5
|
|
YOLO_BATCH_SIZE=1
|
|
```
|
|
|
|
### YOLO local preflight
|
|
|
|
Sprint 13 adds a local-only preflight for configured YOLO paths:
|
|
|
|
```bash
|
|
python scripts/yolo_preflight.py --model-path /absolute/path/to/local-model.pt --tile-manifest-path /absolute/path/to/manifest.json
|
|
```
|
|
|
|
Machine-readable output:
|
|
|
|
```bash
|
|
python scripts/yolo_preflight.py --model-path /absolute/path/to/local-model.pt --tile-manifest-path /absolute/path/to/manifest.json --json
|
|
```
|
|
|
|
To validate only local model/manifest paths on a machine without optional AI dependencies:
|
|
|
|
```bash
|
|
python scripts/yolo_preflight.py --model-path /absolute/path/to/local-model.pt --tile-manifest-path /absolute/path/to/manifest.json --assume-dependencies --json
|
|
```
|
|
|
|
The preflight checks configuration, dependency availability, local model file existence, tile manifest validity, tile count and referenced tile paths. JSON output also includes runtime diagnostics for the model directory, `YOLO_CONFIG_DIR`, installed `torch`/`ultralytics` versions and CUDA availability when dependency checks pass. It does not load a YOLO model, run inference or download weights.
|
|
|
|
`YOLO_MAX_DETECTIONS` is forwarded to Ultralytics as `max_det`. The default is
|
|
`1000` because dense building AOIs can exceed the upstream default cap of 300
|
|
detections before QA/QC can measure recall honestly.
|
|
|
|
`YOLO_DUPLICATE_IOU_THRESHOLD` controls GeoIntel-side cross-tile duplicate
|
|
suppression after YOLO pixel boxes are converted to EPSG:4326 polygons and
|
|
before `Detection` rows are persisted. Candidates are sorted by confidence per
|
|
class; lower-confidence same-class candidates with geometry IoU greater than or
|
|
equal to the threshold are suppressed. The default is `0.5`; set `0` to disable
|
|
this post-processing for debugging.
|
|
|
|
The same read-only status is available through the API and Detection Lab UI:
|
|
|
|
```bash
|
|
curl http://localhost:1202/api/v1/detection/yolo/preflight
|
|
```
|
|
|
|
To validate the full configured-YOLO runtime path against Docker/Tower after a
|
|
model is mounted and selected, run:
|
|
|
|
```bash
|
|
bash scripts/verify_model_asset_detection_workflow.sh http://192.168.10.150:1202
|
|
```
|
|
|
|
The smoke uses the existing demo raster to generate a tile manifest, selects a
|
|
cataloged local model asset, verifies read-only preflight, submits the existing
|
|
detection run endpoint and checks persisted AnalysisRun, Detection list and
|
|
Detection GeoJSON output. It does not download weights or inject detector
|
|
fixtures. A zero detection result is still a valid runtime smoke outcome on the
|
|
synthetic demo raster.
|
|
|
|
To validate the configured building model on operator-provided GIS data, mount
|
|
or copy a real georeferenced raster and a real reference-building GeoJSON onto
|
|
the runtime host, then run:
|
|
|
|
```bash
|
|
REAL_RASTER_PATH=/mnt/user/appdata/geointel/data/orthophoto.tif \
|
|
REAL_REFERENCE_VECTOR_PATH=/mnt/user/appdata/geointel/data/reference-buildings.geojson \
|
|
bash scripts/verify_real_data_detection_qa_workflow.sh http://192.168.10.150:1202
|
|
```
|
|
|
|
This smoke refuses missing/unsupported inputs, uploads the raster and reference
|
|
dataset through the normal dataset service, generates raster tiles, selects a
|
|
local model asset, runs configured YOLO detection, compares persisted
|
|
detections against persisted `vector_features`, persists QA/QC rows and exports
|
|
the detection GeoJSON. It never seeds demo detections, enables fixture mode,
|
|
fetches live providers or downloads model weights. Configured-YOLO model class
|
|
labels are normalized to lowercase for filtering and persisted detections while
|
|
the original model label is retained in detection provenance. Raster tile
|
|
manifests generated for AI handoff include source CRS metadata so pixel-space
|
|
model outputs can be transformed to WGS84 GeoJSON coordinates. Current V1 upload
|
|
support is limited to GeoTIFF-style rasters and GeoJSON/JSON reference vectors.
|
|
|
|
When `REAL_AREA_BBOX=minx,miny,maxx,maxy` is supplied, the same workflow also
|
|
persists an EPSG:4326 project Area before uploading data. `REAL_AREA_NAME` and
|
|
`REAL_PROJECT_REGION` retain operator context. The multi-sample runner fills
|
|
these values from manifest `wgs84_bbox` and municipality metadata, so generated
|
|
projects are immediately usable in the map without an alternate persistence
|
|
path or API contract.
|
|
|
|
To prepare the documented operator sample corpus inside the all-in-one runtime
|
|
container, run:
|
|
|
|
```bash
|
|
docker exec -it geointel python3 /app/scripts/prepare_operator_real_data_samples.py
|
|
```
|
|
|
|
The helper writes GeoTIFF orthophotos, GRB GBG building GeoJSON files and
|
|
`operator_samples_manifest.json` under `/app/storage/operator-data`. In
|
|
addition to the established positive and background AOIs, the registry contains
|
|
Beerse, Rijkevorsel, Hoogstraten and Vorselaar as focused small-building
|
|
training AOIs. Vosselaar and Grobbendonk are independent validation AOIs and
|
|
must not be exported into the training split. Background candidates can persist
|
|
empty GRB FeatureCollections for negative-tile training; normal reference AOIs
|
|
still fail when GRB returns no buildings. These are runtime artifacts only and
|
|
are not committed to Git.
|
|
|
|
Mol additionally has operational holdouts for Achterbos, Gompel, Donk and
|
|
Postel, with Mol center as the historical baseline and Postel-bos as a separate
|
|
background control. Prepare and execute that pack with the documented
|
|
`prepare_operator_real_data_samples.py` and
|
|
`run_mol_operational_validation.sh` commands in `scripts/README.md`. The runner
|
|
produces a coverage-aware operational decision report: canonical footprint-IoU
|
|
metrics remain authoritative, reference-envelope matches remain diagnostic,
|
|
and no report can activate or mutate a model asset.
|
|
|
|
For municipality-wide navigation, run
|
|
`/app/scripts/provision_mol_municipality_workspace.py` inside the all-in-one
|
|
container. It verifies the official Mol boundary (NIS `13025`), pages and clips
|
|
all GRB GBG buildings, records checksums/provenance under persistent operator
|
|
storage and imports both datasets through the existing HTTP service boundary.
|
|
The command is explicit and idempotent; it is never executed during backend
|
|
startup. See `scripts/README.md` for exact usage and refresh controls.
|
|
|
|
The current recommended local building model is
|
|
`geointel-building-yolov8s-smallbld-minpx3-img640-ft30-pt` with tile size
|
|
`512`, overlap `64` and confidence threshold `0.15`. Its SHA256 is
|
|
`a9088b8491dfae36694b53e9e9406cb4e3511d334a5712fa34f75078a47759c1`.
|
|
The promotion evidence covers seven positive AOIs at QA match IoU `0.25` and
|
|
three pure-empty background AOIs. The model improves recall and persisted
|
|
false-negative counts, but has lower precision than the previous balanced
|
|
model; operators must review and persist QA/QC rather than treating detections
|
|
as ground truth.
|
|
|
|
The latest coverage-aligned rerun of this exact profile measured mean precision
|
|
`0.6141`, recall `0.6062` and F1 `0.6069` over Mol Achterbos, Donk, Gompel and
|
|
Postel plus Retie, Turnhout and Westerlo. The three pure-empty controls remained
|
|
at zero detections. A reviewed six-AOI fine-tuning challenger reached mean F1
|
|
`0.6248` but remained inactive because it produced two false detections in the
|
|
Postel-bos empty control.
|
|
|
|
For model-quality calibration, run the confidence sweep wrapper:
|
|
|
|
```bash
|
|
REAL_RASTER_PATH=/mnt/user/appdata/geointel/storage/operator-data/geel_orthophoto_wms_512.tif \
|
|
REAL_REFERENCE_VECTOR_PATH=/mnt/user/appdata/geointel/storage/operator-data/geel_grb_gbg_buildings.geojson \
|
|
CALIBRATION_THRESHOLDS="0.50 0.35 0.25 0.15" \
|
|
bash scripts/run_detection_calibration_sweep.sh http://192.168.10.150:1202
|
|
```
|
|
|
|
The sweep creates one real persisted workflow run per threshold, fetches the
|
|
persisted `QualityCheck`/`Metric` rows and writes a `calibration_summary.json`
|
|
with persisted detection count, raw candidate count, suppressed duplicate count,
|
|
duplicate IoU threshold, score, precision, recall, F1, mean IoU and false
|
|
positive/negative counts. It is intended to tune confidence/IoU/model choices,
|
|
not to add new inference behavior.
|
|
|
|
To compare local model assets and tile settings as well as thresholds, run the
|
|
quality matrix wrapper:
|
|
|
|
```bash
|
|
REAL_RASTER_PATH=/mnt/user/appdata/geointel/storage/operator-data/geel_orthophoto_wms_512.tif \
|
|
REAL_REFERENCE_VECTOR_PATH=/mnt/user/appdata/geointel/storage/operator-data/geel_grb_gbg_buildings.geojson \
|
|
QUALITY_MODEL_ASSET_IDS="yolov8n-building-segmentation-pt yolov8n-pt" \
|
|
QUALITY_TILE_SIZES="512 640" \
|
|
QUALITY_TILE_OVERLAPS="64" \
|
|
QUALITY_THRESHOLDS="0.50 0.15" \
|
|
bash scripts/run_detection_quality_matrix.sh http://192.168.10.150:1202
|
|
```
|
|
|
|
The matrix creates one real persisted workflow run per combination and writes
|
|
`quality_matrix_summary.json` with the selected model asset, tile size, tile
|
|
overlap, threshold, detection count, QA score, precision, recall, F1, mean IoU
|
|
and false-positive/false-negative counts. It ranks `best_by_score`,
|
|
`best_by_recall` and `best_by_precision`. It does not download weights, create
|
|
fake detections, fetch live providers or change backend API behavior.
|
|
|
|
To aggregate the same matrix over every prepared operator sample, run:
|
|
|
|
```bash
|
|
OPERATOR_SAMPLE_MANIFEST_PATH=storage/operator-data/operator_samples_manifest.json \
|
|
QUALITY_MODEL_ASSET_IDS="yolov8n-building-segmentation-pt yolov8n-pt" \
|
|
QUALITY_TILE_SIZES="512 640" \
|
|
QUALITY_TILE_OVERLAPS="64" \
|
|
QUALITY_THRESHOLDS="0.50 0.15" \
|
|
bash scripts/run_multi_sample_detection_quality_matrix.sh http://192.168.10.150:1202
|
|
```
|
|
|
|
The combined `multi_sample_quality_summary.json` reports per-sample and overall
|
|
best configurations. It is an operator benchmarking command, not a backend API
|
|
or provider import path.
|
|
|
|
Before promoting any local model as a default, also run the hard-negative
|
|
matrix against the documented background candidates:
|
|
|
|
```bash
|
|
OPERATOR_SAMPLE_MANIFEST_PATH=storage/operator-data/operator_samples_manifest.json \
|
|
OPERATOR_BACKGROUND_SAMPLE_SLUGS="postel_bos lommel_heide kasterlee_bos" \
|
|
QUALITY_MODEL_ASSET_IDS="geointel-building-yolov8n-expanded160e50-pt geointel-building-yolov8n-tile30-pt yolov8s-building-segmentation-pt" \
|
|
QUALITY_TILE_SIZES="640" \
|
|
QUALITY_TILE_OVERLAPS="64" \
|
|
QUALITY_THRESHOLDS="0.25 0.15 0.05" \
|
|
bash scripts/run_operator_hard_negative_detection_matrix.sh http://192.168.10.150:1202
|
|
```
|
|
|
|
This path uploads only background rasters, runs configured-YOLO detection and
|
|
counts detections as false-positive pressure. It does not upload reference
|
|
vectors or run QA/QC, so it cannot produce fake precision/recall metrics for
|
|
empty background AOIs.
|
|
|
|
To inspect the evidence behind a calibration run, export the persisted QA
|
|
evidence bundle:
|
|
|
|
```bash
|
|
CALIBRATION_SUMMARY_PATH=/mnt/user/appdata/geointel/artifacts/detection-calibration/20260707T002103Z/calibration_summary.json \
|
|
bash scripts/export_detection_calibration_evidence.sh http://192.168.10.150:1202
|
|
```
|
|
|
|
The bundle writes combined QA evidence GeoJSON plus a standalone HTML/SVG review
|
|
artifact that separates matched detections, matched references, false positives
|
|
and false negatives by role. It reads existing persisted `QualityCheck` evidence
|
|
only and does not rerun inference.
|
|
|
|
For source-image review of false negatives, run
|
|
`scripts/render_detection_false_negative_review_contact_sheets.py` against a
|
|
fixed-threshold evidence portfolio. It uses the selected run's persisted tile
|
|
manifest, overlays candidate/reference context and explicitly exports reference
|
|
features outside tile coverage. The command is read-only and never changes
|
|
`QualityCheck`, `Metric`, `Detection` or model state.
|
|
|
|
### Run backend
|
|
|
|
```bash
|
|
cd backend
|
|
python -m uvicorn app.main:app --reload
|
|
```
|
|
|
|
### Run backend tests
|
|
|
|
```bash
|
|
cd backend
|
|
python -m pytest
|
|
```
|
|
|
|
For warning-sensitive release checks, the backend is expected to pass with Python deprecation warnings promoted to errors for the timestamp-heavy service paths:
|
|
|
|
```bash
|
|
cd backend
|
|
python -m pytest -W error::DeprecationWarning tests/test_geojson_dataset_service.py tests/test_qa_service.py tests/test_sprint7a_persistence_foundation.py tests/test_sprint8c_detection_visualization_qa.py tests/test_sprint9_segmentation_foundation.py tests/test_vector_operations_service.py
|
|
```
|
|
|
|
The repository readiness gate now applies the same warning policy to the full backend suite:
|
|
|
|
```bash
|
|
bash scripts/run_readiness_check.sh
|
|
```
|
|
|
|
That readiness gate also runs the API contract smoke check before backend/frontend compilation and tests.
|
|
|
|
### Golden QA/QC benchmark
|
|
|
|
Sprint 12 includes a deterministic QA/QC regression benchmark using explicit fixture data:
|
|
|
|
```bash
|
|
python scripts/run_golden_qa_benchmark.py
|
|
```
|
|
|
|
Machine-readable output:
|
|
|
|
```bash
|
|
python scripts/run_golden_qa_benchmark.py --json
|
|
```
|
|
|
|
Shell wrapper used by release-readiness checks:
|
|
|
|
```bash
|
|
bash scripts/verify_golden_qa_benchmark.sh
|
|
```
|
|
|
|
The benchmark compares `fixtures/golden/predicted_buildings.geojson` against `fixtures/golden/reference_buildings.geojson` and fails on metric drift. Expected baseline:
|
|
|
|
- precision: `0.5`
|
|
- recall: `0.5`
|
|
- F1: `0.5`
|
|
- mean IoU: `0.8339768339761133`
|
|
- false positives: `1`
|
|
- false negatives: `1`
|
|
|
|
The command uses existing QA/QC service logic and verifies `QualityCheck`/`Metric` persistence through an in-memory test session. It does not require live providers, AI models, Docker or PostGIS.
|
|
|
|
`scripts/run_readiness_check.sh` runs this benchmark automatically, so any
|
|
change that alters the golden QA/QC metric baseline must update the fixture and
|
|
expected metrics deliberately.
|
|
|
|
### Demo workflow seed
|
|
|
|
Sprint 15 adds an explicit offline demo workflow seed. It creates or returns a
|
|
demo project, AOI, fixture reference buildings, fixture candidate buildings and
|
|
a persisted QA/QC result. It does not fetch live GRB/OSM data and does not run
|
|
AI inference.
|
|
|
|
API:
|
|
|
|
```bash
|
|
curl -X POST http://localhost:1202/api/v1/demo/workflow
|
|
```
|
|
|
|
CLI:
|
|
|
|
```bash
|
|
python scripts/seed_demo_workflow.py --json
|
|
```
|
|
|
|
In Docker Compose on a LAN host:
|
|
|
|
```bash
|
|
curl -X POST http://192.168.10.150:1202/api/v1/demo/workflow
|
|
```
|
|
|
|
### QA/QC result listing
|
|
|
|
Persisted project quality checks and metric rows can be listed with:
|
|
|
|
```bash
|
|
curl http://localhost:1202/api/v1/projects/{project_id}/quality-checks
|
|
```
|
|
|
|
The frontend QA/QC Results panel uses this endpoint after loading the demo
|
|
workflow or running QA.
|
|
|
|
Detection QA evidence can be reviewed without changing its persisted metrics:
|
|
|
|
```bash
|
|
curl "http://localhost:1202/api/v1/projects/{project_id}/quality-checks/{quality_check_id}/reviews?reviewed=false&limit=50"
|
|
|
|
curl -X POST "http://localhost:1202/api/v1/projects/{project_id}/quality-checks/{quality_check_id}/reviews" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"evidence_role":"false_positive","evidence_feature_id":"DETECTION_UUID","decision":"qa_alignment_mismatch","notes":"Box and footprint represent the same building."}'
|
|
```
|
|
|
|
The list is derived from persisted quality-check evidence and paginates at a
|
|
maximum of 200 rows. The upsert verifies project ownership, quality-check type,
|
|
role-specific decisions and persisted Detection/VectorFeature ownership.
|
|
`detection_reviews` never mutates model output, reference geometry or canonical
|
|
Metric rows. Evidence GeoJSON queries only stored evidence ids instead of a
|
|
complete regional GRB dataset.
|
|
|
|
### Export foundation
|
|
|
|
Persisted exports can be created from the existing workbench state:
|
|
|
|
```bash
|
|
curl -X POST http://localhost:1202/api/v1/exports/metadata \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"project_id":"PROJECT_UUID"}'
|
|
```
|
|
|
|
Vector dataset GeoJSON export:
|
|
|
|
```bash
|
|
curl -X POST http://localhost:1202/api/v1/exports/geojson \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"export_kind":"dataset","dataset_id":"DATASET_UUID"}'
|
|
```
|
|
|
|
Detection or segmentation run GeoJSON export:
|
|
|
|
```bash
|
|
curl -X POST http://localhost:1202/api/v1/exports/geojson \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"export_kind":"detection_run","analysis_run_id":"ANALYSIS_RUN_UUID"}'
|
|
```
|
|
|
|
List and inspect exports:
|
|
|
|
```bash
|
|
curl http://localhost:1202/api/v1/exports/projects/PROJECT_UUID/exports
|
|
curl http://localhost:1202/api/v1/exports/EXPORT_UUID/content
|
|
```
|
|
|
|
Download an artifact as a browser/file response:
|
|
|
|
```bash
|
|
curl -OJ http://localhost:1202/api/v1/exports/EXPORT_UUID/download
|
|
```
|
|
|
|
Create a lightweight HTML project report artifact:
|
|
|
|
```bash
|
|
curl -X POST http://localhost:1202/api/v1/exports/report \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"project_id":"PROJECT_UUID"}'
|
|
```
|
|
|
|
The report contains project, dataset, QA/QC summary and export history state
|
|
only. It is not a PDF designer and does not add a separate reporting module.
|
|
|
|
After rebuilding a Docker/LAN deployment, verify the end-to-end demo and export
|
|
flow through the browser-facing frontend proxy:
|
|
|
|
```bash
|
|
bash scripts/verify_demo_export_workflow.sh http://192.168.10.150:1202
|
|
```
|
|
|
|
The script seeds the explicit demo workflow, verifies persisted QA/QC results,
|
|
creates metadata/report/vector GeoJSON exports, lists exports and downloads the
|
|
JSON/GeoJSON/HTML artifacts.
|
|
|
|
### Backend import smoke
|
|
|
|
```bash
|
|
cd backend
|
|
python -c "from app.main import app; print(app.title)"
|
|
```
|
|
|
|
### Dockerized backend
|
|
|
|
```bash
|
|
docker compose up --build backend db
|
|
```
|
|
|
|
The Docker Compose stack does not require a root `.env` file for the default local runtime. The database service exposes a container-internal Postgres healthcheck, and the backend also runs `docker_start.sh`, which retries an actual SQL `SELECT 1` connection before running `python -m alembic upgrade head` and starting Uvicorn.
|
|
|
|
PostGIS is not published on the host `5432` port by default. This avoids conflicts with existing Postgres/PostGIS services on NAS or server hosts. The backend connects over Docker networking with `db:5432`.
|
|
|
|
Backend and frontend Docker build contexts exclude dependency folders, build outputs and Python bytecode caches via `.dockerignore`.
|
|
|
|
The Docker Compose frontend is published at `http://localhost:1202`.
|
|
|
|
Compose healthchecks are enabled for all runtime services:
|
|
|
|
- `db` uses `pg_isready`.
|
|
- `backend` checks `http://127.0.0.1:8000/health` inside the container.
|
|
- `frontend` checks `http://127.0.0.1/health` through nginx, which also verifies the frontend-to-backend proxy path.
|
|
|
|
The frontend waits for a healthy backend before starting. Check runtime state:
|
|
|
|
```bash
|
|
docker compose ps
|
|
docker compose logs --tail=80 backend
|
|
docker compose logs --tail=80 frontend
|
|
```
|
|
|
|
The backend Docker image installs the approved GIS runtime extra (`.[gis]`) so
|
|
browser-facing Docker deployments can report raster/vector processing
|
|
capabilities accurately:
|
|
|
|
- `rasterio`
|
|
- `numpy`
|
|
- `pillow`
|
|
- `geopandas`
|
|
- `pyogrio`
|
|
- GDAL/GEOS/PROJ system libraries
|
|
|
|
After rebuilding the backend image, verify the LAN/browser runtime from the
|
|
repository root:
|
|
|
|
```bash
|
|
bash scripts/verify_gis_runtime.sh http://localhost:1202
|
|
```
|
|
|
|
On a NAS or server host, use the published LAN URL:
|
|
|
|
```bash
|
|
bash scripts/verify_gis_runtime.sh http://192.168.10.150:1202
|
|
```
|
|
|
|
The script calls `/api/v1/system/capabilities` through the frontend proxy and
|
|
fails if `postgis`, `rasterio` or `geopandas` are not reported as available.
|
|
|
|
The backend Docker build also runs:
|
|
|
|
```bash
|
|
python scripts/gis_import_smoke.py
|
|
```
|
|
|
|
Inside the backend Docker build context this resolves to
|
|
`backend/scripts/gis_import_smoke.py`. The root `scripts/gis_import_smoke.py`
|
|
wrapper calls the same smoke locally. The smoke imports `rasterio`, `geopandas`
|
|
and `pyogrio`; if one of those imports fails, the backend image build fails
|
|
before deployment.
|
|
|
|
### Live Docker/PostGIS migration smoke
|
|
|
|
Sprint 11 validates the real PostGIS runtime path with the existing database service. From the repository root:
|
|
|
|
```bash
|
|
docker compose config
|
|
docker compose up -d db
|
|
DATABASE_URL=postgresql+psycopg://geointel:geointel@localhost:5432/geointel bash scripts/live_migration_smoke.sh
|
|
```
|
|
|
|
The smoke script:
|
|
|
|
- opens a backend SQLAlchemy connection and runs `SELECT 1`
|
|
- runs `alembic upgrade head`
|
|
- checks `PostGIS_Version()` after migrations have created the extension
|
|
- verifies one Alembic head
|
|
- verifies required migrated tables and GiST indexes exist
|
|
|
|
Expected local environment:
|
|
|
|
```bash
|
|
DATABASE_URL=postgresql+psycopg://geointel:geointel@localhost:5432/geointel
|
|
```
|
|
|
|
If the database is not reachable, confirm Docker Desktop is running and that port `5432` is not already occupied. To clean up the local database container without deleting the named volume:
|
|
|
|
```bash
|
|
docker compose stop db
|
|
```
|
|
|
|
To remove the local PostGIS volume as well, use only when you explicitly want a fresh database:
|
|
|
|
```bash
|
|
docker compose down -v
|
|
```
|
|
|
|
## Key docs
|
|
- `docs/API_CONTRACTS.md`
|
|
- `docs/DATABASE_IMPLEMENTATION_PLAN.md`
|
|
- `docs/DEFINITION_OF_DONE.md`
|
|
- `docs/40-build-launch/SPRINT_1_SCOPE_FREEZE.md`
|
|
|
|
## Raster dependency note
|
|
|
|
Raster metadata and raster operations depend on local GDAL/rasterio availability.
|
|
|
|
To enable raster processing locally:
|
|
|
|
```bash
|
|
python -m pip install rasterio
|
|
```
|
|
|
|
If `rasterio` is unavailable:
|
|
- raster metadata responses return `503` with `RASTER_PROCESSING_UNAVAILABLE`
|
|
- raster clip/tile endpoints return explicit unavailable responses
|
|
|
|
## Export report artifact
|
|
|
|
`POST /api/v1/exports/report` creates the existing lightweight
|
|
`project_report_html` artifact. The report is a self-contained HTML handoff
|
|
view rendered from persisted project, dataset, QA/QC and export-history state.
|
|
It includes readiness scorecards, dataset inventory, QA/QC evidence, artifact
|
|
history, known limitations and print-friendly CSS.
|
|
|
|
This remains a simple HTML export. It does not add a PDF designer, report
|
|
builder, live provider fetching or new analysis behavior.
|
|
|
|
## Vector area selection
|
|
|
|
`POST /api/v1/projects/{project_id}/datasets/{dataset_id}/vector/select` runs a
|
|
read-only EPSG:4326 bbox query against persisted PostGIS `vector_features` and
|
|
returns a canonical-envelope GeoJSON FeatureCollection. It is intended for the
|
|
Map workspace area-extract flow and does not create derived datasets or export
|
|
records by itself.
|
|
|
|
The same bounded endpoint is the canonical large-layer map delivery path. The
|
|
frontend requests at most 1,000 features for the current viewport and surfaces
|
|
the response `truncated` flag; the backend does not provide or imply an
|
|
unbounded municipality-wide map response.
|
|
|
|
Selection summaries expose a primary metric plus an additive `metrics` list.
|
|
Known persisted themes are aggregated in `EPSG:31370`: building footprints,
|
|
forest, water surfaces and parcels return hectares; roads and linear
|
|
watercourses return kilometres; population keeps its configured inhabitant
|
|
aggregation. Intersecting feature counts remain available as supporting
|
|
evidence. Water volume is deliberately unavailable because the current GRB
|
|
source has no reliable depth/bathymetry dimension; GeoIntel does not manufacture
|
|
volume from 2D polygons.
|
|
|
|
`POST /api/v1/projects/{project_id}/datasets/{dataset_id}/vector/select/derive`
|
|
uses the same persisted `vector_features` selection but writes the result as a
|
|
new derived vector dataset. The created dataset uses
|
|
`source="operation:selection"`, `source_name="map_selection"` and
|
|
`derived_from_dataset_id` for source provenance, stores a GeoJSON artifact and
|
|
indexes its features back into `vector_features` for later QA/QC and analysis.
|
|
|
|
`POST /api/v1/exports/geojson` with `export_kind="vector_selection"` persists
|
|
the same bbox-selected FeatureCollection as a normal export record with
|
|
`export_type="vector_selection_geojson"`. This creates a handoff artifact only;
|
|
it does not create a derived dataset.
|
|
|
|
## Geographic scope provisioning
|
|
|
|
The explicit operator command below provisions the official 28-municipality
|
|
Vlaamse vervoerregio Kempen boundary foundation:
|
|
|
|
```bash
|
|
docker exec geointel python /app/scripts/provision_geographic_scope.py \
|
|
--scope kempen-transport-region
|
|
```
|
|
|
|
It reads current `VRBG/Refgem` boundaries, validates every registered name and
|
|
NIS code, unions the regional geometry and creates one project, one regional
|
|
Area, 28 municipality Areas and two source datasets through the public API.
|
|
It never writes directly to PostGIS and does not run on startup. The persisted
|
|
scope limitation explicitly distinguishes the transport-policy region from a
|
|
cultural or landscape definition of Kempen.
|
|
|
|
Use `--fetch-only` for a source/geometry/checksum audit. The scope pass does
|
|
not fetch thematic GRB, population or land-use data; those remain separate,
|
|
bounded operator jobs.
|
|
|
|
Provision the regional GRB building theme after the scope pass:
|
|
|
|
```bash
|
|
docker exec geointel python /app/scripts/provision_regional_grb_buildings.py \
|
|
--scope kempen-transport-region
|
|
```
|
|
|
|
The operator retains 28 checksummed municipality partitions but exposes one
|
|
normal regional reference dataset. `StorageService` copies the combined
|
|
artifact without materializing it as upload bytes; `DatasetService` creates
|
|
the Dataset and immutable DatasetVersion; `VectorFeatureService` validates and
|
|
flushes partition features in bounded batches. The transaction must index the
|
|
exact manifest feature count or it rolls back and removes the managed copy.
|
|
No public API contract or provider readiness claim is changed by this
|
|
operator-only path.
|
|
|
|
Provision the regional current road, water and parcel context through the
|
|
same persistence boundary:
|
|
|
|
```bash
|
|
docker exec geointel python /app/scripts/provision_regional_grb_context.py \
|
|
--scope kempen-transport-region --layers roads water parcels
|
|
```
|
|
|
|
The operator keeps one resumable municipality partition set per theme and
|
|
creates one regional reference Dataset per theme. Polygon ownership uses
|
|
maximum overlap area; line ownership uses maximum overlap length. It preserves
|
|
source geometry dimensions and collection-qualified source IDs, copies the
|
|
combined artifact through StorageService and indexes bounded batches through
|
|
DatasetService/VectorFeatureService. It does not add API routes, direct SQL or
|
|
interactive provider downloads.
|
|
|
|
## Temporal Mol data and evolution
|
|
|
|
Dataset uploads accept `temporal_series_key`, `observed_at`, `valid_from`,
|
|
`valid_to`, `temporal_granularity` and `source_version`. Every new source or
|
|
derived dataset also writes dataset version 1 in the same transaction.
|
|
|
|
After the Mol municipality workspace is available, import the official source
|
|
snapshots explicitly:
|
|
|
|
```bash
|
|
docker exec geointel python /app/scripts/provision_mol_population_history.py
|
|
docker exec geointel python /app/scripts/provision_mol_historical_landuse.py
|
|
docker exec geointel python /app/scripts/provision_official_landuse_timeseries.py
|
|
```
|
|
|
|
The first command imports Statbel sector population for 2021-2025. The second
|
|
imports Digitaal Vlaanderen historical land use for 1778, 1873 and 1969. The
|
|
third imports the Departement Omgeving 10 m forest class for 2013, 2016, 2019,
|
|
2022 and 2025. All commands are idempotent, use the normal
|
|
API/DatasetService flow and retain fetched artifacts in persistent operator
|
|
storage. They never run on app startup.
|
|
|
|
Historical land-use work can be bounded explicitly:
|
|
|
|
```bash
|
|
docker exec geointel python /app/scripts/provision_mol_historical_landuse.py --years 1778 1969 --themes forest water
|
|
```
|
|
|
|
`GET /api/v1/projects/{project_id}/temporal/series` discovers the series and
|
|
`POST /api/v1/projects/{project_id}/temporal/compare` compares two snapshots
|
|
inside one EPSG:4326 bbox. Partial statistical sectors are estimates; old map
|
|
editions without stable identities do not produce invented object changes.
|
|
Modern raster-derived forest polygons have the same identity limitation. Their
|
|
area is measured in EPSG:31370 and is exact within the 10 m source
|
|
representation, not a cadastral forest survey.
|
|
|
|
The same source-governed operators can synchronize the approved regional
|
|
scope in one explicit pass:
|
|
|
|
```bash
|
|
docker exec geointel python /app/scripts/provision_regional_timeseries.py
|
|
```
|
|
|
|
This resolves the retained official boundary and imports five Statbel
|
|
population snapshots plus five modern forest, water, built-function and
|
|
transport-infrastructure snapshots into
|
|
`Kempen Regional Workbench`. Mol and regional series keys remain separate and
|
|
existing immutable datasets are reused. Complete statistical sectors use exact
|
|
published totals; a rectangle cutting a sector remains an area-weighted
|
|
estimate. Forest area is measured within the official 10 m representation.
|
|
Use `--fetch-only` to validate source artifacts without database mutation.
|
|
The regional forest path partitions WCS requests by official municipality to
|
|
stay within upstream response limits, then builds one retained 10 m mosaic and
|
|
one normal regional vector Dataset. A failed source request leaves completed
|
|
partition artifacts reusable and never lowers source resolution silently.
|
|
|
|
Official operator datasets record that their geometries were clipped to the
|
|
persisted Area. When that exact Area is selected, vector totals and aggregate
|
|
metrics use the already clipped geometries directly rather than intersecting
|
|
every row with the same detailed boundary again. This optimization is allowed
|
|
only for matching Dataset/Area ids with explicit clipping metadata or a known
|
|
clipping operator; drawn rectangles and ordinary uploads keep the normal exact
|
|
PostGIS intersection path.
|
|
|
|
## Local Ollama GIS assistant
|
|
|
|
The optional assistant is a read-only backend integration. It lists locally
|
|
installed Ollama models, calculates the active Area/bbox metrics from persisted
|
|
PostGIS features and sends only that compact JSON context to Ollama. It never
|
|
downloads models, sends geometries or treats model prose as source data.
|
|
|
|
Configuration:
|
|
|
|
```text
|
|
OLLAMA_ENABLED=true
|
|
OLLAMA_BASE_URL=http://host.docker.internal:11434
|
|
OLLAMA_DEFAULT_MODEL=qwen3.5:9b
|
|
OLLAMA_TIMEOUT_SECONDS=120
|
|
OLLAMA_MAX_OUTPUT_TOKENS=700
|
|
OLLAMA_CONTEXT_TOKENS=16384
|
|
```
|
|
|
|
The Unraid deployment adds `host.docker.internal:host-gateway` automatically.
|
|
Verify the connection with `GET /api/v1/assistant/status`, inspect installed
|
|
models with `GET /api/v1/assistant/models` and ask a grounded question through
|
|
`POST /api/v1/projects/{project_id}/assistant/query`. A requested model must be
|
|
present in Ollama `/api/tags`. Missing water depth/bathymetry remains explicit;
|
|
the assistant cannot turn 2D water geometry into volume. GeoIntel rejects an
|
|
answer when Ollama reports `done_reason=length`, so a visibly truncated sentence
|
|
is never presented as a complete result.
|
|
|
|
## Helpful repository scripts
|
|
|
|
- `bash scripts/backend_install.sh`
|
|
- `bash scripts/backend_test.sh`
|
|
- `bash scripts/backend_dev.sh`
|
|
- `bash scripts/smoke_backend_import.sh`
|
|
|
|
## Bounded official orthophoto acquisition
|
|
|
|
`POST /api/v1/projects/{project_id}/datasets/orthophoto/acquire` accepts an
|
|
explicit EPSG:4326 map rectangle and stores the official Digitaal Vlaanderen
|
|
`OMWRGBMRVL`/`Ortho` response as a canonical EPSG:31370 raster Dataset. The
|
|
default safety envelope is 128-1,024 m per side, 1 m/pixel, 32 MiB and a
|
|
24-hour exact-request cache. It runs synchronously behind the existing Job
|
|
abstraction and never during startup.
|
|
|
|
Settings: `ORTHOPHOTO_ENABLED`, `ORTHOPHOTO_WMS_URL`,
|
|
`ORTHOPHOTO_WMS_LAYER`, `ORTHOPHOTO_RESOLUTION_M`,
|
|
`ORTHOPHOTO_MIN_SIDE_M`, `ORTHOPHOTO_MAX_SIDE_M`,
|
|
`ORTHOPHOTO_TIMEOUT_SECONDS`, `ORTHOPHOTO_MAX_RESPONSE_MB` and
|
|
`ORTHOPHOTO_CACHE_TTL_HOURS`. Keep the official HTTPS URL and 1 m profile
|
|
unless a separately verified deployment/model profile requires a change.
|