from __future__ import annotations from datetime import datetime from uuid import UUID from pydantic import BaseModel, Field from app.schemas.operations import VectorSelectionBBox class ChangeDetectionRequest(BaseModel): source_dataset_id: UUID target_dataset_id: UUID iou_threshold: float = Field(default=0.8, ge=0.0, le=1.0) # Below this the two footprints are separate objects rather than one that # was redrawn; between the two thresholds the change class is "modified". modified_threshold: float = Field(default=0.3, ge=0.0, le=1.0) include_unchanged: bool = True # Without a selection the comparison covers both datasets in full, which is # rarely the question and never a response a map can draw. bbox: VectorSelectionBBox | None = None area_id: UUID | None = None preview_limit: int = Field(default=2_000, ge=1, le=20_000) class ChangeDetectionSummary(BaseModel): source_dataset_id: UUID target_dataset_id: UUID source_feature_count: int target_feature_count: int added_count: int removed_count: int # A footprint that was redrawn rather than demolished and rebuilt. Without # this class it appeared as one removal plus one addition. modified_count: int = 0 unchanged_count: int iou_threshold: float modified_iou_threshold: float | None = None selection_area_id: UUID | None = None # Counts describe the whole selection; the GeoJSON is capped so a regional # comparison does not return both datasets in one response. preview_limit: int | None = None preview_truncated: bool = False warnings: list[str] = Field(default_factory=list) generated_at: datetime geojson: dict