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
+77
View File
@@ -0,0 +1,77 @@
from __future__ import annotations
import uuid
from sqlalchemy.orm import Session
from geoalchemy2.shape import from_shape
from app.core.errors import AppError
from app.models import Area, Project
from app.schemas.area import AreaCreate, AreaRead, AreaUpdate
from app.utils.geometry import area_m2, geometry_bbox_polygon, normalize_to_multipolygon
class AreaService:
@staticmethod
def list_areas(db: Session, project_id: uuid.UUID, limit: int = 50, offset: int = 0) -> tuple[list[AreaRead], int]:
total = db.query(Area).filter(Area.project_id == project_id).count()
areas = (
db.query(Area)
.filter(Area.project_id == project_id)
.order_by(Area.created_at.desc())
.offset(offset)
.limit(limit)
.all()
)
return [AreaRead.model_validate(area) for area in areas], total
@staticmethod
def create_area(db: Session, project_id: uuid.UUID, payload: AreaCreate) -> AreaRead:
if not db.get(Project, project_id):
raise AppError(code="PROJECT_NOT_FOUND", message="Project not found", status_code=404)
try:
multipolygon = normalize_to_multipolygon(payload.geometry)
except ValueError as exc:
raise AppError(code="INVALID_GEOMETRY", message=str(exc), status_code=400) from exc
area = Area(
project_id=project_id,
name=payload.name.strip() or "Unnamed area",
geometry=from_shape(multipolygon, srid=4326),
original_crs=payload.crs or "EPSG:4326",
area_m2=area_m2(multipolygon),
bbox=from_shape(geometry_bbox_polygon(multipolygon), srid=4326),
)
db.add(area)
db.commit()
db.refresh(area)
return AreaRead.model_validate(area)
@staticmethod
def get_area(db: Session, area_id: uuid.UUID) -> AreaRead:
area = db.get(Area, area_id)
if not area:
raise AppError(code="AREA_NOT_FOUND", message="Area not found", status_code=404)
return AreaRead.model_validate(area)
@staticmethod
def update_area(db: Session, area_id: uuid.UUID, payload: AreaUpdate) -> AreaRead:
area = db.get(Area, area_id)
if not area:
raise AppError(code="AREA_NOT_FOUND", message="Area not found", status_code=404)
changed = False
if payload.name:
area.name = payload.name.strip() or area.name
changed = True
if payload.crs:
area.original_crs = payload.crs
changed = True
if not changed:
raise AppError(code="NO_CHANGES", message="No updatable fields provided", status_code=422)
db.add(area)
db.commit()
db.refresh(area)
return AreaRead.model_validate(area)