Add guided operational GIS workflow
This commit is contained in:
+2
-1
@@ -12,7 +12,8 @@
|
||||
- Switched the default MapLibre basemap from demo tiles to an OpenStreetMap road raster basemap with visible attribution while keeping `VITE_MAP_STYLE_URL` as the production override.
|
||||
- Added a persisted database layer selector to the Map workspace so users can directly load a ready vector dataset from stored project data.
|
||||
- Added an Operational GIS run panel that reuses AOI or active layer extents to query persisted PostGIS `vector_features` through the existing bbox selection flow.
|
||||
- Added static regression coverage for the road basemap, attribution, database layer selector and persisted selection run wiring.
|
||||
- Added a basemap policy notice when the public OpenStreetMap fallback is active and a guided operational workflow for query, derived dataset, QA/QC and export handoff.
|
||||
- Added static regression coverage for the road basemap, attribution, basemap policy notice, database layer selector and persisted operational GIS workflow wiring.
|
||||
|
||||
## Sprint 115 QA/QC and Exports usability layout pass (2026-07-04)
|
||||
|
||||
|
||||
@@ -21,9 +21,17 @@ def test_map_workspace_can_select_persisted_database_layer_and_run_query() -> No
|
||||
|
||||
assert "map-database-layer-select" in map_workspace
|
||||
assert "Select persisted vector layer" in map_workspace
|
||||
assert "Basemap usage notice" in map_workspace
|
||||
assert "VITE_MAP_STYLE_URL" in map_workspace
|
||||
assert "Operational GIS run" in map_workspace
|
||||
assert "Guided operational GIS workflow" in map_workspace
|
||||
assert "vector_features" in map_workspace
|
||||
assert "Run AOI/layer query" in map_workspace
|
||||
assert "Save result dataset" in map_workspace
|
||||
assert "Save GeoJSON export" in map_workspace
|
||||
assert "Run QA/QC" in map_workspace
|
||||
assert "selectedMapDatasetId=" in app_shell
|
||||
assert ".basemap-policy-notice" in styles
|
||||
assert ".guided-gis-flow" in styles
|
||||
assert ".gis-test-run-surface" in styles
|
||||
assert ".gis-test-run-grid" in styles
|
||||
|
||||
@@ -4,12 +4,15 @@ Changed:
|
||||
- Switched the default MapLibre basemap to an OpenStreetMap road raster style with visible attribution while preserving `VITE_MAP_STYLE_URL` as the override for managed/production map styles.
|
||||
- Added a persisted database layer selector to the Map workspace so ready vector datasets can be opened directly from stored project data.
|
||||
- Added an Operational GIS run panel that reuses the selected AOI bbox or active layer bbox and calls the existing persisted `vector_features` bbox selection workflow.
|
||||
- Added a visible basemap policy notice when the public OpenStreetMap fallback is active.
|
||||
- Added a guided operational workflow that brings query, derived dataset save, GeoJSON export, reference selection, QA/QC run and evidence handoff into the Map workspace.
|
||||
- Updated `CHANGELOG.md`, `docs/TODO.md`, `docs/ENVIRONMENT_SPEC.md` and `frontend/README.md`.
|
||||
- Added regression coverage in `backend/tests/test_sprint116_operational_gis_map_workflow.py`.
|
||||
|
||||
Validation:
|
||||
- `cd frontend && npm run typecheck` passed.
|
||||
- `python -m pytest backend\tests\test_sprint116_operational_gis_map_workflow.py backend\tests\test_sprint85_map_workspace_density.py backend\tests\test_sprint106_map_bbox_extract.py backend\tests\test_sprint107_map_selection_export.py backend\tests\test_sprint108_map_selection_derived_dataset.py -q` passed: 18 tests.
|
||||
- `python -m pytest backend\tests\test_sprint116_operational_gis_map_workflow.py backend\tests\test_sprint109_map_selection_qa_shortcut.py backend\tests\test_sprint107_map_selection_export.py backend\tests\test_sprint108_map_selection_derived_dataset.py -q` passed: 11 tests.
|
||||
- `cd frontend && npm run build` passed.
|
||||
- `python -m compileall backend/app` passed.
|
||||
- `cd backend && python -m pytest -q` passed: 364 tests.
|
||||
|
||||
@@ -77,6 +77,7 @@ This file now starts with the current implementation status. Older preparation/b
|
||||
- [x] Add Map workspace panel hierarchy and layer-control density polish.
|
||||
- [x] Add selected map feature extraction with highlight, property table, copy and GeoJSON download.
|
||||
- [x] Add operational GIS map workflow with road basemap, persisted database layer selection and AOI/layer `vector_features` query run.
|
||||
- [x] Add basemap policy notice and guided GIS query-to-QA/export workflow in the Map workspace.
|
||||
- [x] Add QA/QC workspace result hierarchy and filter density polish.
|
||||
- [x] Add Change Detection panel hierarchy and analysis workspace density polish.
|
||||
- [x] Add calm workbench layout pass to reduce duplicate navigation, heavy card styling and shell density.
|
||||
|
||||
+3
-1
@@ -10,7 +10,9 @@ Data and Map have an additional usability layout pass for the core daily workflo
|
||||
|
||||
The Map workspace defaults to an OpenStreetMap road basemap with visible attribution so uploaded vectors, AOIs and QA overlays appear on a real street context. Set `VITE_MAP_STYLE_URL` to a managed MapLibre style URL to override this for production or high-volume deployments.
|
||||
|
||||
Operational GIS testing is now available directly in the Map workspace. Users can choose a persisted vector database layer, load it on the map, reuse the selected AOI or active layer extent, and run the existing persisted `vector_features` bbox query without creating fake data or a parallel backend path.
|
||||
When the public OpenStreetMap fallback is active, the Map workspace shows a basemap usage notice. This keeps the local/demo default honest and reminds operators to configure a managed style URL before production or heavier tile traffic.
|
||||
|
||||
Operational GIS testing is now available directly in the Map workspace. Users can choose a persisted vector database layer, load it on the map, reuse the selected AOI or active layer extent, run the existing persisted `vector_features` bbox query, save the result as a derived dataset, export the selection GeoJSON, choose a reference dataset and launch QA/QC without creating fake data or a parallel backend path.
|
||||
|
||||
QA/QC and Exports follow the same calmer density model. QA/QC keeps metric evidence, feature ids and raw findings available but compresses provenance and history surfaces so review starts from the selected check and map evidence actions. Exports uses denser handoff cards, latest-artifact cards and history filters so artifact creation and download paths are easier to scan.
|
||||
|
||||
|
||||
@@ -308,6 +308,7 @@ export function MapWorkspace({
|
||||
)
|
||||
const selectedFeatureFilename = selectedFeatureStem === 'selected-feature' ? DEFAULT_SELECTED_FEATURE_FILENAME : `${selectedFeatureStem}.geojson`
|
||||
const selectedMapDataset = availableMapDatasets.find((dataset) => dataset.id === selectedMapDatasetId) ?? null
|
||||
const usesDefaultOsmBasemap = !import.meta.env.VITE_MAP_STYLE_URL
|
||||
|
||||
useEffect(() => {
|
||||
setBboxInput(bboxToInputState(mapSelectionBbox))
|
||||
@@ -437,6 +438,12 @@ export function MapWorkspace({
|
||||
</div>
|
||||
|
||||
<div className="map-control-surface" aria-label="Map workspace controls">
|
||||
{usesDefaultOsmBasemap ? (
|
||||
<div className="basemap-policy-notice" aria-label="Basemap usage notice">
|
||||
<strong>Local/demo basemap</strong>
|
||||
<span>Public OpenStreetMap tiles are active. Configure VITE_MAP_STYLE_URL for production or heavier use.</span>
|
||||
</div>
|
||||
) : null}
|
||||
<div className="map-toolbar">
|
||||
<label>
|
||||
Database layer
|
||||
@@ -664,6 +671,92 @@ export function MapWorkspace({
|
||||
</button>
|
||||
</div>
|
||||
{mapSelectionError ? <p className="error">{mapSelectionError}</p> : null}
|
||||
<div className="guided-gis-flow" aria-label="Guided operational GIS workflow">
|
||||
<div className="guided-gis-steps">
|
||||
<div className={selectedMapDataset ? 'complete' : ''}>
|
||||
<span>1</span>
|
||||
<strong>Layer</strong>
|
||||
<small>{selectedMapDataset ? selectedMapDataset.name : 'Select database layer'}</small>
|
||||
</div>
|
||||
<div className={currentSelectionBbox ? 'complete' : ''}>
|
||||
<span>2</span>
|
||||
<strong>Extent</strong>
|
||||
<small>{currentSelectionBbox ? 'AOI/layer bbox ready' : 'Use AOI or layer extent'}</small>
|
||||
</div>
|
||||
<div className={mapSelectionResult ? 'complete' : ''}>
|
||||
<span>3</span>
|
||||
<strong>Query</strong>
|
||||
<small>{mapSelectionResult ? `${mapSelectionResult.feature_count} persisted features` : 'Run PostGIS selection'}</small>
|
||||
</div>
|
||||
<div className={latestSelectionDatasetName ? 'complete' : ''}>
|
||||
<span>4</span>
|
||||
<strong>Dataset</strong>
|
||||
<small>{latestSelectionDatasetName ?? 'Save query result'}</small>
|
||||
</div>
|
||||
<div className={mapSelectionQaResult ? 'complete' : ''}>
|
||||
<span>5</span>
|
||||
<strong>QA/QC</strong>
|
||||
<small>{mapSelectionQaResult ? `F1 ${mapSelectionQaResult.f1_score ?? 'n/a'}` : 'Compare with reference'}</small>
|
||||
</div>
|
||||
<div className={latestSelectionExportPath ? 'complete' : ''}>
|
||||
<span>6</span>
|
||||
<strong>Export</strong>
|
||||
<small>{latestSelectionExportPath ? 'GeoJSON artifact ready' : 'Save handoff artifact'}</small>
|
||||
</div>
|
||||
</div>
|
||||
<div className="guided-gis-actions">
|
||||
<button
|
||||
className="secondary-action"
|
||||
disabled={!mapSelectionResult || !currentSelectionBbox || selectionDatasetSaving}
|
||||
type="button"
|
||||
onClick={saveAreaSelectionDataset}
|
||||
>
|
||||
{selectionDatasetSaving ? 'Saving dataset...' : 'Save result dataset'}
|
||||
</button>
|
||||
<button
|
||||
className="secondary-action"
|
||||
disabled={!mapSelectionResult || !currentSelectionBbox || selectionExporting}
|
||||
type="button"
|
||||
onClick={saveAreaSelectionExport}
|
||||
>
|
||||
{selectionExporting ? 'Saving export...' : 'Save GeoJSON export'}
|
||||
</button>
|
||||
<label>
|
||||
Reference
|
||||
<select
|
||||
value={selectedMapQaReferenceDatasetId}
|
||||
onChange={(event) => onSelectMapQaReferenceDataset(event.target.value)}
|
||||
disabled={!latestSelectionDatasetName || mapQaReferenceDatasets.length === 0 || mapSelectionQaRunning}
|
||||
>
|
||||
<option value="">Select reference</option>
|
||||
{mapQaReferenceDatasets.map((dataset) => (
|
||||
<option key={dataset.id} value={dataset.id}>
|
||||
{dataset.name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
<button
|
||||
className="primary-action"
|
||||
disabled={!latestSelectionDatasetName || !selectedMapQaReferenceDatasetId || mapSelectionQaRunning}
|
||||
type="button"
|
||||
onClick={onRunMapSelectionQa}
|
||||
>
|
||||
{mapSelectionQaRunning ? 'Running QA...' : 'Run QA/QC'}
|
||||
</button>
|
||||
<button
|
||||
className="secondary-action"
|
||||
disabled={!latestMapSelectionQualityCheckId}
|
||||
type="button"
|
||||
onClick={onOpenMapSelectionQualityEvidence}
|
||||
>
|
||||
Open evidence
|
||||
</button>
|
||||
</div>
|
||||
{selectionDatasetError ? <p className="error">{selectionDatasetError}</p> : null}
|
||||
{selectionExportError ? <p className="error">{selectionExportError}</p> : null}
|
||||
{mapSelectionQaError ? <p className="error">{mapSelectionQaError}</p> : null}
|
||||
</div>
|
||||
</div>
|
||||
<div className="bbox-select-surface" aria-label="Area selection and extract">
|
||||
<div className="panel-title-row">
|
||||
|
||||
+109
-2
@@ -4769,6 +4769,110 @@ section {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.basemap-policy-notice {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.28rem 0.55rem;
|
||||
align-items: center;
|
||||
border: 1px solid #fde68a;
|
||||
border-radius: 6px;
|
||||
padding: 0.46rem 0.58rem;
|
||||
background: #fffbeb;
|
||||
color: #713f12;
|
||||
font-size: 0.76rem;
|
||||
}
|
||||
|
||||
.basemap-policy-notice strong {
|
||||
font-size: 0.72rem;
|
||||
letter-spacing: 0.045em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.basemap-policy-notice span {
|
||||
color: #854d0e;
|
||||
}
|
||||
|
||||
.guided-gis-flow {
|
||||
display: grid;
|
||||
gap: 0.5rem;
|
||||
border-top: 1px solid var(--line);
|
||||
padding-top: 0.54rem;
|
||||
}
|
||||
|
||||
.guided-gis-steps {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 0.38rem;
|
||||
}
|
||||
|
||||
.guided-gis-steps > div {
|
||||
display: grid;
|
||||
grid-template-columns: auto minmax(0, 1fr);
|
||||
gap: 0.08rem 0.38rem;
|
||||
align-items: center;
|
||||
min-width: 0;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 6px;
|
||||
padding: 0.4rem 0.44rem;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.guided-gis-steps > div.complete {
|
||||
border-color: rgba(15, 118, 110, 0.26);
|
||||
background: #f0fdfa;
|
||||
}
|
||||
|
||||
.guided-gis-steps span {
|
||||
display: inline-grid;
|
||||
place-items: center;
|
||||
grid-row: span 2;
|
||||
width: 1.35rem;
|
||||
height: 1.35rem;
|
||||
border-radius: 999px;
|
||||
background: var(--panel-soft);
|
||||
color: var(--muted);
|
||||
font-size: 0.68rem;
|
||||
font-weight: 850;
|
||||
}
|
||||
|
||||
.guided-gis-steps .complete span {
|
||||
background: var(--accent);
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.guided-gis-steps strong,
|
||||
.guided-gis-steps small {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.guided-gis-steps strong {
|
||||
font-size: 0.76rem;
|
||||
}
|
||||
|
||||
.guided-gis-steps small {
|
||||
color: var(--muted);
|
||||
font-size: 0.68rem;
|
||||
}
|
||||
|
||||
.guided-gis-actions {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 0.38rem;
|
||||
align-items: end;
|
||||
}
|
||||
|
||||
.guided-gis-actions label {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.guided-gis-actions button {
|
||||
min-height: 2.15rem;
|
||||
padding: 0.38rem 0.55rem;
|
||||
font-size: 0.78rem;
|
||||
}
|
||||
|
||||
.bbox-select-surface {
|
||||
border-left-width: 3px;
|
||||
}
|
||||
@@ -4829,7 +4933,8 @@ section {
|
||||
.dataset-action-grid,
|
||||
.map-context-summary,
|
||||
.map-workspace-shell .map-toolbar,
|
||||
.layer-provenance-rail {
|
||||
.layer-provenance-rail,
|
||||
.guided-gis-steps {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
@@ -4842,7 +4947,9 @@ section {
|
||||
.map-context-summary,
|
||||
.map-workspace-shell .map-toolbar,
|
||||
.layer-provenance-rail,
|
||||
.bbox-select-grid {
|
||||
.bbox-select-grid,
|
||||
.guided-gis-steps,
|
||||
.guided-gis-actions {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user