fix: keep Mol database layers explicit on map
This commit is contained in:
+24
-14
@@ -56,11 +56,11 @@ const workspaceNavGroups: Array<{ label: string; keys: WorkspaceKey[] }> = [
|
||||
function App(): JSX.Element {
|
||||
const [activeWorkspace, setActiveWorkspace] = useState<WorkspaceKey>('overview')
|
||||
const [inspectorOpen, setInspectorOpen] = useState(false)
|
||||
const [mapContentMode, setMapContentMode] = useState<'dataset' | 'analysis'>('dataset')
|
||||
useEffect(() => {
|
||||
window.scrollTo({ top: 0, left: 0 })
|
||||
setInspectorOpen(false)
|
||||
}, [activeWorkspace])
|
||||
|
||||
const {
|
||||
projects,
|
||||
selectedProject,
|
||||
@@ -83,6 +83,10 @@ function App(): JSX.Element {
|
||||
setProjectForm,
|
||||
setAreaForm,
|
||||
} = useProjectWorkspace()
|
||||
const selectProject = (projectId: string) => {
|
||||
setMapContentMode('dataset')
|
||||
setSelectedProjectId(projectId)
|
||||
}
|
||||
const {
|
||||
selectedDatasetId,
|
||||
selectedDataset,
|
||||
@@ -351,7 +355,8 @@ function App(): JSX.Element {
|
||||
featureCount: selectedDatasetSummary?.feature_count ?? selectedDataset?.feature_count ?? null,
|
||||
isVectorDatasetType,
|
||||
})
|
||||
const analysisMapLayerActive = Boolean(changeDetectionResult?.geojson || segmentationGeoJson || detectionGeoJson)
|
||||
const analysisMapLayerAvailable = Boolean(changeDetectionResult?.geojson || segmentationGeoJson || detectionGeoJson)
|
||||
const analysisMapLayerActive = mapContentMode === 'analysis' && analysisMapLayerAvailable
|
||||
const viewportVectorLayerActive = viewportVectorLayer.enabled && !analysisMapLayerActive
|
||||
const datasetMapContent = viewportVectorLayer.enabled ? viewportVectorLayer.data : datasetContent
|
||||
const useRasterTileManifestForSegmentation = () => {
|
||||
@@ -410,9 +415,9 @@ function App(): JSX.Element {
|
||||
setSelectedMapFeature,
|
||||
} = useMapWorkspaceState({
|
||||
areas,
|
||||
changeDetectionGeoJson: changeDetectionResult?.geojson ?? null,
|
||||
segmentationGeoJson,
|
||||
detectionGeoJson,
|
||||
changeDetectionGeoJson: analysisMapLayerActive ? changeDetectionResult?.geojson ?? null : null,
|
||||
segmentationGeoJson: analysisMapLayerActive ? segmentationGeoJson : null,
|
||||
detectionGeoJson: analysisMapLayerActive ? detectionGeoJson : null,
|
||||
datasetContent: datasetMapContent,
|
||||
selectedDataset,
|
||||
datasetLayerActive: Boolean(selectedDataset && isVectorDatasetType(selectedDataset.dataset_type)),
|
||||
@@ -479,7 +484,7 @@ function App(): JSX.Element {
|
||||
loadSegmentationRuns,
|
||||
loadQualityChecks,
|
||||
loadExports,
|
||||
setSelectedProjectId,
|
||||
setSelectedProjectId: selectProject,
|
||||
setSelectedDatasetId,
|
||||
setSelectedMapAreaId,
|
||||
setQaCandidateDatasetId,
|
||||
@@ -521,28 +526,28 @@ function App(): JSX.Element {
|
||||
const selectedArea = areas.find((area) => area.id === selectedMapAreaId) ?? null
|
||||
const activeWorkspaceItem = workspaceNavItems.find((item) => item.key === activeWorkspace) ?? workspaceNavItems[0]
|
||||
const mapLayerSourceLabel = useMemo(() => {
|
||||
if (changeDetectionResult?.geojson) {
|
||||
if (analysisMapLayerActive && changeDetectionResult?.geojson) {
|
||||
return 'Change detection'
|
||||
}
|
||||
if (segmentationGeoJson) {
|
||||
if (analysisMapLayerActive && segmentationGeoJson) {
|
||||
return 'Segmentation run'
|
||||
}
|
||||
if (detectionGeoJson) {
|
||||
if (analysisMapLayerActive && detectionGeoJson) {
|
||||
return 'Detection run'
|
||||
}
|
||||
if ((datasetMapContent || viewportVectorLayer.enabled) && selectedDataset) {
|
||||
return `${selectedDataset.dataset_type} dataset`
|
||||
}
|
||||
return 'No active vector or result layer'
|
||||
}, [changeDetectionResult?.geojson, datasetMapContent, detectionGeoJson, segmentationGeoJson, selectedDataset, viewportVectorLayer.enabled])
|
||||
}, [analysisMapLayerActive, changeDetectionResult?.geojson, datasetMapContent, detectionGeoJson, segmentationGeoJson, selectedDataset, viewportVectorLayer.enabled])
|
||||
const mapLayerProvenance = useMemo(() => {
|
||||
if (changeDetectionResult?.geojson) {
|
||||
if (analysisMapLayerActive && changeDetectionResult?.geojson) {
|
||||
return `source ${changeSourceDatasetId || 'n/a'} -> target ${changeTargetDatasetId || 'n/a'}`
|
||||
}
|
||||
if (segmentationGeoJson) {
|
||||
if (analysisMapLayerActive && segmentationGeoJson) {
|
||||
return selectedSegmentationRunId ? `analysis run ${selectedSegmentationRunId}` : 'segmentation results loaded'
|
||||
}
|
||||
if (detectionGeoJson) {
|
||||
if (analysisMapLayerActive && detectionGeoJson) {
|
||||
return selectedDetectionRunId ? `analysis run ${selectedDetectionRunId}` : 'detection results loaded'
|
||||
}
|
||||
if ((datasetMapContent || viewportVectorLayer.enabled) && selectedDataset) {
|
||||
@@ -550,6 +555,7 @@ function App(): JSX.Element {
|
||||
}
|
||||
return 'Open a dataset, detection run, segmentation run or change result to draw it here.'
|
||||
}, [
|
||||
analysisMapLayerActive,
|
||||
changeDetectionResult?.geojson,
|
||||
changeSourceDatasetId,
|
||||
changeTargetDatasetId,
|
||||
@@ -565,6 +571,7 @@ function App(): JSX.Element {
|
||||
if (selectedProjectId) {
|
||||
loadDatasetDetails(selectedProjectId, dataset)
|
||||
}
|
||||
setMapContentMode('dataset')
|
||||
setMapLayerVisible(true)
|
||||
setActiveWorkspace('map')
|
||||
}
|
||||
@@ -867,7 +874,7 @@ function App(): JSX.Element {
|
||||
demoWorkflowMessage={demoWorkflowMessage}
|
||||
onCreateProject={createProject}
|
||||
onUpdateProjectForm={setProjectForm}
|
||||
onSelectProject={setSelectedProjectId}
|
||||
onSelectProject={selectProject}
|
||||
onLoadDemoWorkflow={loadDemoWorkflow}
|
||||
/>
|
||||
|
||||
@@ -925,6 +932,8 @@ function App(): JSX.Element {
|
||||
viewportVectorStatus={viewportVectorLayerActive ? viewportVectorLayer.statusMessage : null}
|
||||
viewportVectorTone={viewportVectorLayer.error ? 'error' : viewportVectorLayer.truncated ? 'warning' : viewportVectorLayer.loading || viewportVectorLayer.zoomRequired ? 'pending' : 'ready'}
|
||||
fitMapDataOnChange={!viewportVectorLayerActive}
|
||||
mapContentMode={analysisMapLayerActive ? 'analysis' : 'dataset'}
|
||||
analysisLayerAvailable={analysisMapLayerAvailable}
|
||||
selectedMapFeature={selectedMapFeature}
|
||||
mapSelectionBbox={mapSelectionBbox}
|
||||
mapSelectionResult={mapSelectionResult}
|
||||
@@ -952,6 +961,7 @@ function App(): JSX.Element {
|
||||
onSetAreaLayerOpacity={setAreaLayerOpacity}
|
||||
onSetMapLayerVisible={setMapLayerVisible}
|
||||
onSetMapLayerOpacity={setMapLayerOpacity}
|
||||
onSetMapContentMode={setMapContentMode}
|
||||
onSelectMapFeature={setSelectedMapFeature}
|
||||
onMapViewportChange={viewportVectorLayer.setViewport}
|
||||
onSetMapSelectionBbox={setMapSelectionBbox}
|
||||
|
||||
@@ -193,6 +193,8 @@ interface MapWorkspaceProps {
|
||||
viewportVectorStatus: string | null
|
||||
viewportVectorTone: 'ready' | 'pending' | 'warning' | 'error'
|
||||
fitMapDataOnChange: boolean
|
||||
mapContentMode: 'dataset' | 'analysis'
|
||||
analysisLayerAvailable: boolean
|
||||
selectedMapFeature: GeoJSON.Feature | null
|
||||
selectedFeature?: GeoJSON.Feature | null
|
||||
mapSelectionBbox: VectorSelectionBBox | null
|
||||
@@ -220,6 +222,7 @@ interface MapWorkspaceProps {
|
||||
onSetAreaLayerOpacity: (opacity: number) => void
|
||||
onSetMapLayerVisible: (visible: boolean) => void
|
||||
onSetMapLayerOpacity: (opacity: number) => void
|
||||
onSetMapContentMode: (mode: 'dataset' | 'analysis') => void
|
||||
onSelectMapFeature: (feature: GeoJSON.Feature | null) => void
|
||||
onMapViewportChange: (viewport: MapViewportState) => void
|
||||
onSetMapSelectionBbox: (bbox: VectorSelectionBBox | null) => void
|
||||
@@ -256,6 +259,8 @@ export function MapWorkspace({
|
||||
viewportVectorStatus,
|
||||
viewportVectorTone,
|
||||
fitMapDataOnChange,
|
||||
mapContentMode,
|
||||
analysisLayerAvailable,
|
||||
selectedMapFeature,
|
||||
selectedFeature = selectedMapFeature,
|
||||
mapSelectionBbox,
|
||||
@@ -283,6 +288,7 @@ export function MapWorkspace({
|
||||
onSetAreaLayerOpacity,
|
||||
onSetMapLayerVisible,
|
||||
onSetMapLayerOpacity,
|
||||
onSetMapContentMode,
|
||||
onSelectMapFeature,
|
||||
onMapViewportChange,
|
||||
onSetMapSelectionBbox,
|
||||
@@ -508,6 +514,26 @@ export function MapWorkspace({
|
||||
</div>
|
||||
) : null}
|
||||
<div className="map-toolbar">
|
||||
<div className="map-layer-mode" aria-label="Map content mode">
|
||||
<span>Map content</span>
|
||||
<div role="group" aria-label="Map content source">
|
||||
<button
|
||||
type="button"
|
||||
aria-pressed={mapContentMode === 'dataset'}
|
||||
onClick={() => onSetMapContentMode('dataset')}
|
||||
>
|
||||
Database
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
aria-pressed={mapContentMode === 'analysis'}
|
||||
disabled={!analysisLayerAvailable}
|
||||
onClick={() => onSetMapContentMode('analysis')}
|
||||
>
|
||||
Analysis result
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<label>
|
||||
Database layer
|
||||
<select
|
||||
|
||||
@@ -4969,7 +4969,7 @@ section {
|
||||
}
|
||||
|
||||
.map-workspace-shell .map-toolbar {
|
||||
grid-template-columns: minmax(13rem, 1.25fr) minmax(10rem, 1fr) repeat(2, minmax(8rem, 0.85fr)) minmax(11rem, 1fr);
|
||||
grid-template-columns: minmax(10rem, 0.9fr) minmax(13rem, 1.25fr) minmax(10rem, 1fr) repeat(2, minmax(8rem, 0.85fr)) minmax(11rem, 1fr);
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
@@ -4977,6 +4977,55 @@ section {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.map-layer-mode {
|
||||
display: grid;
|
||||
align-content: start;
|
||||
gap: 0.34rem;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.map-layer-mode > span {
|
||||
color: var(--muted);
|
||||
font-size: 0.7rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.map-layer-mode > div {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--line-strong);
|
||||
border-radius: 6px;
|
||||
background: var(--panel-soft);
|
||||
}
|
||||
|
||||
.map-layer-mode button {
|
||||
min-width: 0;
|
||||
min-height: 2.2rem;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
padding: 0.36rem 0.42rem;
|
||||
background: transparent;
|
||||
color: var(--muted);
|
||||
font-size: 0.68rem;
|
||||
font-weight: 750;
|
||||
line-height: 1.15;
|
||||
}
|
||||
|
||||
.map-layer-mode button + button {
|
||||
border-left: 1px solid var(--line-strong);
|
||||
}
|
||||
|
||||
.map-layer-mode button[aria-pressed='true'] {
|
||||
background: var(--accent-strong);
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.map-layer-mode button:disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.45;
|
||||
}
|
||||
|
||||
.layer-control-card,
|
||||
.map-status {
|
||||
border-radius: 6px;
|
||||
|
||||
@@ -936,7 +936,7 @@ details.ai-lab-model-surface > summary strong {
|
||||
|
||||
.map-workspace-shell .map-toolbar {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(13rem, 1.3fr) minmax(10rem, 0.9fr) minmax(8rem, 0.65fr) minmax(8rem, 0.65fr) minmax(10rem, 0.9fr);
|
||||
grid-template-columns: minmax(10rem, 0.85fr) minmax(13rem, 1.3fr) minmax(10rem, 0.9fr) minmax(8rem, 0.65fr) minmax(8rem, 0.65fr);
|
||||
gap: 0.6rem;
|
||||
align-items: end;
|
||||
}
|
||||
@@ -956,7 +956,10 @@ details.ai-lab-model-surface > summary strong {
|
||||
|
||||
.map-status {
|
||||
display: grid;
|
||||
min-height: 3.9rem;
|
||||
grid-column: 1 / -1;
|
||||
grid-template-columns: minmax(13rem, 1.35fr) repeat(3, minmax(10rem, 1fr));
|
||||
gap: 0.25rem 0.85rem;
|
||||
min-height: auto;
|
||||
align-content: center;
|
||||
border-left: 3px solid var(--accent);
|
||||
border-radius: 4px;
|
||||
@@ -969,7 +972,15 @@ details.ai-lab-model-surface > summary strong {
|
||||
}
|
||||
|
||||
.map-status span {
|
||||
display: none;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.map-status .viewport-vector-status {
|
||||
grid-column: 1 / -1;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.basemap-policy-notice {
|
||||
|
||||
Reference in New Issue
Block a user