diff --git a/docs/API_CONTRACTS.md b/docs/API_CONTRACTS.md index 589efce2..4c3cc63d 100644 --- a/docs/API_CONTRACTS.md +++ b/docs/API_CONTRACTS.md @@ -1080,6 +1080,21 @@ Return vector stats (feature counts and geometry summary). Return vector bounds and feature count. + +### Export provenance + +Every exported GeoJSON carries a `geointel_provenance` foreign member on the +FeatureCollection. RFC 7946 requires parsers to ignore members they do not +know, so QGIS and ogr2ogr read the file normally. + +It names the source, the dataset and its `source_version`/`observed_at`, the +selection the export was taken from, and — most importantly — whether the file +is complete. A capped selection export previously recorded `truncated` on the +export *record* only: the downloaded file looked whole, and an operator opening +250 of 1.400 buildings in QGIS had nothing to tell them so. Completeness is +derived from the counts as well as the flag, so a caller that forgets to pass +it cannot produce a file that claims to hold everything. + ### POST `/api/v1/projects/{project_id}/datasets/{dataset_id}/vector/select` Selection metrics describe two different populations and now say so. @@ -2015,7 +2030,7 @@ If the segmentation run has no persisted geometries, the endpoint returns `SEGME ### POST `/api/v1/analysis/change-detection` Compares two persisted vector datasets in the same project and returns a synchronous -job envelope. This is a lightweight V1 foundation for added/removed object review, +job envelope. This is a lightweight V1 foundation for object-level change review, not a temporal run-history engine. Request: @@ -2025,10 +2040,37 @@ Request: "source_dataset_id": "uuid", "target_dataset_id": "uuid", "iou_threshold": 0.8, - "include_unchanged": true + "modified_threshold": 0.3, + "include_unchanged": true, + "bbox": {"min_x": 5.0, "min_y": 51.1, "max_x": 5.2, "max_y": 51.3, "crs": "EPSG:4326"}, + "area_id": "uuid", + "preview_limit": 2000 } ``` +`bbox` and `area_id` bound the comparison to the operator's selection, resolved +the same way every other analysis resolves it: the drawn rectangle intersected +with the named work area. Both populations are loaded through an indexed +`ST_Intersects` predicate rather than being read into Python in full. Without a +selection the comparison still covers both datasets entire, which is rarely the +question and — with `include_unchanged` true — previously returned a +FeatureCollection holding both datasets. + +Features are **not** clipped to the selection. A change class describes a whole +object, so comparing a clipped earlier footprint against an unclipped later one +would manufacture "modified" along the selection edge. Features the edge crosses +are compared in full and counted in a warning instead. + +`modified_threshold` separates a redrawn footprint from two distinct objects. +Between it and `iou_threshold` the change class is `modified`; below it the +source is `removed` and the target `added`. Without that class an extended +building appeared as one removal plus one addition, hiding the category the +analysis exists to show and inflating both counts. + +`preview_limit` caps the returned geometry — modified, added, removed and only +then unchanged — while every count still describes the whole selection. +`preview_truncated` says whether the cap applied. + Response is a canonical API envelope containing a `JobRead` payload. On success, `result_json` contains: diff --git a/docs/KNOWN_LIMITATIONS.md b/docs/KNOWN_LIMITATIONS.md index 5eb41565..0100fe46 100644 --- a/docs/KNOWN_LIMITATIONS.md +++ b/docs/KNOWN_LIMITATIONS.md @@ -53,6 +53,15 @@ runtime source of truth. - Flood-hazard percentages are shares of the modelled area. Where the VMM model does not cover the selection, that is reported as missing coverage and never as an absence of risk. +- Change detection compares whole objects and does not clip them to the + selection, so a footprint crossing the selection edge is compared in full. + That is deliberate: clipping one snapshot and not the other would report the + selection edge itself as a change. The count of such objects is reported. +- The change-detection GeoJSON is capped by `preview_limit` (changes first). + The counts always describe the whole selection; the map does not. +- An export states its own completeness in `geointel_provenance`. A capped + export is still a valid, usable file — it simply no longer implies it holds + everything the selection contains. ## Historical analysis