from __future__ import annotations from uuid import UUID from pydantic import BaseModel, Field, field_validator class VectorOperationResult(BaseModel): feature_count: int geometry_type_summary: dict[str, int] bounds_json: dict | None = None crs: str | None = None source_dataset_id: str class VectorOperationRequest(BaseModel): output_name: str | None = None class VectorClipRequest(VectorOperationRequest): area_id: str class VectorBufferRequest(VectorOperationRequest): distance_m: float dissolve: bool = False class VectorIntersectRequest(VectorOperationRequest): other_dataset_id: str class VectorStatsRequest(BaseModel): pass class RasterReadyResponse(BaseModel): dataset_id: str ready: bool message: str | None = None class RasterOperationResult(BaseModel): dataset_id: str ready: bool metadata: dict | None = None output_dataset_id: str | None = None operation: str | None = None class RasterMetadataResponse(BaseModel): dataset_id: str driver: str | None = None width: int | None = None height: int | None = None band_count: int | None = None crs: str | None = None bounds: list[float] | None = None resolution: list[float] | None = None dtype: list[str] | None = None nodata: list[float] | float | None = None transform: list[float] | None = None size_bytes: int | None = None checksum_sha256: str | None = None path: str | None = None class RasterPreviewResponse(BaseModel): dataset_id: str ready: bool preview: dict metadata: dict | None = None class RasterBandStats(BaseModel): band_index: int dtype: str | None = None min: float | None = None max: float | None = None mean: float | None = None std: float | None = None nodata_count: int nodata_ratio: float valid_pixel_count: int histogram: list[int] | None = None histogram_bins: list[float] | None = None class RasterStatsResponse(BaseModel): dataset_id: str source_dataset_id: str | None = None bands: list[RasterBandStats] generated_at: str | None = None metadata: dict | None = None class RasterReprojectRequest(BaseModel): target_crs: str | None = "EPSG:31370" resampling: str = "nearest" output_name: str | None = None class RasterClipRequest(BaseModel): area_id: str output_name: str | None = None class RasterTileRequest(BaseModel): tile_size: int = 512 overlap: int = 64 output_name: str | None = None class RasterIndexBaseRequest(BaseModel): output_name: str | None = None class RasterNdviRequest(RasterIndexBaseRequest): nir_band: int red_band: int class RasterNdwiRequest(RasterIndexBaseRequest): green_band: int nir_band: int class RasterNdbiRequest(RasterIndexBaseRequest): swir_band: int nir_band: int class RasterTileManifestTile(BaseModel): path: str pixel_window: list[int] bounds: list[float] transform: list[float] index: int class RasterTileManifest(BaseModel): tile_set_id: str source_dataset_id: str source_raster_id: str bounds: list[float] tile_size: int overlap: int parameters: dict[str, str | int | float | bool | None] created_at: str tile_paths: list[str] count: int tiles: list[RasterTileManifestTile] ai_inference: bool = False tile_server: str | None = None class RasterTileResponse(BaseModel): dataset_id: str ready: bool operation: str tile_set_id: str tile_size: int overlap: int manifest_path: str count: int manifest: RasterTileManifest class RasterReprojectResponse(BaseModel): dataset_id: str ready: bool operation: str output_dataset_id: str source_dataset_id: str target_dataset_id: str | None = None class RasterOperationUnavailable(BaseModel): code: str message: str class VectorBBoxResponse(BaseModel): dataset_id: str bounds_json: dict | None feature_count: int crs: str | None = None class VectorStatsResponse(BaseModel): dataset_id: str feature_count: int geometry_type_summary: dict[str, int] bounds_json: dict | None crs: str | None = None class VectorSelectionBBox(BaseModel): min_x: float min_y: float max_x: float max_y: float crs: str = "EPSG:4326" @field_validator("crs") @classmethod def validate_crs(cls, value: str) -> str: if value.upper() != "EPSG:4326": raise ValueError("Only EPSG:4326 bbox selection is supported") return "EPSG:4326" class VectorSelectionRequest(BaseModel): bbox: VectorSelectionBBox area_id: UUID | None = None limit: int = Field(default=100, ge=1, le=1000) class VectorSelectionDeriveRequest(VectorSelectionRequest): output_name: str | None = None class VectorSelectionSummary(BaseModel): metric_label: str metric_value: float metric_unit: str aggregation_method: str feature_count: int is_estimate: bool = False warning: str | None = None class VectorSelectionResponse(BaseModel): selection_bbox: VectorSelectionBBox selection_area_id: UUID | None = None feature_count: int total_feature_count: int | None = None limit: int truncated: bool geojson: dict summary: VectorSelectionSummary | None = None