first commit
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
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)
|
||||
Reference in New Issue
Block a user