Add guided operational GIS workflow
This commit is contained in:
@@ -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