diff --git a/app/api/tree.py b/app/api/tree.py old mode 100755 new mode 100644 index 0d5b84f..1dcd698 --- a/app/api/tree.py +++ b/app/api/tree.py @@ -11,12 +11,10 @@ ) from ..dependencies import get_session from ..services.tree import ( - get_tree_by_id + get_tree_by_id, + get_tree_by_species ) -from geoalchemy2.shape import to_shape -from shapely.geometry import mapping - route_street_tree = APIRouter(prefix='/street_tree/v1') def create_geojson_from_rows(rows: List[Dict[str, Any]]) -> FeatureCollection: @@ -39,37 +37,42 @@ def create_geojson_from_rows(rows: List[Dict[str, Any]]) -> FeatureCollection: '/details', response_model=StreetTreeResponse, tags=['Strassenbaeume'], - description='Retrieves street tree details based on the provided tree id.' + description=( + 'Retrieves street tree details based on the provided tree id.' + ) ) async def fetch_tree_by_id( tree_id: int, session: AsyncSession = Depends(get_session) -) -> StreetTreeResponse: - row = await get_tree_by_id(session, tree_id) +) -> List[StreetTreeResponse]: + rows = await get_tree_by_id(session, tree_id) + identifier = f'tree_id {tree_id}' - if not row: + if not rows: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, - detail=f'No matches found for tree_id {tree_id}' + detail=f'No matches found for {identifier}' ) - # `row` is a RowMapping, convert to model instance or dict if needed - tree: StreetTreeRegister = row[0] if isinstance(row, tuple) else row + return rows + +@route_street_tree.get( + '/species', + response_model=StreetTreeResponse, + tags=['Strassenbaeume'], + description=( + 'Retrieves street tree details based on the provided tree id.' + ) +) +async def fetch_tree_by_species( + session: AsyncSession = Depends(get_session) +) -> List[StreetTreeResponse]: + rows = await get_tree_by_species(session) - # Convert WKBElement geom to GeoJSON dict - shape = to_shape(tree.geom) - geojson_dict = mapping(shape) + if not rows: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=f'No matches found' + ) - # Return tree data with geom as GeoJSON dict - return { - "id": tree.id, - "tree_number": tree.tree_number, - "street": tree.street, - "area_type": tree.area_type, - "species": tree.species, - "north": tree.north, - "east": tree.east, - "registration_date": tree.registration_date.isoformat() if tree.registration_date else None, - "type": tree.type, - "geom": geojson_dict, - } \ No newline at end of file + return rows diff --git a/app/models/tree.py b/app/models/tree.py old mode 100755 new mode 100644 index 2f532b5..4ef84c0 --- a/app/models/tree.py +++ b/app/models/tree.py @@ -17,4 +17,4 @@ class StreetTreeRegister(SQLModel, table=True): east: float = Field(sa_column=Column(Numeric, nullable=False)) registration_date: datetime.date = Field(sa_column=Column(Date(), nullable=False)) type: str = Field(sa_column=Column(String, nullable=False)) - geom: object = Field(sa_column=Column(Geometry('POINT', srid=31467), nullable=True)) # nullable depends on DB column \ No newline at end of file + geom: object = Field(sa_column=Column(Geometry('POINT', srid=31467), nullable=True)) # nullable depends on DB column diff --git a/app/schemas/tree.py b/app/schemas/tree.py old mode 100755 new mode 100644 diff --git a/app/services/tree.py b/app/services/tree.py old mode 100755 new mode 100644 index 6bba84b..dba5e8a --- a/app/services/tree.py +++ b/app/services/tree.py @@ -1,5 +1,7 @@ +from sqlalchemy import select, case, literal_column, func +from sqlalchemy.sql import exists, and_, not_, text +from sqlalchemy.orm import aliased from sqlalchemy.ext.asyncio import AsyncSession -from sqlmodel import select from app.models.tree import StreetTreeRegister async def get_tree_by_id(session: AsyncSession, tree_id: int): @@ -10,3 +12,39 @@ async def get_tree_by_id(session: AsyncSession, tree_id: int): row = result.scalars().first() return row + +async def get_tree_by_species(session: AsyncSession): + stmt = text(''' + SELECT + tr.id, + tr.tree_number, + tr.street, + tr.species, + tr.type, + ROUND(ST_X(ST_Transform(tr.geom, 4326))::numeric, 6) AS lon, + ROUND(ST_Y(ST_Transform(tr.geom, 4326))::numeric, 6) AS lat, + CASE + WHEN tr.species ILIKE '%Tilia%' THEN 1 + WHEN tr.species ILIKE '%Acer%' THEN 2 + WHEN tr.species ILIKE '%Quercus%' THEN 3 + WHEN tr.species ILIKE '%Fagus%' THEN 4 + WHEN tr.species ILIKE '%Betula%' THEN 5 + WHEN tr.species ILIKE '%Carpinus%' THEN 6 + ELSE 0 + END AS species_index + FROM flensburg.street_tree_register tr + WHERE tr.type = 'bestand' + AND NOT EXISTS ( + SELECT 1 + FROM flensburg.street_tree_register gef + WHERE gef.type = 'gefaellt' + AND gef.tree_number = tr.tree_number + AND gef.street = tr.street + ) + ORDER BY tr.species + ''') + + result = await session.execute(stmt) + rows = result.mappings().all() + + return [dict(row) for row in rows]