Add V1 map workbench controls
GeoIntel CI / docs-smoke (push) Has been cancelled
GeoIntel CI / contract-smoke (push) Has been cancelled

This commit is contained in:
Codex
2026-06-17 00:08:27 +02:00
parent 539ec71806
commit 97be9d9293
8 changed files with 206 additions and 5 deletions
+69 -4
View File
@@ -94,6 +94,9 @@ function App(): JSX.Element {
const [selectedRasterMetadata, setSelectedRasterMetadata] = useState<RasterMetadataResponse | null>(null)
const [selectedRasterStats, setSelectedRasterStats] = useState<RasterStatsResponse | null>(null)
const [datasetContent, setDatasetContent] = useState<GeoJSON.FeatureCollection | null>(null)
const [mapLayerVisible, setMapLayerVisible] = useState(true)
const [mapLayerOpacity, setMapLayerOpacity] = useState(0.4)
const [selectedMapFeature, setSelectedMapFeature] = useState<GeoJSON.Feature | null>(null)
const [jobs, setJobs] = useState<JobRead[]>([])
const [providerCapabilities, setProviderCapabilities] = useState<ProviderCapability[]>([])
const [loadingCapabilities, setLoadingCapabilities] = useState(false)
@@ -244,6 +247,22 @@ function App(): JSX.Element {
() => changeDetectionResult?.geojson ?? segmentationGeoJson ?? detectionGeoJson ?? datasetContent,
[changeDetectionResult, segmentationGeoJson, detectionGeoJson, datasetContent],
)
const mapLayerLabel = useMemo(() => {
if (changeDetectionResult) {
return 'Change detection result'
}
if (segmentationGeoJson) {
return 'Segmentation result'
}
if (detectionGeoJson) {
return 'Detection result'
}
if (datasetContent && selectedDataset) {
return selectedDataset.name
}
return 'No active vector layer'
}, [changeDetectionResult, datasetContent, detectionGeoJson, segmentationGeoJson, selectedDataset])
const mapFeatureCount = mapFeatureCollection?.features.length ?? 0
const isRasterTileInputValid = useMemo(
() => rasterTileSize > 0 && rasterTileOverlap >= 0 && rasterTileOverlap < rasterTileSize,
[rasterTileSize, rasterTileOverlap],
@@ -624,6 +643,10 @@ function App(): JSX.Element {
loadSegmentationModels().catch(() => null)
}, [])
useEffect(() => {
setSelectedMapFeature(null)
}, [mapFeatureCollection])
useEffect(() => {
if (!selectedProjectId) {
setAreas([])
@@ -1300,8 +1323,8 @@ function App(): JSX.Element {
return (
<div className="app-shell">
<header>
<h1>GeoIntel Kempen Sprint 9</h1>
<p>Sprint 9: raster/vector workbench with detection and segmentation foundations.</p>
<h1>GeoIntel Kempen V1 Workbench</h1>
<p>Raster/vector workbench with QA/QC, exports, change detection and AI foundation layers.</p>
</header>
{errorMessage ? <p className="error">{errorMessage}</p> : null}
@@ -1826,8 +1849,50 @@ function App(): JSX.Element {
<section>
<h2>Map workspace</h2>
<p>{selectedDatasetId ? `Showing dataset ${selectedDatasetId}` : 'No vector dataset selected'}</p>
<GeoMap data={mapFeatureCollection} />
<p>{mapLayerLabel}</p>
<div className="map-controls">
<label className="checkbox-row">
<input
checked={mapLayerVisible}
disabled={!mapFeatureCollection}
type="checkbox"
onChange={(event) => setMapLayerVisible(event.target.checked)}
/>
Layer visible
</label>
<label>
Layer opacity
<input
disabled={!mapFeatureCollection}
max="1"
min="0.05"
step="0.05"
type="range"
value={mapLayerOpacity}
onChange={(event) => setMapLayerOpacity(Number(event.target.value))}
/>
</label>
<div className="map-status">
{mapFeatureCollection ? `${mapFeatureCount} features loaded` : 'No vector layer loaded'}
</div>
</div>
<GeoMap
data={mapFeatureCollection}
visible={mapLayerVisible}
opacity={mapLayerOpacity}
onFeatureSelect={setSelectedMapFeature}
/>
<div className="feature-inspector">
<h3>Feature inspector</h3>
{selectedMapFeature ? (
<>
<p>Geometry: {selectedMapFeature.geometry?.type ?? 'n/a'}</p>
<pre className="job-result">{JSON.stringify(selectedMapFeature.properties ?? {}, null, 2)}</pre>
</>
) : (
<p className="muted">Click a visible map feature to inspect its properties.</p>
)}
</div>
</section>
</div>
)