Add map QA evidence drilldown
This commit is contained in:
@@ -405,6 +405,7 @@ function App(): JSX.Element {
|
||||
mapSelectionQaRunning,
|
||||
mapSelectionQaError,
|
||||
mapSelectionQaResult,
|
||||
latestMapSelectionQualityCheckId,
|
||||
runMapSelectionQa,
|
||||
setSelectedMapQaReferenceDatasetId,
|
||||
} = useMapSelectionQa({
|
||||
@@ -413,6 +414,9 @@ function App(): JSX.Element {
|
||||
loadQualityChecks,
|
||||
loadProjectData,
|
||||
})
|
||||
const openMapSelectionQualityEvidence = () => {
|
||||
setActiveWorkspace('analysis')
|
||||
}
|
||||
const {
|
||||
loadingDemoWorkflow,
|
||||
demoWorkflowMessage,
|
||||
@@ -845,6 +849,7 @@ function App(): JSX.Element {
|
||||
mapSelectionQaRunning={mapSelectionQaRunning}
|
||||
mapSelectionQaError={mapSelectionQaError}
|
||||
mapSelectionQaResult={mapSelectionQaResult}
|
||||
latestMapSelectionQualityCheckId={latestMapSelectionQualityCheckId}
|
||||
availableMapDatasets={availableMapDatasets}
|
||||
selectedFeature={selectedMapFeature}
|
||||
onSelectMapArea={setSelectedMapAreaId}
|
||||
@@ -861,6 +866,7 @@ function App(): JSX.Element {
|
||||
onDeriveMapSelectionDataset={deriveMapSelectionDataset}
|
||||
onSelectMapQaReferenceDataset={setSelectedMapQaReferenceDatasetId}
|
||||
onRunMapSelectionQa={runMapSelectionQa}
|
||||
onOpenMapSelectionQualityEvidence={openMapSelectionQualityEvidence}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
|
||||
@@ -202,6 +202,7 @@ interface MapWorkspaceProps {
|
||||
mapSelectionQaRunning: boolean
|
||||
mapSelectionQaError: string | null
|
||||
mapSelectionQaResult: QaComparisonResult | null
|
||||
latestMapSelectionQualityCheckId: string | null
|
||||
availableMapDatasets: DatasetCreateResponse[]
|
||||
onSelectMapArea: (areaId: string) => void
|
||||
onOpenDatasetInMap: (dataset: DatasetCreateResponse) => void
|
||||
@@ -217,6 +218,7 @@ interface MapWorkspaceProps {
|
||||
onDeriveMapSelectionDataset: (bbox: VectorSelectionBBox) => void
|
||||
onSelectMapQaReferenceDataset: (datasetId: string) => void
|
||||
onRunMapSelectionQa: () => void
|
||||
onOpenMapSelectionQualityEvidence: () => void
|
||||
}
|
||||
|
||||
export function MapWorkspace({
|
||||
@@ -250,6 +252,7 @@ export function MapWorkspace({
|
||||
mapSelectionQaRunning,
|
||||
mapSelectionQaError,
|
||||
mapSelectionQaResult,
|
||||
latestMapSelectionQualityCheckId,
|
||||
availableMapDatasets,
|
||||
onSelectMapArea,
|
||||
onOpenDatasetInMap,
|
||||
@@ -265,6 +268,7 @@ export function MapWorkspace({
|
||||
onDeriveMapSelectionDataset,
|
||||
onSelectMapQaReferenceDataset,
|
||||
onRunMapSelectionQa,
|
||||
onOpenMapSelectionQualityEvidence,
|
||||
}: MapWorkspaceProps): JSX.Element {
|
||||
const [bboxSelectionMode, setBboxSelectionMode] = useState(false)
|
||||
const [firstSelectionCorner, setFirstSelectionCorner] = useState<[number, number] | null>(null)
|
||||
@@ -685,23 +689,65 @@ export function MapWorkspace({
|
||||
</button>
|
||||
{mapSelectionQaError ? <p className="error">{mapSelectionQaError}</p> : null}
|
||||
{mapSelectionQaResult ? (
|
||||
<div className="feature-extract-grid" aria-label="Map selection QA result">
|
||||
<div>
|
||||
<span>Precision</span>
|
||||
<strong>{mapSelectionQaResult.precision ?? 'n/a'}</strong>
|
||||
<div className="map-selection-qa-evidence" aria-label="Map selection QA result">
|
||||
<div className="panel-title-row">
|
||||
<div>
|
||||
<p className="eyebrow">QA evidence</p>
|
||||
<h4>Saved selection comparison</h4>
|
||||
</div>
|
||||
<button
|
||||
className="secondary-action"
|
||||
disabled={!latestMapSelectionQualityCheckId}
|
||||
type="button"
|
||||
onClick={onOpenMapSelectionQualityEvidence}
|
||||
>
|
||||
Open QA/QC evidence
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<span>Recall</span>
|
||||
<strong>{mapSelectionQaResult.recall ?? 'n/a'}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>F1</span>
|
||||
<strong>{mapSelectionQaResult.f1_score ?? 'n/a'}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>Quality check</span>
|
||||
<strong>{mapSelectionQaResult.status}</strong>
|
||||
<div className="feature-extract-grid">
|
||||
<div>
|
||||
<span>Precision</span>
|
||||
<strong>{mapSelectionQaResult.precision ?? 'n/a'}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>Recall</span>
|
||||
<strong>{mapSelectionQaResult.recall ?? 'n/a'}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>F1</span>
|
||||
<strong>{mapSelectionQaResult.f1_score ?? 'n/a'}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>Mean IoU</span>
|
||||
<strong>{mapSelectionQaResult.mean_iou ?? 'n/a'}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>Matches</span>
|
||||
<strong>{mapSelectionQaResult.matches}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>False positives</span>
|
||||
<strong>{mapSelectionQaResult.false_positives}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>False negatives</span>
|
||||
<strong>{mapSelectionQaResult.false_negatives}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>Quality check id</span>
|
||||
<strong>{latestMapSelectionQualityCheckId ?? 'not persisted'}</strong>
|
||||
</div>
|
||||
</div>
|
||||
{mapSelectionQaResult.warnings.length > 0 ? (
|
||||
<div className="map-selection-qa-warnings" aria-label="Map selection QA warnings">
|
||||
<span>Map selection QA warnings</span>
|
||||
<ul>
|
||||
{mapSelectionQaResult.warnings.map((warning) => (
|
||||
<li key={warning}>{warning}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
@@ -20,6 +20,7 @@ export function useMapSelectionQa({
|
||||
const [mapSelectionQaRunning, setMapSelectionQaRunning] = useState(false)
|
||||
const [mapSelectionQaError, setMapSelectionQaError] = useState<string | null>(null)
|
||||
const [mapSelectionQaResult, setMapQaResult] = useState<QaComparisonResult | null>(null)
|
||||
const [latestMapSelectionQualityCheckId, setLatestMapSelectionQualityCheckId] = useState<string | null>(null)
|
||||
|
||||
const runMapSelectionQa = async () => {
|
||||
if (!selectedProjectId) {
|
||||
@@ -42,6 +43,7 @@ export function useMapSelectionQa({
|
||||
setMapSelectionQaRunning(true)
|
||||
setMapSelectionQaError(null)
|
||||
setMapQaResult(null)
|
||||
setLatestMapSelectionQualityCheckId(null)
|
||||
try {
|
||||
const request: QaComparisonRequest = {
|
||||
candidate_dataset_id: latestSelectionDataset.id,
|
||||
@@ -65,6 +67,7 @@ export function useMapSelectionQa({
|
||||
return
|
||||
}
|
||||
setMapQaResult(parsed)
|
||||
setLatestMapSelectionQualityCheckId(parsed.quality_check_id ?? null)
|
||||
await loadQualityChecks(selectedProjectId)
|
||||
await loadProjectData(selectedProjectId)
|
||||
} catch (error) {
|
||||
@@ -79,6 +82,7 @@ export function useMapSelectionQa({
|
||||
mapSelectionQaRunning,
|
||||
mapSelectionQaError,
|
||||
mapSelectionQaResult,
|
||||
latestMapSelectionQualityCheckId,
|
||||
runMapSelectionQa,
|
||||
setSelectedMapQaReferenceDatasetId,
|
||||
}
|
||||
|
||||
@@ -2690,10 +2690,56 @@ button.entity-card {
|
||||
}
|
||||
|
||||
.map-selection-qa-surface .error,
|
||||
.map-selection-qa-surface .feature-extract-grid {
|
||||
.map-selection-qa-surface .map-selection-qa-evidence {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
.map-selection-qa-evidence {
|
||||
display: grid;
|
||||
gap: 0.62rem;
|
||||
min-width: 0;
|
||||
border: 1px solid rgba(15, 118, 110, 0.2);
|
||||
border-radius: 8px;
|
||||
padding: 0.68rem;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.map-selection-qa-evidence .panel-title-row {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.map-selection-qa-evidence .secondary-action {
|
||||
width: fit-content;
|
||||
max-width: 100%;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.map-selection-qa-warnings {
|
||||
display: grid;
|
||||
gap: 0.36rem;
|
||||
min-width: 0;
|
||||
border-top: 1px solid var(--line);
|
||||
padding-top: 0.58rem;
|
||||
}
|
||||
|
||||
.map-selection-qa-warnings span {
|
||||
color: var(--muted);
|
||||
font-size: 0.72rem;
|
||||
font-weight: 850;
|
||||
letter-spacing: 0.05em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.map-selection-qa-warnings ul {
|
||||
display: grid;
|
||||
gap: 0.28rem;
|
||||
margin: 0;
|
||||
padding-left: 1.1rem;
|
||||
color: var(--text);
|
||||
font-size: 0.82rem;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
@media (max-width: 720px) {
|
||||
.map-selection-qa-surface {
|
||||
grid-template-columns: 1fr;
|
||||
|
||||
@@ -601,6 +601,7 @@ export interface QaComparisonResult {
|
||||
unsupported_geometry: boolean
|
||||
unsupported_geometries: string[]
|
||||
generated_at: string
|
||||
quality_check_id?: string
|
||||
}
|
||||
|
||||
export interface MetricRead {
|
||||
|
||||
Reference in New Issue
Block a user