Upgrade async GPU analysis and workbench UX
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import type {
|
||||
DatasetCreateResponse,
|
||||
JobRead,
|
||||
SegmentationModelCapability,
|
||||
SegmentationQaResult,
|
||||
SegmentationRead,
|
||||
@@ -7,7 +8,11 @@ import type {
|
||||
SegmentationRunResponse,
|
||||
} from '../../types'
|
||||
import { ModelSelector } from '../models/ModelSelector'
|
||||
import { toAnalysisModelOption } from '../models/modelOptions'
|
||||
import {
|
||||
analysisModelAvailabilityMessage,
|
||||
analysisModelDisplayName,
|
||||
toAnalysisModelOption,
|
||||
} from '../models/modelOptions'
|
||||
|
||||
interface SegmentationLabProps {
|
||||
segmentationModels: SegmentationModelCapability[]
|
||||
@@ -18,11 +23,14 @@ interface SegmentationLabProps {
|
||||
segmentationTileManifestPath: string
|
||||
segmentationConfidenceThreshold: number
|
||||
runningSegmentation: boolean
|
||||
segmentationJob: JobRead | null
|
||||
segmentationRunResult: SegmentationRunResponse | null
|
||||
segmentationRunError: string | null
|
||||
segmentationRuns: SegmentationRunRead[]
|
||||
selectedSegmentationRunId: string
|
||||
segmentationItems: SegmentationRead[]
|
||||
segmentationTotal: number
|
||||
segmentationTruncated: boolean
|
||||
segmentationClassFilter: string
|
||||
segmentationMinConfidenceFilter: number
|
||||
loadingSegmentationResults: boolean
|
||||
@@ -92,11 +100,14 @@ export function SegmentationLab({
|
||||
segmentationTileManifestPath,
|
||||
segmentationConfidenceThreshold,
|
||||
runningSegmentation,
|
||||
segmentationJob,
|
||||
segmentationRunResult,
|
||||
segmentationRunError,
|
||||
segmentationRuns,
|
||||
selectedSegmentationRunId,
|
||||
segmentationItems,
|
||||
segmentationTotal,
|
||||
segmentationTruncated,
|
||||
segmentationClassFilter,
|
||||
segmentationMinConfidenceFilter,
|
||||
loadingSegmentationResults,
|
||||
@@ -127,8 +138,15 @@ export function SegmentationLab({
|
||||
const segmentationHasTileManifest = segmentationTileManifestPath.trim().length > 0
|
||||
const segmentationModelUiRunnable =
|
||||
selectedSegmentationModelConfigured && selectedSegmentationModelId !== 'fixture-segmenter'
|
||||
const selectedSegmentationModel = segmentationModels.find(
|
||||
(model) => model.model_id === selectedSegmentationModelId,
|
||||
) ?? null
|
||||
const selectedSegmentationModelAvailability = selectedSegmentationModel
|
||||
? analysisModelAvailabilityMessage(selectedSegmentationModel)
|
||||
: selectedSegmentationModelLimitation ?? 'Het gekozen segmentatiemodel is niet geconfigureerd'
|
||||
const segmentationRunReady =
|
||||
Boolean(selectedProjectId) && segmentationHasDataset && segmentationModelUiRunnable
|
||||
Boolean(selectedProjectId) && segmentationHasDataset && segmentationModelUiRunnable && segmentationHasTileManifest
|
||||
const segmentationJobActive = segmentationJob?.status === 'queued' || segmentationJob?.status === 'running'
|
||||
const segmentationRunBlockedReason = !selectedProjectId
|
||||
? 'Kies eerst een werkruimte'
|
||||
: !segmentationHasDataset
|
||||
@@ -136,8 +154,10 @@ export function SegmentationLab({
|
||||
: selectedSegmentationModelId === 'fixture-segmenter'
|
||||
? 'Het fixturemodel is alleen bedoeld voor expliciete tests en demo’s'
|
||||
: !selectedSegmentationModelConfigured
|
||||
? selectedSegmentationModelLimitation ?? 'Het gekozen segmentatiemodel is niet geconfigureerd'
|
||||
: null
|
||||
? selectedSegmentationModelAvailability
|
||||
: !segmentationHasTileManifest
|
||||
? 'Koppel eerst het beeldtegelmanifest van het gekozen rasterbestand'
|
||||
: null
|
||||
|
||||
return (
|
||||
<section className="workspace-panel ai-lab-shell segmentation-lab-shell">
|
||||
@@ -180,10 +200,10 @@ export function SegmentationLab({
|
||||
<ul className="model-list">
|
||||
{segmentationModels.map((model) => (
|
||||
<li className={model.configured ? 'model-card model-card-ready' : 'model-card'} key={model.model_id}>
|
||||
<strong>{model.display_name}</strong>
|
||||
<strong>{analysisModelDisplayName(model)}</strong>
|
||||
<span className={model.configured ? 'status-badge status-badge-ready' : 'status-badge'}>{model.configured ? 'gereed' : 'niet geconfigureerd'}</span>
|
||||
<p className="muted">Ondersteunde klassen: {model.supported_classes.join(', ') || 'niet opgegeven'}</p>
|
||||
<p className="muted">{model.limitation_message}</p>
|
||||
<p className="muted">{analysisModelAvailabilityMessage(model)}</p>
|
||||
<details className="technical-inline-details">
|
||||
<summary>Technische identificatie</summary>
|
||||
<div className="entity-meta">
|
||||
@@ -219,17 +239,19 @@ export function SegmentationLab({
|
||||
<span>Rasterbestand</span>
|
||||
<strong>{segmentationHasDataset ? 'Geselecteerd' : 'Kies een rasterbestand'}</strong>
|
||||
</div>
|
||||
<div className={selectedSegmentationModelConfigured ? 'lab-readiness-item lab-readiness-item-ready' : 'lab-readiness-item'}>
|
||||
<div className={segmentationModelUiRunnable ? 'lab-readiness-item lab-readiness-item-ready' : 'lab-readiness-item'}>
|
||||
<span>Analysemodel</span>
|
||||
<strong>
|
||||
{selectedSegmentationModelConfigured
|
||||
{selectedSegmentationModelId === 'fixture-segmenter'
|
||||
? 'Alleen beschikbaar voor geautomatiseerde tests'
|
||||
: selectedSegmentationModelConfigured
|
||||
? 'Het gekozen model is beschikbaar'
|
||||
: selectedSegmentationModelLimitation ?? 'Kies een geconfigureerd segmentatiemodel'}
|
||||
: selectedSegmentationModelAvailability}
|
||||
</strong>
|
||||
</div>
|
||||
<div className={segmentationHasTileManifest ? 'lab-readiness-item lab-readiness-item-ready' : 'lab-readiness-item'}>
|
||||
<span>Beeldtegels</span>
|
||||
<strong>{segmentationHasTileManifest ? 'Technisch manifest gekoppeld' : 'Niet vereist voor het fixturemodel'}</strong>
|
||||
<strong>{segmentationHasTileManifest ? 'Technisch manifest gekoppeld' : 'Koppel het tegelmanifest van het rasterbestand'}</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -292,29 +314,44 @@ export function SegmentationLab({
|
||||
className="primary-action"
|
||||
type="button"
|
||||
onClick={onRunSegmentation}
|
||||
disabled={runningSegmentation || !segmentationRunReady}
|
||||
disabled={runningSegmentation || segmentationJobActive || !segmentationRunReady}
|
||||
>
|
||||
Segmentatie starten
|
||||
{segmentationJob?.status === 'queued'
|
||||
? 'Wachten op NVIDIA GPU…'
|
||||
: runningSegmentation
|
||||
? 'GPU-segmentatie wordt verwerkt…'
|
||||
: 'Segmentatie starten'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="ai-lab-state-stack">
|
||||
{segmentationJobActive ? (
|
||||
<div className="result-state result-state-loading" role="status" aria-live="polite">
|
||||
<strong>{segmentationJob?.status === 'queued' ? 'GPU-taak staat in de wachtrij.' : 'GPU-segmentatie wordt uitgevoerd.'}</strong>
|
||||
<p>
|
||||
{segmentationJob?.status === 'queued'
|
||||
? 'De server start de taak zodra de NVIDIA-worker beschikbaar is.'
|
||||
: 'GeoIntel volgt de servertaak en toont na voltooiing alleen de werkelijk bewaarde polygonen.'}
|
||||
</p>
|
||||
<span className="muted">Taak-ID: {segmentationJob?.id}</span>
|
||||
</div>
|
||||
) : null}
|
||||
{!selectedSegmentationModelConfigured ? (
|
||||
<div className="result-state result-state-empty">
|
||||
<strong>Het segmentatiemodel is nog niet gereed.</strong>
|
||||
<p>{selectedSegmentationModelLimitation ?? 'Kies een geconfigureerd segmentatiemodel.'}</p>
|
||||
<p>{selectedSegmentationModelAvailability}</p>
|
||||
</div>
|
||||
) : null}
|
||||
{segmentationRunError ? (
|
||||
<div className="result-state result-state-error">
|
||||
<div className="result-state result-state-error" role="alert">
|
||||
<strong>De segmentatie is mislukt.</strong>
|
||||
<p>{segmentationRunError}</p>
|
||||
</div>
|
||||
) : null}
|
||||
{segmentationRunResult ? (
|
||||
<div className="result-summary-card">
|
||||
<p>Status: {segmentationRunResult.status === 'completed' ? 'afgerond' : segmentationRunResult.status}</p>
|
||||
<div className={segmentationRunResult.segmentation_count === 0 ? 'result-state result-state-warning' : 'result-summary-card'} role="status">
|
||||
<p>Status: {analysisStatusLabel(segmentationRunResult.status)}</p>
|
||||
<p>{segmentationRunResult.message}</p>
|
||||
<p>Herkende vlakken: {segmentationRunResult.segmentation_count}</p>
|
||||
{segmentationRunResult.error_code ? <p className="error">Code: {segmentationRunResult.error_code}</p> : null}
|
||||
@@ -382,10 +419,30 @@ export function SegmentationLab({
|
||||
</div>
|
||||
) : null}
|
||||
<div className="ai-lab-state-stack">
|
||||
<div className="result-state result-state-ready">
|
||||
<strong>{segmentationItems.length} vlakken geladen</strong>
|
||||
<p>{selectedSegmentationRunId ? 'Deze resultaten zijn bewaard in de database.' : 'Kies eerst een bewaarde analyse.'}</p>
|
||||
</div>
|
||||
{loadingSegmentationResults || segmentationRunError ? null : !selectedSegmentationRunId ? (
|
||||
<div className="result-state result-state-empty">
|
||||
<strong>Kies eerst een bewaarde analyse.</strong>
|
||||
<p>Daarna toont GeoIntel uitsluitend de polygonen van die analyserun.</p>
|
||||
</div>
|
||||
) : segmentationTotal === 0 ? (
|
||||
<div className="result-state result-state-empty">
|
||||
<strong>Geen bewaarde vlakken binnen deze filters.</strong>
|
||||
<p>Dit bewijst niet dat het gebied geen relevante objecten bevat.</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className={segmentationTruncated ? 'result-state result-state-warning' : 'result-state result-state-ready'}>
|
||||
<strong>
|
||||
{segmentationTruncated
|
||||
? `${segmentationItems.length} van ${segmentationTotal} vlakken geladen`
|
||||
: `${segmentationTotal} vlakken geladen`}
|
||||
</strong>
|
||||
<p>
|
||||
{segmentationTruncated
|
||||
? 'De kaart en tabel tonen een begrensde pagina. Gebruik filters om het resultaat gericht te verfijnen.'
|
||||
: 'Deze resultaten zijn bewaard in de database.'}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{segmentationItems.length > 0 ? (
|
||||
<div className="table-scroll">
|
||||
|
||||
Reference in New Issue
Block a user