Add raster tile detection handoff
This commit is contained in:
@@ -277,6 +277,7 @@ AI Lab run controls explicitly explain when no raster dataset is available, inst
|
||||
- When the Map workspace has no active result layer, it lists ready vector/GeoJSON datasets as direct quick actions so populated demo projects can jump straight from the empty state to map inspection.
|
||||
- The Data catalog shows a compact selected/reference/candidate/source summary and scan-friendly badges. Persisted `reference` datasets are shown as Reference, non-reference vector/GeoJSON layers are shown as QA Candidates for workbench scanning, and raster/other uploads remain Source.
|
||||
- Dataset cards explain the recommended next action and use compact two-line action buttons for inspect, map, export/QA and metadata refresh. Disabled actions keep a visible reason, such as `Vector/GeoJSON only`.
|
||||
- Raster controls show the latest generated tile manifest path from persisted `raster.tile` jobs and can hand that path directly to Detection Lab with the selected raster dataset.
|
||||
- The QA/QC workspace shows candidate/reference handoff cards and resolves persisted quality-check dataset IDs back to dataset names when the datasets are loaded in the current project context.
|
||||
- The QA/QC workspace includes a selected-check evidence drilldown with candidate/reference provenance, false-positive/negative metric evidence, map handoff context and parameters/findings JSON.
|
||||
|
||||
|
||||
@@ -79,6 +79,7 @@ function App(): JSX.Element {
|
||||
datasetContent,
|
||||
jobs,
|
||||
rasterPreview,
|
||||
latestRasterTileManifestPath,
|
||||
selectedIntersectTargetId,
|
||||
selectedClipAreaId,
|
||||
rasterTileSize,
|
||||
@@ -254,6 +255,15 @@ function App(): JSX.Element {
|
||||
loadProjectData,
|
||||
loadQualityChecks,
|
||||
})
|
||||
const useRasterTileManifestForDetection = () => {
|
||||
const manifestPath = latestRasterTileManifestPath.trim()
|
||||
if (!manifestPath || !selectedDataset || selectedDataset.dataset_type !== 'raster') {
|
||||
return
|
||||
}
|
||||
setDetectionTileManifestPath(manifestPath)
|
||||
setSelectedDetectionDatasetId(selectedDataset.id)
|
||||
setActiveWorkspace('ai')
|
||||
}
|
||||
const {
|
||||
segmentationModels,
|
||||
loadingSegmentationModels,
|
||||
@@ -949,6 +959,7 @@ function App(): JSX.Element {
|
||||
selectedRasterMetadata,
|
||||
selectedRasterStats,
|
||||
rasterPreview,
|
||||
latestRasterTileManifestPath,
|
||||
rasterUnavailableMessage,
|
||||
selectedClipAreaId,
|
||||
selectedIntersectTargetId,
|
||||
@@ -987,6 +998,7 @@ function App(): JSX.Element {
|
||||
onRunRasterReproject: runRasterReproject,
|
||||
onRunRasterClip: runRasterClip,
|
||||
onRunRasterTile: runRasterTile,
|
||||
onUseTileManifestForDetection: useRasterTileManifestForDetection,
|
||||
onRunRasterNdvi: runRasterNdvi,
|
||||
onRunRasterNdwi: runRasterNdwi,
|
||||
onRunRasterNdbi: runRasterNdbi,
|
||||
|
||||
@@ -19,6 +19,7 @@ export interface DatasetDetailPanelProps {
|
||||
selectedRasterMetadata: RasterMetadataResponse | null
|
||||
selectedRasterStats: RasterStatsResponse | null
|
||||
rasterPreview: RasterPreviewResponse | null
|
||||
latestRasterTileManifestPath: string
|
||||
rasterUnavailableMessage: string | null
|
||||
selectedClipAreaId: string
|
||||
selectedIntersectTargetId: string
|
||||
@@ -57,6 +58,7 @@ export interface DatasetDetailPanelProps {
|
||||
onRunRasterReproject: () => void
|
||||
onRunRasterClip: () => void
|
||||
onRunRasterTile: () => void
|
||||
onUseTileManifestForDetection: () => void
|
||||
onRunRasterNdvi: () => void
|
||||
onRunRasterNdwi: () => void
|
||||
onRunRasterNdbi: () => void
|
||||
@@ -100,6 +102,7 @@ export function DatasetDetailPanel({
|
||||
selectedRasterMetadata,
|
||||
selectedRasterStats,
|
||||
rasterPreview,
|
||||
latestRasterTileManifestPath,
|
||||
rasterUnavailableMessage,
|
||||
selectedClipAreaId,
|
||||
selectedIntersectTargetId,
|
||||
@@ -138,6 +141,7 @@ export function DatasetDetailPanel({
|
||||
onRunRasterReproject,
|
||||
onRunRasterClip,
|
||||
onRunRasterTile,
|
||||
onUseTileManifestForDetection,
|
||||
onRunRasterNdvi,
|
||||
onRunRasterNdwi,
|
||||
onRunRasterNdbi,
|
||||
@@ -171,6 +175,7 @@ export function DatasetDetailPanel({
|
||||
selectedRasterMetadata={selectedRasterMetadata}
|
||||
selectedRasterStats={selectedRasterStats}
|
||||
rasterPreview={rasterPreview}
|
||||
latestRasterTileManifestPath={latestRasterTileManifestPath}
|
||||
rasterUnavailableMessage={rasterUnavailableMessage}
|
||||
selectedClipAreaId={selectedClipAreaId}
|
||||
rasterTileSize={rasterTileSize}
|
||||
@@ -203,6 +208,7 @@ export function DatasetDetailPanel({
|
||||
onRunRasterReproject={onRunRasterReproject}
|
||||
onRunRasterClip={onRunRasterClip}
|
||||
onRunRasterTile={onRunRasterTile}
|
||||
onUseTileManifestForDetection={onUseTileManifestForDetection}
|
||||
onRunRasterNdvi={onRunRasterNdvi}
|
||||
onRunRasterNdwi={onRunRasterNdwi}
|
||||
onRunRasterNdbi={onRunRasterNdbi}
|
||||
|
||||
@@ -6,6 +6,7 @@ interface RasterControlsProps {
|
||||
selectedRasterMetadata: RasterMetadataResponse | null
|
||||
selectedRasterStats: RasterStatsResponse | null
|
||||
rasterPreview: RasterPreviewResponse | null
|
||||
latestRasterTileManifestPath: string
|
||||
rasterUnavailableMessage: string | null
|
||||
selectedClipAreaId: string
|
||||
rasterTileSize: number
|
||||
@@ -38,6 +39,7 @@ interface RasterControlsProps {
|
||||
onRunRasterReproject: () => void
|
||||
onRunRasterClip: () => void
|
||||
onRunRasterTile: () => void
|
||||
onUseTileManifestForDetection: () => void
|
||||
onRunRasterNdvi: () => void
|
||||
onRunRasterNdwi: () => void
|
||||
onRunRasterNdbi: () => void
|
||||
@@ -57,6 +59,7 @@ export function RasterControls({
|
||||
selectedRasterMetadata,
|
||||
selectedRasterStats,
|
||||
rasterPreview,
|
||||
latestRasterTileManifestPath,
|
||||
rasterUnavailableMessage,
|
||||
selectedClipAreaId,
|
||||
rasterTileSize,
|
||||
@@ -89,6 +92,7 @@ export function RasterControls({
|
||||
onRunRasterReproject,
|
||||
onRunRasterClip,
|
||||
onRunRasterTile,
|
||||
onUseTileManifestForDetection,
|
||||
onRunRasterNdvi,
|
||||
onRunRasterNdwi,
|
||||
onRunRasterNdbi,
|
||||
@@ -156,11 +160,14 @@ export function RasterControls({
|
||||
))}
|
||||
</div>
|
||||
<div className="raster-manifest-handoff">
|
||||
<span>Tile manifest handoff</span>
|
||||
<span>Latest tile manifest</span>
|
||||
<p>
|
||||
Generated tile manifests remain backend artifacts and are referenced by path in detection/segmentation requests; this panel only prepares
|
||||
and explains the handoff.
|
||||
{latestRasterTileManifestPath ||
|
||||
'No tile manifest generated yet. Generate tiles before handing this raster to Detection Lab.'}
|
||||
</p>
|
||||
<button type="button" onClick={onUseTileManifestForDetection} disabled={!latestRasterTileManifestPath}>
|
||||
Use in Detection Lab
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
<section className="raster-guardrail-surface" aria-label="Processing guardrails">
|
||||
|
||||
@@ -27,6 +27,15 @@ function toRasterMetadata(metadata: Record<string, unknown> | null | undefined):
|
||||
return metadata as unknown as RasterMetadataResponse
|
||||
}
|
||||
|
||||
function extractRasterTileManifestPath(job: JobRead | null | undefined): string {
|
||||
const result = job?.result_json
|
||||
if (!result) {
|
||||
return ''
|
||||
}
|
||||
const manifestPath = result.manifest_path
|
||||
return typeof manifestPath === 'string' ? manifestPath : ''
|
||||
}
|
||||
|
||||
export function useDatasetWorkflow({
|
||||
selectedProjectId,
|
||||
areas,
|
||||
@@ -43,6 +52,7 @@ export function useDatasetWorkflow({
|
||||
const [datasetContent, setDatasetContent] = useState<GeoJSON.FeatureCollection | null>(null)
|
||||
const [jobs, setJobs] = useState<JobRead[]>([])
|
||||
const [rasterPreview, setRasterPreview] = useState<RasterPreviewResponse | null>(null)
|
||||
const [latestRasterTileManifestPath, setLatestRasterTileManifestPath] = useState('')
|
||||
const [selectedIntersectTargetId, setSelectedIntersectTargetId] = useState('')
|
||||
const [selectedClipAreaId, setSelectedClipAreaId] = useState('')
|
||||
const [rasterTileSize, setRasterTileSize] = useState(512)
|
||||
@@ -100,6 +110,10 @@ export function useDatasetWorkflow({
|
||||
const loadDatasetJobs = async (projectId: string, datasetId: string) => {
|
||||
const response = await jobsApi.list(projectId, { dataset_id: datasetId, limit: 20, offset: 0 })
|
||||
setJobs(response.items)
|
||||
const latestRasterTileJob = response.items.find(
|
||||
(job) => job.job_type === 'raster.tile' && extractRasterTileManifestPath(job),
|
||||
)
|
||||
setLatestRasterTileManifestPath(extractRasterTileManifestPath(latestRasterTileJob))
|
||||
}
|
||||
|
||||
const loadDatasetDetails = async (projectId: string, dataset: DatasetCreateResponse) => {
|
||||
@@ -111,6 +125,7 @@ export function useDatasetWorkflow({
|
||||
setSelectedRasterStats(null)
|
||||
setDatasetContent(null)
|
||||
setRasterPreview(null)
|
||||
setLatestRasterTileManifestPath('')
|
||||
setSelectedDatasetId(dataset.id)
|
||||
setJobs([])
|
||||
try {
|
||||
@@ -352,12 +367,17 @@ export function useDatasetWorkflow({
|
||||
return
|
||||
}
|
||||
try {
|
||||
await datasetsApi.rasterTile(selectedProjectId, selectedDatasetId, {
|
||||
const job = await datasetsApi.rasterTile(selectedProjectId, selectedDatasetId, {
|
||||
tile_size: rasterTileSize,
|
||||
overlap: rasterTileOverlap,
|
||||
output_name: rasterTileOutputName || undefined,
|
||||
})
|
||||
const manifestPath = extractRasterTileManifestPath(job)
|
||||
setLatestRasterTileManifestPath(manifestPath)
|
||||
await refreshSelectedDatasetAfterJob()
|
||||
if (manifestPath) {
|
||||
setLatestRasterTileManifestPath(manifestPath)
|
||||
}
|
||||
setDatasetDetailError(null)
|
||||
} catch (error) {
|
||||
setDatasetDetailError(formatError(error, 'Raster tile failed'))
|
||||
@@ -444,6 +464,7 @@ export function useDatasetWorkflow({
|
||||
setSelectedRasterStats(null)
|
||||
setDatasetContent(null)
|
||||
setRasterPreview(null)
|
||||
setLatestRasterTileManifestPath('')
|
||||
setJobs([])
|
||||
}
|
||||
|
||||
@@ -456,6 +477,7 @@ export function useDatasetWorkflow({
|
||||
datasetContent,
|
||||
jobs,
|
||||
rasterPreview,
|
||||
latestRasterTileManifestPath,
|
||||
selectedIntersectTargetId,
|
||||
selectedClipAreaId,
|
||||
rasterTileSize,
|
||||
|
||||
Reference in New Issue
Block a user