gendesign/backend/app/api/v1/cadastral.py
lekss361 0efd2dca1d
All checks were successful
Deploy Trade-In / changes (push) Successful in 5s
Deploy / changes (push) Successful in 5s
Deploy Trade-In / build-frontend (push) Successful in 25s
Deploy Trade-In / build-backend (push) Successful in 47s
Deploy / build-frontend (push) Successful in 29s
Deploy / build-backend (push) Successful in 1m25s
Deploy Trade-In / deploy (push) Successful in 37s
Deploy / build-worker (push) Successful in 2m29s
Deploy / deploy (push) Successful in 1m2s
feat(tradein): cadastral reverse geocode via main backend cad_buildings (#492)
2026-05-24 07:31:12 +00:00

101 lines
3.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""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,
)