Make Mol the primary operating context
This commit is contained in:
@@ -7,6 +7,14 @@
|
|||||||
|
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## Sprint 177 Mol-first operating context (2026-07-13)
|
||||||
|
|
||||||
|
- Made Mol the explicit primary operating focus while retaining the broader Kempen as the validation and interoperability region.
|
||||||
|
- Added one centralized frontend focus definition for the empty-map center, new-project region, default AOI and persisted project/dataset recognition.
|
||||||
|
- Initial workbench selection now prefers real persisted Mol data without overriding an explicit current or newly created project selection.
|
||||||
|
- Moved Mol to the front of default operator sample preparation and added AOI slugs to future detection quality-matrix project names.
|
||||||
|
- Kept APIs, migrations, persistence, providers, AI dependencies and model behavior unchanged.
|
||||||
|
|
||||||
## Sprint 176 Detection false-positive visual review gate (2026-07-13)
|
## Sprint 176 Detection false-positive visual review gate (2026-07-13)
|
||||||
|
|
||||||
- Enriched existing QA evidence GeoJSON with persisted detection and segmentation provenance without changing its endpoint or canonical envelope.
|
- Enriched existing QA evidence GeoJSON with persisted detection and segmentation provenance without changing its endpoint or canonical envelope.
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
GeoIntel Kempen is a GeoAI Workbench for the Belgian Kempen. It is designed as a portfolio-grade project combining GIS, remote sensing, raster/vector processing, computer vision, QA/QC and geospatial exports.
|
GeoIntel Kempen is a GeoAI Workbench for the Belgian Kempen. It is designed as a portfolio-grade project combining GIS, remote sensing, raster/vector processing, computer vision, QA/QC and geospatial exports.
|
||||||
|
|
||||||
|
The primary operating focus is Mol. New workbench contexts, AOIs and operator samples start there, while the broader Kempen remains fully supported for cross-area validation and regional interoperability.
|
||||||
|
|
||||||
GeoIntel is not a generic dashboard or chatbot. The core product is:
|
GeoIntel is not a generic dashboard or chatbot. The core product is:
|
||||||
|
|
||||||
> data → processing → geospatial output → QA/QC → export
|
> data → processing → geospatial output → QA/QC → export
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[2]
|
||||||
|
|
||||||
|
|
||||||
|
def test_frontend_declares_mol_as_primary_operating_focus() -> None:
|
||||||
|
focus = (ROOT / "frontend" / "src" / "config" / "primaryFocus.ts").read_text(
|
||||||
|
encoding="utf-8"
|
||||||
|
)
|
||||||
|
project_hook = (ROOT / "frontend" / "src" / "hooks" / "useProjectWorkspace.ts").read_text(
|
||||||
|
encoding="utf-8"
|
||||||
|
)
|
||||||
|
map_source = (ROOT / "frontend" / "src" / "components" / "GeoMap.tsx").read_text(
|
||||||
|
encoding="utf-8"
|
||||||
|
)
|
||||||
|
app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8")
|
||||||
|
|
||||||
|
assert "PRIMARY_FOCUS_LABEL = 'Mol'" in focus
|
||||||
|
assert "PRIMARY_FOCUS_REGION = 'Mol, Kempen'" in focus
|
||||||
|
assert "[5.1167, 51.1919]" in focus
|
||||||
|
assert "isPrimaryFocusProjectData" in focus
|
||||||
|
assert "isPrimaryFocusProjectData(project, data.datasets)" in project_hook
|
||||||
|
assert "PRIMARY_FOCUS_AREA_NAME" in project_hook
|
||||||
|
assert "PRIMARY_FOCUS_AREA_GEOJSON" in project_hook
|
||||||
|
assert "4.35,51.28" not in project_hook
|
||||||
|
assert "center: PRIMARY_FOCUS_CENTER" in map_source
|
||||||
|
assert "GeoAI Workbench · Mol / Kempen" in app
|
||||||
|
|
||||||
|
|
||||||
|
def test_operator_workflows_put_mol_first_and_name_future_projects() -> None:
|
||||||
|
samples = (ROOT / "scripts" / "prepare_operator_real_data_samples.py").read_text(
|
||||||
|
encoding="utf-8"
|
||||||
|
)
|
||||||
|
matrix = (ROOT / "scripts" / "run_detection_quality_matrix.sh").read_text(
|
||||||
|
encoding="utf-8"
|
||||||
|
)
|
||||||
|
multi_matrix = (
|
||||||
|
ROOT / "scripts" / "run_multi_sample_detection_quality_matrix.sh"
|
||||||
|
).read_text(encoding="utf-8")
|
||||||
|
|
||||||
|
assert samples.index('"mol": OperatorSample(') < samples.index('"geel": OperatorSample(')
|
||||||
|
assert 'QUALITY_SAMPLE_SLUG="${QUALITY_SAMPLE_SLUG:-}"' in matrix
|
||||||
|
assert "sample ${QUALITY_SAMPLE_SLUG}" in matrix
|
||||||
|
assert 'QUALITY_SAMPLE_SLUG="${sample_slug}"' in multi_matrix
|
||||||
|
|
||||||
|
|
||||||
|
def test_product_docs_record_mol_primary_focus_without_dropping_kempen_scope() -> None:
|
||||||
|
readme = (ROOT / "README.md").read_text(encoding="utf-8")
|
||||||
|
vision = (ROOT / "docs" / "PRODUCT_VISION.md").read_text(encoding="utf-8")
|
||||||
|
|
||||||
|
assert "primary operating focus is Mol" in readme
|
||||||
|
assert "Mol is de primaire operationele focus" in vision
|
||||||
|
assert "broader Kempen" in readme
|
||||||
@@ -7233,4 +7233,28 @@ Open:
|
|||||||
|
|
||||||
## Next recommended pass
|
## Next recommended pass
|
||||||
|
|
||||||
- Re-export the seven-AOI evidence portfolio from the deployed backend, render the Turnhout/Herentals/Geel sheets and inspect the real orthophoto evidence. Keep all CSV decisions `unreviewed` until an operator makes an explicit visual classification; do not start another model training run yet.
|
- Re-export the seven-AOI evidence portfolio from the deployed backend, render the Mol/Geel/Turnhout sheets and inspect the real orthophoto evidence. Keep all CSV decisions `unreviewed` until an operator makes an explicit visual classification; do not start another model training run yet.
|
||||||
|
|
||||||
|
# Sprint 177 - Mol-first operating context
|
||||||
|
|
||||||
|
## Product and workbench focus
|
||||||
|
|
||||||
|
- Declared Mol as the primary operational context while preserving the broader Kempen for cross-area validation and regional interoperability.
|
||||||
|
- Centralized Mol coordinates, default region and a compact 1 km EPSG:4326 MultiPolygon AOI in the frontend focus configuration.
|
||||||
|
- Initial project discovery now recognizes Mol from project metadata or persisted dataset provenance and inspects those candidates first.
|
||||||
|
- Explicitly selected and newly created projects still take precedence, so the focus rule does not fight operator intent.
|
||||||
|
- The empty MapLibre workbench starts over Mol; loaded AOIs and data continue to determine map bounds normally.
|
||||||
|
|
||||||
|
## Operator provenance
|
||||||
|
|
||||||
|
- Moved Mol to the first position in default real-data sample preparation without dropping any existing Kempen sample.
|
||||||
|
- Multi-sample quality runs now forward their AOI slug, and future persisted matrix project names retain that origin instead of appearing as anonymous model/tile runs.
|
||||||
|
- No provider fetch, AI output, QA metric, migration, API contract or model configuration was changed.
|
||||||
|
|
||||||
|
## Validation
|
||||||
|
|
||||||
|
- Focused Mol-primary regression coverage: `3 passed`.
|
||||||
|
- `bash scripts/run_readiness_check.sh`: passed with `481` backend tests, API-contract audit, single Alembic head, frontend typecheck and production build.
|
||||||
|
- Frontend production bundles: app `217.69 kB`, React vendor `140.74 kB`, MapLibre `801.82 kB` before gzip.
|
||||||
|
- `python -m alembic upgrade head --sql`: complete migration chain rendered through single head `202606120900`.
|
||||||
|
- Both changed matrix scripts pass `bash -n`; no migration file changed.
|
||||||
|
|||||||
@@ -32,11 +32,13 @@ GeoIntel moet bewijzen dat de ontwikkelaar de volledige keten begrijpt:
|
|||||||
|
|
||||||
De Kempen vormen de afgebakende regio. Dit maakt het project concreet, realistisch en demo-baar. Voorbeelden:
|
De Kempen vormen de afgebakende regio. Dit maakt het project concreet, realistisch en demo-baar. Voorbeelden:
|
||||||
|
|
||||||
- gebouwdetectie in Geel
|
- gebouwdetectie en infrastructuuranalyse in Mol
|
||||||
- infrastructuurdruk rond Mol
|
- vergelijkende validatie in Geel
|
||||||
- verstedelijking rond Turnhout
|
- verstedelijking rond Turnhout
|
||||||
- natuurfragmentatie rond Kasterlee of Retie
|
- natuurfragmentatie rond Kasterlee of Retie
|
||||||
|
|
||||||
|
Mol is de primaire operationele focus. De standaardkaart, nieuwe AOI-context en operator-volgorde vertrekken daarom vanuit Mol. De bredere Kempen blijft bewust onderdeel van de productscope voor onafhankelijke validatie, overdraagbaarheid en regionale vergelijking.
|
||||||
|
|
||||||
## Richting
|
## Richting
|
||||||
|
|
||||||
De definitieve richting is:
|
De definitieve richting is:
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ This file now starts with the current implementation status. Older preparation/b
|
|||||||
|
|
||||||
## Current implementation status
|
## Current implementation status
|
||||||
|
|
||||||
|
- [x] Make Mol the primary workbench, AOI and operator-sample context while preserving broader Kempen coverage.
|
||||||
- [x] Backend FastAPI foundation, health endpoint and service structure.
|
- [x] Backend FastAPI foundation, health endpoint and service structure.
|
||||||
- [x] React/TypeScript frontend foundation and MapLibre workbench.
|
- [x] React/TypeScript frontend foundation and MapLibre workbench.
|
||||||
- [x] Map layer visibility, opacity and feature property inspection.
|
- [x] Map layer visibility, opacity and feature property inspection.
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
React + TypeScript + MapLibre foundation for project/area/dataset workflow.
|
React + TypeScript + MapLibre foundation for project/area/dataset workflow.
|
||||||
|
|
||||||
|
Mol is the primary operating context. On a fresh session the workbench prefers a persisted Mol project or dataset, centers an empty MapLibre view on Mol and pre-fills a compact Mol AOI. Explicit project and dataset selections remain authoritative, and all broader Kempen workflows remain available.
|
||||||
|
|
||||||
The workbench now uses a task-based shell instead of a single long panel stack. `App.tsx` still owns shared orchestration state, but the UI is organized into Overview, Data, Map, QA/QC, AI Labs, Exports and System workspaces with a persistent top context bar and right-side dataset inspector.
|
The workbench now uses a task-based shell instead of a single long panel stack. `App.tsx` still owns shared orchestration state, but the UI is organized into Overview, Data, Map, QA/QC, AI Labs, Exports and System workspaces with a persistent top context bar and right-side dataset inspector.
|
||||||
|
|
||||||
The shell has a calmer V1 workbench density pass: the duplicated workspace command bar is hidden, the sidebar is more compact, the top context bar is quieter, readiness tiles are smaller and panels use softer borders/shadows. This keeps the same workflows and API calls while reducing visual noise on desktop and mobile.
|
The shell has a calmer V1 workbench density pass: the duplicated workspace command bar is hidden, the sidebar is more compact, the top context bar is quieter, readiness tiles are smaller and panels use softer borders/shadows. This keeps the same workflows and API calls while reducing visual noise on desktop and mobile.
|
||||||
|
|||||||
@@ -643,7 +643,7 @@ function App(): JSX.Element {
|
|||||||
</a>
|
</a>
|
||||||
<header className="workbench-topbar">
|
<header className="workbench-topbar">
|
||||||
<div className="brand-block">
|
<div className="brand-block">
|
||||||
<p className="eyebrow">GeoAI Workbench</p>
|
<p className="eyebrow">GeoAI Workbench · Mol / Kempen</p>
|
||||||
<h1>GeoIntel Kempen</h1>
|
<h1>GeoIntel Kempen</h1>
|
||||||
</div>
|
</div>
|
||||||
<div className="context-bar" aria-label="Active workbench context">
|
<div className="context-bar" aria-label="Active workbench context">
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { useEffect, useRef, useState } from 'react'
|
import { useEffect, useRef, useState } from 'react'
|
||||||
import maplibregl from 'maplibre-gl'
|
import maplibregl from 'maplibre-gl'
|
||||||
import 'maplibre-gl/dist/maplibre-gl.css'
|
import 'maplibre-gl/dist/maplibre-gl.css'
|
||||||
|
import { PRIMARY_FOCUS_CENTER } from '../config/primaryFocus'
|
||||||
|
|
||||||
interface GeoMapProps {
|
interface GeoMapProps {
|
||||||
data: GeoJSON.FeatureCollection | null
|
data: GeoJSON.FeatureCollection | null
|
||||||
@@ -162,8 +163,8 @@ function GeoMap({
|
|||||||
const map = new maplibregl.Map({
|
const map = new maplibregl.Map({
|
||||||
container: containerRef.current,
|
container: containerRef.current,
|
||||||
style: defaultMapStyle(),
|
style: defaultMapStyle(),
|
||||||
center: [5.3, 51.3],
|
center: PRIMARY_FOCUS_CENTER,
|
||||||
zoom: 9,
|
zoom: 11,
|
||||||
attributionControl: false,
|
attributionControl: false,
|
||||||
})
|
})
|
||||||
map.addControl(new maplibregl.NavigationControl(), 'top-right')
|
map.addControl(new maplibregl.NavigationControl(), 'top-right')
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import type { DatasetCreateResponse, ProjectRead } from '../types'
|
||||||
|
|
||||||
|
export const PRIMARY_FOCUS_LABEL = 'Mol'
|
||||||
|
export const PRIMARY_FOCUS_REGION = 'Mol, Kempen'
|
||||||
|
export const PRIMARY_FOCUS_CENTER: [number, number] = [5.1167, 51.1919]
|
||||||
|
export const PRIMARY_FOCUS_AREA_NAME = 'Mol primary AOI'
|
||||||
|
export const PRIMARY_FOCUS_AREA_GEOJSON =
|
||||||
|
'{"type":"MultiPolygon","coordinates":[[[[5.109476746222376,51.18745107550715],[5.123924622861509,51.18745107550715],[5.123924622861509,51.19634848029239],[5.109476746222376,51.19634848029239],[5.109476746222376,51.18745107550715]]]]}'
|
||||||
|
|
||||||
|
const PRIMARY_FOCUS_TOKEN = /(^|[^a-z0-9])mol([^a-z0-9]|$)/i
|
||||||
|
|
||||||
|
function containsPrimaryFocus(values: Array<string | null | undefined>): boolean {
|
||||||
|
return values.some((value) => Boolean(value && PRIMARY_FOCUS_TOKEN.test(value)))
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isPrimaryFocusProject(project: ProjectRead): boolean {
|
||||||
|
return containsPrimaryFocus([project.name, project.description, project.region])
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isPrimaryFocusProjectData(
|
||||||
|
project: ProjectRead,
|
||||||
|
datasets: DatasetCreateResponse[],
|
||||||
|
): boolean {
|
||||||
|
if (isPrimaryFocusProject(project)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return datasets.some((dataset) =>
|
||||||
|
containsPrimaryFocus([
|
||||||
|
dataset.name,
|
||||||
|
dataset.original_filename,
|
||||||
|
dataset.stored_filename,
|
||||||
|
dataset.storage_path,
|
||||||
|
dataset.source_name,
|
||||||
|
dataset.reference_layer_name,
|
||||||
|
]),
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,4 +1,11 @@
|
|||||||
import { FormEvent, useMemo, useState } from 'react'
|
import { FormEvent, useMemo, useState } from 'react'
|
||||||
|
import {
|
||||||
|
PRIMARY_FOCUS_AREA_GEOJSON,
|
||||||
|
PRIMARY_FOCUS_AREA_NAME,
|
||||||
|
PRIMARY_FOCUS_REGION,
|
||||||
|
isPrimaryFocusProject,
|
||||||
|
isPrimaryFocusProjectData,
|
||||||
|
} from '../config/primaryFocus'
|
||||||
import { areasApi } from '../services/api/areas'
|
import { areasApi } from '../services/api/areas'
|
||||||
import { datasetsApi } from '../services/api/datasets'
|
import { datasetsApi } from '../services/api/datasets'
|
||||||
import { projectsApi } from '../services/api/projects'
|
import { projectsApi } from '../services/api/projects'
|
||||||
@@ -31,12 +38,11 @@ export function useProjectWorkspace() {
|
|||||||
const [projectForm, setProjectForm] = useState<ProjectCreate>({
|
const [projectForm, setProjectForm] = useState<ProjectCreate>({
|
||||||
name: '',
|
name: '',
|
||||||
description: '',
|
description: '',
|
||||||
region: 'Kempen',
|
region: PRIMARY_FOCUS_REGION,
|
||||||
})
|
})
|
||||||
const [areaForm, setAreaForm] = useState({
|
const [areaForm, setAreaForm] = useState({
|
||||||
name: 'Demo AOI',
|
name: PRIMARY_FOCUS_AREA_NAME,
|
||||||
geometry:
|
geometry: PRIMARY_FOCUS_AREA_GEOJSON,
|
||||||
'{"type":"MultiPolygon","coordinates":[[[[4.35,51.28],[4.55,51.28],[4.55,51.46],[4.35,51.46],[4.35,51.28]]]]}',
|
|
||||||
crs: 'EPSG:4326',
|
crs: 'EPSG:4326',
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -60,19 +66,32 @@ export function useProjectWorkspace() {
|
|||||||
if (selectedProjectId && items.some((project) => project.id === selectedProjectId)) {
|
if (selectedProjectId && items.some((project) => project.id === selectedProjectId)) {
|
||||||
return selectedProjectId
|
return selectedProjectId
|
||||||
}
|
}
|
||||||
|
const primaryProjects = items.filter(isPrimaryFocusProject)
|
||||||
const demoProjects = items.filter(isDemoProject)
|
const demoProjects = items.filter(isDemoProject)
|
||||||
const candidates = [...demoProjects, ...items.filter((project) => !isDemoProject(project))].slice(0, 8)
|
const candidates = Array.from(
|
||||||
|
new Map(
|
||||||
|
[...primaryProjects, ...demoProjects, ...items].map((project) => [project.id, project]),
|
||||||
|
).values(),
|
||||||
|
).slice(0, 12)
|
||||||
|
const inspectedCandidates: Array<{
|
||||||
|
project: ProjectRead
|
||||||
|
data: { areas: AreaRead[]; datasets: DatasetCreateResponse[] }
|
||||||
|
}> = []
|
||||||
for (const project of candidates) {
|
for (const project of candidates) {
|
||||||
try {
|
try {
|
||||||
const data = await fetchProjectData(project.id)
|
const data = await fetchProjectData(project.id)
|
||||||
if (data.areas.length > 0 && data.datasets.length > 0) {
|
inspectedCandidates.push({ project, data })
|
||||||
|
if (data.datasets.length > 0 && isPrimaryFocusProjectData(project, data.datasets)) {
|
||||||
return project.id
|
return project.id
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
// Project list should still render if a candidate's detail endpoints are temporarily unavailable.
|
// Project list should still render if a candidate's detail endpoints are temporarily unavailable.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return demoProjects[0]?.id ?? items[0]?.id ?? null
|
const completeContext = inspectedCandidates.find(
|
||||||
|
({ data }) => data.areas.length > 0 && data.datasets.length > 0,
|
||||||
|
)
|
||||||
|
return completeContext?.project.id ?? primaryProjects[0]?.id ?? demoProjects[0]?.id ?? items[0]?.id ?? null
|
||||||
}
|
}
|
||||||
|
|
||||||
const loadProjects = async (preferredProjectId?: string | null) => {
|
const loadProjects = async (preferredProjectId?: string | null) => {
|
||||||
@@ -120,7 +139,7 @@ export function useProjectWorkspace() {
|
|||||||
const createdProject = await projectsApi.create({
|
const createdProject = await projectsApi.create({
|
||||||
name: projectForm.name.trim(),
|
name: projectForm.name.trim(),
|
||||||
description: projectForm.description?.trim() || undefined,
|
description: projectForm.description?.trim() || undefined,
|
||||||
region: projectForm.region?.trim() || 'Kempen',
|
region: projectForm.region?.trim() || PRIMARY_FOCUS_REGION,
|
||||||
})
|
})
|
||||||
setProjectForm((previous) => ({ ...previous, name: '', description: '' }))
|
setProjectForm((previous) => ({ ...previous, name: '', description: '' }))
|
||||||
setSelectedProjectId(createdProject.id)
|
setSelectedProjectId(createdProject.id)
|
||||||
|
|||||||
@@ -64,18 +64,18 @@ class OperatorSample:
|
|||||||
|
|
||||||
|
|
||||||
SAMPLES: dict[str, OperatorSample] = {
|
SAMPLES: dict[str, OperatorSample] = {
|
||||||
"geel": OperatorSample(
|
|
||||||
slug="geel",
|
|
||||||
display_name="Geel center",
|
|
||||||
center_lon=4.991,
|
|
||||||
center_lat=51.162,
|
|
||||||
),
|
|
||||||
"mol": OperatorSample(
|
"mol": OperatorSample(
|
||||||
slug="mol",
|
slug="mol",
|
||||||
display_name="Mol center",
|
display_name="Mol center",
|
||||||
center_lon=5.1167,
|
center_lon=5.1167,
|
||||||
center_lat=51.1919,
|
center_lat=51.1919,
|
||||||
),
|
),
|
||||||
|
"geel": OperatorSample(
|
||||||
|
slug="geel",
|
||||||
|
display_name="Geel center",
|
||||||
|
center_lon=4.991,
|
||||||
|
center_lat=51.162,
|
||||||
|
),
|
||||||
"turnhout": OperatorSample(
|
"turnhout": OperatorSample(
|
||||||
slug="turnhout",
|
slug="turnhout",
|
||||||
display_name="Turnhout center",
|
display_name="Turnhout center",
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ Usage:
|
|||||||
QUALITY_TILE_SIZES="512 640" \
|
QUALITY_TILE_SIZES="512 640" \
|
||||||
QUALITY_TILE_OVERLAPS="64" \
|
QUALITY_TILE_OVERLAPS="64" \
|
||||||
QUALITY_THRESHOLDS="0.50 0.25 0.15" \
|
QUALITY_THRESHOLDS="0.50 0.25 0.15" \
|
||||||
|
QUALITY_SAMPLE_SLUG="mol" \
|
||||||
bash scripts/run_detection_quality_matrix.sh [base_url]
|
bash scripts/run_detection_quality_matrix.sh [base_url]
|
||||||
|
|
||||||
or:
|
or:
|
||||||
@@ -25,6 +26,7 @@ Optional environment:
|
|||||||
QUALITY_TILE_OVERLAPS Space/comma separated raster tile overlaps, default: 64.
|
QUALITY_TILE_OVERLAPS Space/comma separated raster tile overlaps, default: 64.
|
||||||
QUALITY_THRESHOLDS Space/comma separated confidence thresholds, default: 0.50 0.25 0.15.
|
QUALITY_THRESHOLDS Space/comma separated confidence thresholds, default: 0.50 0.25 0.15.
|
||||||
QUALITY_OUTPUT_DIR Output directory, default: artifacts/detection-quality-matrix/<timestamp>.
|
QUALITY_OUTPUT_DIR Output directory, default: artifacts/detection-quality-matrix/<timestamp>.
|
||||||
|
QUALITY_SAMPLE_SLUG Optional AOI slug included in persisted project names.
|
||||||
REAL_IOU_THRESHOLD QA IoU threshold, default inherited by the underlying workflow.
|
REAL_IOU_THRESHOLD QA IoU threshold, default inherited by the underlying workflow.
|
||||||
|
|
||||||
This script never downloads models and never uses fixture detections. It repeats
|
This script never downloads models and never uses fixture detections. It repeats
|
||||||
@@ -42,6 +44,7 @@ QUALITY_MODEL_ASSET_IDS="${QUALITY_MODEL_ASSET_IDS:-${REAL_MODEL_ASSET_ID:-__act
|
|||||||
QUALITY_TILE_SIZES="${QUALITY_TILE_SIZES:-640}"
|
QUALITY_TILE_SIZES="${QUALITY_TILE_SIZES:-640}"
|
||||||
QUALITY_TILE_OVERLAPS="${QUALITY_TILE_OVERLAPS:-64}"
|
QUALITY_TILE_OVERLAPS="${QUALITY_TILE_OVERLAPS:-64}"
|
||||||
QUALITY_THRESHOLDS="${QUALITY_THRESHOLDS:-0.50 0.25 0.15}"
|
QUALITY_THRESHOLDS="${QUALITY_THRESHOLDS:-0.50 0.25 0.15}"
|
||||||
|
QUALITY_SAMPLE_SLUG="${QUALITY_SAMPLE_SLUG:-}"
|
||||||
QUALITY_OUTPUT_DIR="${QUALITY_OUTPUT_DIR:-artifacts/detection-quality-matrix/$(date -u +%Y%m%dT%H%M%SZ)}"
|
QUALITY_OUTPUT_DIR="${QUALITY_OUTPUT_DIR:-artifacts/detection-quality-matrix/$(date -u +%Y%m%dT%H%M%SZ)}"
|
||||||
|
|
||||||
if [ "${BASE_URL}" = "-h" ] || [ "${BASE_URL}" = "--help" ]; then
|
if [ "${BASE_URL}" = "-h" ] || [ "${BASE_URL}" = "--help" ]; then
|
||||||
@@ -174,9 +177,13 @@ while IFS=$'\t' read -r model_request tile_size tile_overlap threshold run_label
|
|||||||
if [ "${model_request}" = "__active__" ]; then
|
if [ "${model_request}" = "__active__" ]; then
|
||||||
model_env=""
|
model_env=""
|
||||||
fi
|
fi
|
||||||
|
project_sample_label=""
|
||||||
|
if [ -n "${QUALITY_SAMPLE_SLUG}" ]; then
|
||||||
|
project_sample_label=" sample ${QUALITY_SAMPLE_SLUG}"
|
||||||
|
fi
|
||||||
|
|
||||||
echo "-- Matrix run ${run_index}: model=${model_request} tile=${tile_size} overlap=${tile_overlap} threshold=${threshold} --"
|
echo "-- Matrix run ${run_index}: model=${model_request} tile=${tile_size} overlap=${tile_overlap} threshold=${threshold} --"
|
||||||
if ! REAL_PROJECT_NAME="GeoIntel Detection Quality Matrix ${model_request} tile ${tile_size} overlap ${tile_overlap} threshold ${threshold}" \
|
if ! REAL_PROJECT_NAME="GeoIntel Detection Quality Matrix${project_sample_label} ${model_request} tile ${tile_size} overlap ${tile_overlap} threshold ${threshold}" \
|
||||||
REAL_MODEL_ASSET_ID="${model_env}" \
|
REAL_MODEL_ASSET_ID="${model_env}" \
|
||||||
REAL_TILE_SIZE="${tile_size}" \
|
REAL_TILE_SIZE="${tile_size}" \
|
||||||
REAL_TILE_OVERLAP="${tile_overlap}" \
|
REAL_TILE_OVERLAP="${tile_overlap}" \
|
||||||
|
|||||||
@@ -137,6 +137,7 @@ while IFS=$'\t' read -r sample_slug raster_path reference_path reference_feature
|
|||||||
echo "-- Sample ${sample_slug}: reference_features=${reference_feature_count} --"
|
echo "-- Sample ${sample_slug}: reference_features=${reference_feature_count} --"
|
||||||
REAL_RASTER_PATH="${raster_path}" \
|
REAL_RASTER_PATH="${raster_path}" \
|
||||||
REAL_REFERENCE_VECTOR_PATH="${reference_path}" \
|
REAL_REFERENCE_VECTOR_PATH="${reference_path}" \
|
||||||
|
QUALITY_SAMPLE_SLUG="${sample_slug}" \
|
||||||
QUALITY_OUTPUT_DIR="${sample_output_dir}" \
|
QUALITY_OUTPUT_DIR="${sample_output_dir}" \
|
||||||
bash scripts/run_detection_quality_matrix.sh "${BASE_URL}"
|
bash scripts/run_detection_quality_matrix.sh "${BASE_URL}"
|
||||||
done < "${sample_manifest_tsv}"
|
done < "${sample_manifest_tsv}"
|
||||||
|
|||||||
Reference in New Issue
Block a user