Initial GeoIntel V1 foundation
GeoIntel CI / docs-smoke (push) Has been cancelled
GeoIntel CI / contract-smoke (push) Has been cancelled

This commit is contained in:
Codex
2026-06-16 23:36:32 +02:00
commit 6ea3586a3e
605 changed files with 45284 additions and 0 deletions
View File
+156
View File
@@ -0,0 +1,156 @@
from __future__ import annotations
from .common import ApiErrorEnvelope, ApiErrorItem, Envelope, PaginationEnvelope
from .project import ProjectCreate, ProjectList, ProjectRead, ProjectUpdate
from .area import AreaCreate, AreaList, AreaRead, AreaUpdate
from .dataset import DatasetCreateResponse, DatasetList
from .detection import (
DetectionListResponse,
DetectionModelCapability,
DetectionModelsResponse,
DetectionQaRequest,
DetectionRead,
DetectionRunListResponse,
DetectionRunRead,
DetectionRunRequest,
DetectionRunResponse,
)
from .segmentation import (
SegmentationListResponse,
SegmentationModelCapability,
SegmentationModelsResponse,
SegmentationQaRequest,
SegmentationRead,
SegmentationRunListResponse,
SegmentationRunRead,
SegmentationRunRequest,
SegmentationRunResponse,
)
from .health import HealthResponse, SystemCapabilities
from .job import JobCreate, JobList, JobRead, JobStatus
from .external import (
ExternalFetchRequest,
ExternalFetchResponse,
ProviderCapabilitiesResponse,
ProviderCapabilityResponse,
ProviderImportRequest,
ProviderImportResponse,
ProviderLayersResponse,
ProviderStatusResponse,
)
from .export import (
ExportContentResponse,
ExportCreateResponse,
ExportListResponse,
ExportRead,
GeoJsonExportRequest,
MetadataExportRequest,
ReportExportRequest,
)
from .qa import QaProviderComparisonRequest, QaProviderComparisonResult
from .operations import (
RasterClipRequest,
RasterIndexBaseRequest,
RasterMetadataResponse,
RasterNdviRequest,
RasterNdwiRequest,
RasterNdbiRequest,
RasterOperationResult,
RasterPreviewResponse,
RasterReprojectRequest,
RasterReprojectResponse,
RasterStatsResponse,
RasterTileManifest,
RasterTileManifestTile,
RasterTileRequest,
RasterTileResponse,
VectorBBoxResponse,
VectorBufferRequest,
VectorClipRequest,
VectorIntersectRequest,
VectorOperationRequest,
VectorOperationResult,
VectorStatsRequest,
VectorStatsResponse,
)
__all__ = [
"Envelope",
"ApiErrorEnvelope",
"ApiErrorItem",
"PaginationEnvelope",
"ProjectCreate",
"ProjectRead",
"ProjectUpdate",
"ProjectList",
"AreaCreate",
"AreaRead",
"AreaUpdate",
"AreaList",
"DatasetCreateResponse",
"DatasetList",
"DetectionListResponse",
"DetectionModelCapability",
"DetectionModelsResponse",
"DetectionQaRequest",
"DetectionRead",
"DetectionRunListResponse",
"DetectionRunRead",
"DetectionRunRequest",
"DetectionRunResponse",
"SegmentationListResponse",
"SegmentationModelCapability",
"SegmentationModelsResponse",
"SegmentationQaRequest",
"SegmentationRead",
"SegmentationRunListResponse",
"SegmentationRunRead",
"SegmentationRunRequest",
"SegmentationRunResponse",
"HealthResponse",
"SystemCapabilities",
"JobCreate",
"JobList",
"JobRead",
"JobStatus",
"VectorBBoxResponse",
"VectorClipRequest",
"VectorBufferRequest",
"VectorIntersectRequest",
"VectorOperationRequest",
"VectorOperationResult",
"RasterClipRequest",
"RasterStatsResponse",
"RasterReprojectRequest",
"RasterReprojectResponse",
"RasterTileRequest",
"RasterMetadataResponse",
"RasterOperationResult",
"RasterPreviewResponse",
"RasterTileManifestTile",
"RasterTileManifest",
"RasterTileResponse",
"RasterIndexBaseRequest",
"RasterNdviRequest",
"RasterNdwiRequest",
"RasterNdbiRequest",
"VectorStatsRequest",
"VectorStatsResponse",
"ExternalFetchRequest",
"ExternalFetchResponse",
"ProviderCapabilitiesResponse",
"ProviderCapabilityResponse",
"ProviderImportRequest",
"ProviderImportResponse",
"ProviderLayersResponse",
"ProviderStatusResponse",
"GeoJsonExportRequest",
"MetadataExportRequest",
"ReportExportRequest",
"ExportRead",
"ExportCreateResponse",
"ExportListResponse",
"ExportContentResponse",
"QaProviderComparisonRequest",
"QaProviderComparisonResult",
]
+40
View File
@@ -0,0 +1,40 @@
from __future__ import annotations
from datetime import datetime
from uuid import UUID
from pydantic import BaseModel
class AreaCreate(BaseModel):
name: str
geometry: dict
crs: str | None = "EPSG:4326"
class AreaUpdate(BaseModel):
name: str | None = None
crs: str | None = None
class AreaRead(BaseModel):
id: UUID
project_id: UUID
name: str
original_crs: str | None
area_m2: float | None
created_at: datetime | None = None
geometry_type: str | None = None
model_config = {"from_attributes": True}
class AreaListItem(AreaRead):
pass
class AreaList(BaseModel):
items: list[AreaRead]
total: int
limit: int
offset: int
+31
View File
@@ -0,0 +1,31 @@
from __future__ import annotations
from pydantic import BaseModel, Field
class Envelope(BaseModel):
data: object
class PaginatedEnvelope(BaseModel):
items: list
total: int
limit: int
offset: int
class PaginationEnvelope(BaseModel):
items: list
total: int
limit: int = Field(default=50)
offset: int = Field(default=0)
class ApiErrorItem(BaseModel):
code: str
message: str
details: dict = Field(default_factory=dict)
class ApiErrorEnvelope(BaseModel):
error: ApiErrorItem
+81
View File
@@ -0,0 +1,81 @@
from __future__ import annotations
from datetime import datetime
from uuid import UUID
from pydantic import BaseModel
class DatasetStorageResponse(BaseModel):
original_filename: str | None = None
stored_filename: str | None = None
content_type: str | None = None
size_bytes: int | None = None
checksum_sha256: str | None = None
class DatasetVectorSummary(BaseModel):
feature_count: int | None = None
geometry_types: list[str] | None = None
bounds_json: dict | None = None
approximate_area_m2: float | None = None
crs: str | None = None
feature_geometry_count: int | None = None
invalid_features: int | None = None
crs_assumed: bool | None = None
class DatasetCreateResponse(BaseModel):
id: UUID
name: str
dataset_type: str
source: str
dataset_role: str = "source"
source_name: str | None = None
reference_layer_name: str | None = None
source_metadata: dict | None = None
provenance_metadata: dict | None = None
imported_at: datetime | None = None
project_id: UUID
area_id: UUID | None = None
storage_path: str | None = None
original_filename: str | None = None
stored_filename: str | None = None
content_type: str | None = None
size_bytes: int | None = None
checksum_sha256: str | None = None
crs: str | None = None
bounds_json: dict | None = None
metadata_json: dict | None = None
vector_summary: DatasetVectorSummary | None = None
status: str
derived_from_dataset_id: UUID | None = None
created_at: datetime | None = None
feature_count: int | None = None
model_config = {"from_attributes": True}
class DatasetList(BaseModel):
items: list[DatasetCreateResponse]
total: int
limit: int
offset: int
class DatasetMetadataRefresh(BaseModel):
feature_count: int | None = None
geometry_types: list[str] | None = None
bounds_json: dict | None = None
crs: str | None = None
class ExportRequest(BaseModel):
dataset_id: UUID
name: str | None = None
class ExportRead(BaseModel):
export_id: UUID
path: str
status: str
+17
View File
@@ -0,0 +1,17 @@
from __future__ import annotations
from uuid import UUID
from pydantic import BaseModel
class DemoWorkflowResponse(BaseModel):
project_id: UUID
area_id: UUID
reference_dataset_id: UUID
candidate_dataset_id: UUID
quality_check_id: UUID
metric_count: int
status: str
message: str
created: bool
+98
View File
@@ -0,0 +1,98 @@
from __future__ import annotations
from datetime import datetime
from uuid import UUID
from pydantic import BaseModel, Field
class DetectionModelCapability(BaseModel):
model_id: str
display_name: str
framework: str
task_type: str
supported_classes: list[str]
configured: bool
status: str
limitation_message: str
version: str | None = None
class DetectionModelsResponse(BaseModel):
models: list[DetectionModelCapability]
class DetectionRunRequest(BaseModel):
project_id: UUID
dataset_id: UUID
model_id: str
confidence_threshold: float = Field(default=0.5, ge=0.0, le=1.0)
class_filter: list[str] | None = None
tile_manifest_path: str | None = None
parameters_json: dict = Field(default_factory=dict)
class DetectionQaRequest(BaseModel):
reference_dataset_id: UUID
iou_threshold: float = Field(default=0.5, ge=0.0, le=1.0)
class_name: str | None = None
min_confidence: float | None = Field(default=None, ge=0.0, le=1.0)
class DetectionRunResponse(BaseModel):
analysis_run_id: UUID
job_id: UUID
project_id: UUID
dataset_id: UUID
model_id: str
status: str
detection_count: int
error_code: str | None = None
message: str
class DetectionRunRead(BaseModel):
id: UUID
project_id: UUID
dataset_id: UUID | None = None
job_id: UUID | None = None
analysis_type: str
status: str
model_name: str | None = None
model_version: str | None = None
parameters_json: dict
result_json: dict | None = None
error_message: str | None = None
created_at: datetime | None = None
started_at: datetime | None = None
finished_at: datetime | None = None
model_config = {"from_attributes": True}
class DetectionRunListResponse(BaseModel):
items: list[DetectionRunRead]
total: int
class DetectionRead(BaseModel):
id: UUID
project_id: UUID
dataset_id: UUID | None = None
analysis_run_id: UUID | None = None
job_id: UUID | None = None
model_name: str
model_version: str | None = None
class_name: str
confidence: float
bbox_json: dict | None = None
source_tile_path: str | None = None
properties_json: dict | None = None
created_at: datetime | None = None
model_config = {"from_attributes": True}
class DetectionListResponse(BaseModel):
items: list[DetectionRead]
total: int
+69
View File
@@ -0,0 +1,69 @@
from __future__ import annotations
from datetime import datetime
from typing import Literal
from uuid import UUID
from pydantic import BaseModel, model_validator
ExportKind = Literal["dataset", "detection_run", "segmentation_run"]
class GeoJsonExportRequest(BaseModel):
dataset_id: UUID | None = None
analysis_run_id: UUID | None = None
export_kind: ExportKind = "dataset"
name: str | None = None
@model_validator(mode="after")
def validate_target(self) -> "GeoJsonExportRequest":
if self.export_kind == "dataset" and self.dataset_id is None:
raise ValueError("dataset_id is required for dataset GeoJSON exports")
if self.export_kind in {"detection_run", "segmentation_run"} and self.analysis_run_id is None:
raise ValueError("analysis_run_id is required for run GeoJSON exports")
return self
class MetadataExportRequest(BaseModel):
project_id: UUID
name: str | None = None
class ReportExportRequest(BaseModel):
project_id: UUID
name: str | None = None
class ExportRead(BaseModel):
id: UUID
project_id: UUID
analysis_run_id: UUID | None = None
export_type: str
storage_path: str
metadata_json: dict | None = None
created_at: datetime | None = None
status: str = "ready"
model_config = {"from_attributes": True}
class ExportCreateResponse(BaseModel):
export_id: UUID
path: str
status: str
export_type: str
metadata_json: dict | None = None
class ExportListResponse(BaseModel):
items: list[ExportRead]
total: int
limit: int
offset: int
class ExportContentResponse(BaseModel):
export_id: UUID
export_type: str
content: dict
+68
View File
@@ -0,0 +1,68 @@
from __future__ import annotations
from uuid import UUID
from pydantic import BaseModel
class ProviderCapabilityResponse(BaseModel):
provider_name: str
display_name: str
authority_level: str
supported_layers: list[str]
supported_geometry_types: list[str]
supported_query_modes: list[str]
fetch_signature: str
configured: bool
status: str
limitation_message: str
attribution: str
license_note: str
not_configured_reason: str | None = None
class ProviderCapabilitiesResponse(BaseModel):
providers: list[ProviderCapabilityResponse]
class ProviderLayersResponse(BaseModel):
provider_name: str
layers: list[str]
class ProviderStatusResponse(BaseModel):
provider_name: str
configured: bool
status: str
limitation_message: str
class ExternalFetchRequest(BaseModel):
project_id: UUID
area_id: UUID | None = None
layers: list[str] = []
class ExternalFetchResponse(BaseModel):
provider: str
status: str
message: str
requested_layers: list[str]
project_id: UUID
area_id: UUID | None = None
class ProviderImportRequest(BaseModel):
project_id: str
area_id: str | None = None
layers: list[str] = []
dataset_role: str | None = None
class ProviderImportResponse(BaseModel):
provider_name: str
status: str
message: str
requested_layers: list[str]
dataset_id: str | None = None
dataset_role: str | None = None
source_name: str | None = None
+37
View File
@@ -0,0 +1,37 @@
from __future__ import annotations
from pydantic import BaseModel, Field
class ProviderCapability(BaseModel):
provider_name: str
display_name: str
authority_level: str
supported_layers: list[str]
supported_geometry_types: list[str]
supported_query_modes: list[str]
fetch_signature: str
configured: bool
status: str
limitation_message: str
attribution: str
license_note: str
not_configured_reason: str | None = None
class HealthResponse(BaseModel):
status: str
service: str
version: str
database: str | None = None
class SystemCapabilities(BaseModel):
postgis: bool
rasterio: bool
geopandas: bool
yolo: bool | str
sam: bool | str
grb: str
sentinel: str
providers: list[ProviderCapability] = Field(default_factory=list)
+51
View File
@@ -0,0 +1,51 @@
from __future__ import annotations
from datetime import datetime
from uuid import UUID
from pydantic import BaseModel, Field
class JobCreate(BaseModel):
job_type: str
project_id: UUID
dataset_id: UUID | None = None
input_dataset_id: UUID | None = None
output_dataset_id: UUID | None = None
parameters_json: dict = Field(default_factory=dict)
class JobRead(BaseModel):
id: UUID
job_type: str
status: str
project_id: UUID
dataset_id: UUID | None = None
input_dataset_id: UUID | None = None
output_dataset_id: UUID | None = None
parameters_json: dict
result_json: dict | None = None
error_message: str | None = None
created_at: datetime | None = None
started_at: datetime | None = None
finished_at: datetime | None = None
model_config = {"from_attributes": True}
class JobStatus(BaseModel):
id: UUID
status: str
error_message: str | None = None
started_at: datetime | None = None
finished_at: datetime | None = None
result_json: dict | None = None
model_config = {"from_attributes": True}
class JobList(BaseModel):
items: list[JobRead]
total: int
limit: int
offset: int
+193
View File
@@ -0,0 +1,193 @@
from __future__ import annotations
from pydantic import BaseModel
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
+41
View File
@@ -0,0 +1,41 @@
from __future__ import annotations
from datetime import datetime
from uuid import UUID
from pydantic import BaseModel
class ProjectCreate(BaseModel):
name: str
description: str | None = None
region: str | None = "Kempen"
class ProjectUpdate(BaseModel):
name: str | None = None
description: str | None = None
region: str | None = None
class ProjectRead(BaseModel):
id: UUID
name: str
description: str | None = None
region: str
status: str
created_at: datetime | None = None
updated_at: datetime | None = None
model_config = {"from_attributes": True}
class ProjectListItem(ProjectRead):
pass
class ProjectList(BaseModel):
items: list[ProjectRead]
total: int
limit: int
offset: int
+71
View File
@@ -0,0 +1,71 @@
from __future__ import annotations
from datetime import datetime
from uuid import UUID
from pydantic import BaseModel, Field
class QaProviderComparisonRequest(BaseModel):
candidate_dataset_id: UUID
reference_dataset_id: UUID
iou_threshold: float = Field(default=0.5, ge=0.0, le=1.0)
area_id: UUID | None = None
class QaProviderComparisonResult(BaseModel):
status: str
warnings: list[str] = Field(default_factory=list)
candidate_feature_count: int
reference_feature_count: int
matches: int
false_positives: int
false_negatives: int
precision: float | None
recall: float | None
f1_score: float | None
mean_iou: float | None
iou_threshold: float
unsupported_geometry: bool = False
unsupported_geometries: list[str] = Field(default_factory=list)
generated_at: datetime
class MetricRead(BaseModel):
id: UUID
quality_check_id: UUID | None = None
analysis_run_id: UUID | None = None
metric_key: str
metric_value: float | None = None
metric_unit: str | None = None
label: str | None = None
metadata_json: dict | None = None
created_at: datetime | None = None
model_config = {"from_attributes": True}
class QualityCheckRead(BaseModel):
id: UUID
project_id: UUID
job_id: UUID | None = None
analysis_run_id: UUID | None = None
candidate_dataset_id: UUID | None = None
reference_dataset_id: UUID
check_type: str
status: str
score: float | None = None
parameters_json: dict | None = None
findings_json: dict | None = None
created_at: datetime | None = None
completed_at: datetime | None = None
metrics: list[MetricRead] = Field(default_factory=list)
model_config = {"from_attributes": True}
class QualityCheckList(BaseModel):
items: list[QualityCheckRead]
total: int
limit: int
offset: int
+95
View File
@@ -0,0 +1,95 @@
from __future__ import annotations
from datetime import datetime
from uuid import UUID
from pydantic import BaseModel, Field
from app.schemas.detection import DetectionModelCapability
SegmentationModelCapability = DetectionModelCapability
class SegmentationModelsResponse(BaseModel):
models: list[SegmentationModelCapability]
class SegmentationRunRequest(BaseModel):
project_id: UUID
dataset_id: UUID
model_id: str
confidence_threshold: float = Field(default=0.5, ge=0.0, le=1.0)
class_filter: list[str] | None = None
tile_manifest_path: str | None = None
parameters_json: dict = Field(default_factory=dict)
class SegmentationQaRequest(BaseModel):
reference_dataset_id: UUID
iou_threshold: float = Field(default=0.5, ge=0.0, le=1.0)
class_name: str | None = None
min_confidence: float | None = Field(default=None, ge=0.0, le=1.0)
class SegmentationRunResponse(BaseModel):
analysis_run_id: UUID
job_id: UUID
project_id: UUID
dataset_id: UUID
model_id: str
status: str
segmentation_count: int
error_code: str | None = None
message: str
class SegmentationRunRead(BaseModel):
id: UUID
project_id: UUID
dataset_id: UUID | None = None
job_id: UUID | None = None
analysis_type: str
status: str
model_name: str | None = None
model_version: str | None = None
parameters_json: dict
result_json: dict | None = None
error_message: str | None = None
created_at: datetime | None = None
started_at: datetime | None = None
finished_at: datetime | None = None
model_config = {"from_attributes": True}
class SegmentationRunListResponse(BaseModel):
items: list[SegmentationRunRead]
total: int
class SegmentationRead(BaseModel):
id: UUID
project_id: UUID
dataset_id: UUID | None = None
analysis_run_id: UUID | None = None
job_id: UUID | None = None
model_name: str
model_version: str | None = None
class_name: str
confidence: float | None = None
bbox_json: dict | None = None
area_m2: float | None = None
mask_path: str | None = None
source_tile_path: str | None = None
tile_index: int | None = None
properties_json: dict | None = None
provenance_json: dict | None = None
created_at: datetime | None = None
model_config = {"from_attributes": True}
class SegmentationListResponse(BaseModel):
items: list[SegmentationRead]
total: int