Files
2026-06-17 13:26:35 +03:00

91 lines
2.8 KiB
Python

from __future__ import annotations
import asyncio
from typing import Iterable
from psycopg.rows import dict_row
from psycopg_pool import ConnectionPool
from app.config import settings
class BuildingService:
def __init__(self, database_url: str | None = None) -> None:
self.database_url = database_url or settings.database_url
self._pool: ConnectionPool | None = None
self._available = False
def connect(self) -> None:
try:
self._pool = ConnectionPool(
conninfo=self.database_url,
min_size=1,
max_size=10,
kwargs={"row_factory": dict_row},
open=True,
)
with self._pool.connection() as conn:
with conn.cursor() as cur:
cur.execute("SELECT to_regclass('public.buildings') IS NOT NULL AS exists")
row = cur.fetchone()
self._available = bool(row and row["exists"])
except Exception:
self._available = False
if self._pool is not None:
self._pool.close()
self._pool = None
def close(self) -> None:
if self._pool is not None:
self._pool.close()
self._pool = None
def is_available(self) -> bool:
return self._available and self._pool is not None
def _get_max_height_sync(self, lat: float, lng: float) -> float:
if not self.is_available() or self._pool is None:
return 0.0
with self._pool.connection() as conn:
with conn.cursor() as cur:
cur.execute(
"""
SELECT COALESCE(MAX(height_m), 0) AS height_m
FROM buildings
WHERE ST_Contains(geom, ST_SetSRID(ST_Point(%s, %s), 4326))
""",
(lng, lat),
)
row = cur.fetchone()
return float(row["height_m"]) if row else 0.0
def _get_max_heights_sync(self, coordinates: Iterable[tuple[float, float]]) -> list[float]:
coords = list(coordinates)
if not coords:
return []
if not self.is_available() or self._pool is None:
return [0.0] * len(coords)
values = [0.0] * len(coords)
with self._pool.connection() as conn:
with conn.cursor() as cur:
for index, (lat, lng) in enumerate(coords):
cur.execute(
"""
SELECT COALESCE(MAX(height_m), 0) AS height_m
FROM buildings
WHERE ST_Contains(geom, ST_SetSRID(ST_Point(%s, %s), 4326))
""",
(lng, lat),
)
row = cur.fetchone()
values[index] = float(row["height_m"]) if row else 0.0
return values
async def get_max_height(self, lat: float, lng: float) -> float:
return await asyncio.to_thread(self._get_max_height_sync, lat, lng)
async def get_max_heights(self, coordinates: list[tuple[float, float]]) -> list[float]:
return await asyncio.to_thread(self._get_max_heights_sync, coordinates)