From 404ab607688ce9c02a0b1155406446cf6026d664 Mon Sep 17 00:00:00 2001
From: shuishen <1109946754@qq.com>
Date: Mon, 17 Aug 2026 17:59:09 +0800
Subject: [PATCH] feat:变化检测的

---
 apps/workbench-console/src/api/artifacts.ts |   26 ++++++++++++++++++++++++++
 1 files changed, 26 insertions(+), 0 deletions(-)

diff --git a/apps/workbench-console/src/api/artifacts.ts b/apps/workbench-console/src/api/artifacts.ts
index 88a563a..6f68f57 100644
--- a/apps/workbench-console/src/api/artifacts.ts
+++ b/apps/workbench-console/src/api/artifacts.ts
@@ -6,6 +6,12 @@
 export interface SemanticRun { capability: string; classification: string; task_id?: string; task_name?: string; created_at?: string; geoai_version: string; method: string; model: string; device: string; thresholds: Record<string, number>; input_count: number; processed_images: number; elapsed_seconds: number; images: SemanticImage[]; classes: Array<{ id: number; key: string; label: string; color: number[] }>; limitations: string[]; input_dir?: string; raw_input_dir?: string; }
 export interface SemanticCase { id: string; label: string; note: string; artifactRoot: string; inputRoot: string; rawInputRoot: string; createdAt: string; run: SemanticRun; }
 export interface SemanticTask { id: string; name: string; status: string; selectable: boolean; input_sensor: string[]; classes: string[]; project_use?: string; required_evidence?: string[]; prohibited_claim?: string; }
+export interface MeasurementImage { file: string; width: number; height: number; raster_file: string; preview_file: string; vector_file: string; csv_file: string; object_count: number; class_counts: Record<string, number>; total_area: number; total_perimeter: number; area_unit: string; length_unit: string; measurement_basis: string; crs: string | null; georeferenced: boolean; vectorizer: string; elapsed_seconds: number; }
+export interface MeasurementRun { capability: string; classification: string; created_at?: string; geoai_version: string; method: string; model: string; device: string; input_count: number; processed_images: number; elapsed_seconds: number; images: MeasurementImage[]; limitations: string[]; input_dir?: string; raw_input_dir?: string; }
+export interface MeasurementCase { id: string; label: string; note: string; artifactRoot: string; createdAt: string; run: MeasurementRun; }
+export interface ChangeFeature { type: "Feature"; properties: { feature_id?: number; area_pixels?: number; mean_probability?: number; max_probability?: number; bounds_pixel?: number[]; confidence?: number; class?: number }; geometry: { type: string; coordinates: unknown }; }
+export interface ChangeRun { schema_version: number; capability: string; classification: string; geoai_version: string; method: string; model: string; device: string; thresholds: Record<string, number>; tile_size: number; overlap: number; input_files: string[]; input_shape: number[]; processed_shape: number[]; registration: { method: string; matches: number; inliers: number; inlier_ratio: number; valid_ratio: number }; valid_pixel_ratio: number; raw_changed_pixels: number; changed_pixels: number; changed_pixel_ratio: number; vector_feature_count: number; georeferenced: boolean; coordinate_basis: string; crs: string | null; elapsed_seconds: number; limitations: string[]; artifacts: { probability_raster: string; raw_mask_raster: string; mask_raster: string; overlay: string; vector: string; features: string }; created_at?: string; }
+export interface ChangeCase { id: string; label: string; note: string; artifactRoot: string; beforeImage: string; afterImage: string; createdAt: string; run: ChangeRun; features: ChangeFeature[]; }
 export interface TrajectoryEvent { event_id: string; event_type: string; track_ids: string[]; start_time: string; end_time: string; duration_seconds: number; }
 export interface TrajectoryCaseRun { case_id: string; input_count: number; track_count: number; event_count: number; dropped_duplicate_observations: number; elapsed_seconds: number; device: string; input: string; thresholds: Record<string, number>; created_at?: string; }
 export interface TrajectorySummary { track_id: string; entity_type: string; point_count: string; distance_m: string; average_speed_mps: string; behavior_labels: string; }
@@ -14,6 +20,8 @@
 interface TrajectoryDefinition extends CaseDefinition { showSpatialContext: boolean; showFlyableZones: boolean; }
 interface DetectionDefinition extends CaseDefinition { inputRoot: string; }
 interface SemanticDefinition extends CaseDefinition { inputRoot: string; rawInputRoot: string; }
+interface MeasurementDefinition extends CaseDefinition {}
+interface ChangeDefinition extends CaseDefinition { beforeImage: string; afterImage: string; }
 export interface UploadFilePayload { name: string; content: string; }
 
 export const artifactUrl = (path: string) => `/${path.replace(/\\/g, "/").split("/").map(encodeURIComponent).join("/")}`;
@@ -55,8 +63,26 @@
   return (await getJson<{ tasks: SemanticTask[] }>("api/semantic-mapping/tasks")).tasks;
 }
 
+export async function loadMeasurementArtifacts(): Promise<Record<string, MeasurementCase>> {
+  const { runs } = await getJson<{ runs: MeasurementDefinition[] }>("api/spatial-measurement/runs");
+  const cases = await Promise.all(runs.map(async (definition) => ({ ...definition, run: await getJson<MeasurementRun>(`${definition.artifactRoot}/run_metadata.json`) } satisfies MeasurementCase)));
+  return Object.fromEntries(cases.map((item) => [item.id, item]));
+}
+
+export async function loadChangeArtifacts(): Promise<Record<string, ChangeCase>> {
+  const { runs } = await getJson<{ runs: ChangeDefinition[] }>("api/change-detection/runs");
+  const cases = await Promise.all(runs.map(async (definition) => {
+    const run = await getJson<ChangeRun>(`${definition.artifactRoot}/run_metadata.json`);
+    const vector = await getJson<{ features?: ChangeFeature[] }>(`${definition.artifactRoot}/${run.artifacts.vector}`);
+    return { ...definition, run, features: vector.features ?? [] } satisfies ChangeCase;
+  }));
+  return Object.fromEntries(cases.map((item) => [item.id, item]));
+}
+
 async function postRun<T>(path: string, body: object): Promise<T> { return responseJson<T>(await fetch(artifactUrl(path), { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body) })); }
 export async function createTrajectoryRun(files: { flight: UploadFilePayload; route: UploadFilePayload; restricted: UploadFilePayload; flyable?: UploadFilePayload }) { return postRun<{ run: TrajectoryDefinition }>("api/trajectory/runs", { files }); }
 export async function createDetectionRun(images: UploadFilePayload[]) { return postRun<{ run: DetectionDefinition }>("api/object-detection/runs", { images }); }
 export async function createSemanticRun(images: UploadFilePayload[], taskId: string) { return postRun<{ run: SemanticDefinition }>("api/semantic-mapping/runs", { images, taskId }); }
+export async function createMeasurementRun(rasters: UploadFilePayload[]) { return postRun<{ run: MeasurementDefinition }>("api/spatial-measurement/runs", { rasters }); }
+export async function createChangeRun(files: { before: UploadFilePayload; after: UploadFilePayload }, threshold = 0.5) { return postRun<{ run: ChangeDefinition }>("api/change-detection/runs", { files, threshold }); }
 export function readFileAsPayload(file: File): Promise<UploadFilePayload> { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onerror = () => reject(new Error(`无法读取 ${file.name}`)); reader.onload = () => { const value = String(reader.result ?? ""); resolve({ name: file.name, content: value.slice(value.indexOf(",") + 1) }); }; reader.readAsDataURL(file); }); }

--
Gitblit v1.9.3