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
265 lines
6.2 KiB
Python
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
|