"""Cadastral lookup endpoints — reverse geocode via cad_buildings. Used by trade-in backend (and potentially other internal services) to resolve координаты → адрес жилого дома без зависимости от внешних геокодеров. """ from __future__ import annotations import logging from typing import Annotated from fastapi import APIRouter, Depends, HTTPException, Query from pydantic import BaseModel from sqlalchemy import text from sqlalchemy.orm import Session from app.core.db import get_db logger = logging.getLogger(__name__) router = APIRouter() class CadastralReverseResult(BaseModel): address: str lat: float lon: float distance_m: float cad_num: str | None = None @router.get("/reverse", response_model=CadastralReverseResult) async def reverse( lat: Annotated[float, Query(ge=-90, le=90)], lon: Annotated[float, Query(ge=-180, le=180)], radius_m: Annotated[int, Query(ge=10, le=2000)] = 200, db: Annotated[Session, Depends(get_db)] = None, # type: ignore[assignment] ) -> CadastralReverseResult: """Координаты → ближайшее здание из cad_buildings. Filters out гаражи/СНТ/садовод/товарищества — для trade-in нужны жилые дома. Returns 404 если в радиусе нет подходящего здания. """ row = db.execute( text( """ SELECT readable_address, cad_num, ST_Y(ST_Centroid(geom)) AS lat, ST_X(ST_Centroid(geom)) AS lon, ST_Distance( geom::geography, ST_SetSRID( ST_MakePoint( CAST(:lon AS double precision), CAST(:lat AS double precision) ), 4326 )::geography ) AS distance_m FROM cad_buildings WHERE ST_DWithin( geom::geography, ST_SetSRID( ST_MakePoint( CAST(:lon AS double precision), CAST(:lat AS double precision) ), 4326 )::geography, CAST(:radius AS double precision) ) AND readable_address !~* '(гараж|снт|садовод|товарищ|уч\\.)' ORDER BY ST_Distance( geom::geography, ST_SetSRID( ST_MakePoint( CAST(:lon AS double precision), CAST(:lat AS double precision) ), 4326 )::geography ) LIMIT 1 """ ), {"lat": lat, "lon": lon, "radius": float(radius_m)}, ).first() if row is None: raise HTTPException(status_code=404, detail="no cadastral building found within radius") return CadastralReverseResult( address=row.readable_address, lat=float(row.lat), lon=float(row.lon), distance_m=round(float(row.distance_m), 1), cad_num=row.cad_num, )