Add guided operational GIS workflow
GeoIntel CI / docs-smoke (push) Has been cancelled
GeoIntel CI / contract-smoke (push) Has been cancelled

This commit is contained in:
Codex
2026-07-04 23:52:25 +02:00
parent 069de33080
commit 14b66e642b
7 changed files with 219 additions and 4 deletions
@@ -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
View File
@@ -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;
}