Files
geointel/backend/app/schemas/operations.py
T
Jens faeb58ef6d
GeoIntel release gates / Compile, test, contracts and builds (push) Successful in 1m49s
GeoIntel release gates / Python and npm vulnerability policy (push) Successful in 21s
GeoIntel release gates / Production AI image, SBOM and container scan (push) Successful in 5m39s
GeoIntel release gates / Deploy exact gated revision to Unraid (push) Failing after 58m43s
Initial public release
2026-08-31 21:56:53 +02:00

265 lines
6.2 KiB
Python

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 VectorSelectionMetric(BaseModel):
metric_key: str
metric_label: str
metric_value: float
metric_unit: str
aggregation_method: str
is_estimate: bool = False
warning: str | None = None
class VectorSelectionSummary(BaseModel):
metric_label: str
metric_value: float
metric_unit: str
aggregation_method: str
primary_metric_key: str | None = None
# ``feature_count`` counts whole features that touch the selection, while
# area and length metrics clip to it. These fields say how far the two
# populations diverge, so the numbers on one panel can be read together.
feature_count: int
fully_covered_feature_count: int | None = None
partially_covered_feature_count: int | None = None
selection_edge_warning: str | None = None
is_estimate: bool = False
warning: str | None = None
metrics: list[VectorSelectionMetric] = Field(default_factory=list)
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
partition_count: int | None = None
available_partition_count: int | None = None
partition_scope_key: str | None = None
source_name: str | None = None
dataset_ids: list[UUID] | None = None