diff --git a/src/routers/forecasts.py b/src/routers/forecasts.py index 5bdb830..b59e030 100644 --- a/src/routers/forecasts.py +++ b/src/routers/forecasts.py @@ -150,15 +150,20 @@ def list_forecasts( if bbox: min_lon, min_lat, max_lon, max_lat = _parse_bbox(bbox) - query = query.join( - ParkingZone, - Forecast.zone_id == ParkingZone.parking_zone_id, - ).filter( - ParkingZone.longitude >= min_lon, - ParkingZone.latitude >= min_lat, - ParkingZone.longitude <= max_lon, - ParkingZone.latitude <= max_lat, - ) + # Фильтруем по центроиду geometry зоны на Python, + # т.к. longitude/latitude нет в parking_zones напрямую + zone_ids_in_bbox = [] + zones = db.query(ParkingZone).all() + for z in zones: + try: + coords = z.geometry["coordinates"][0] + z_lon = sum(c[0] for c in coords) / len(coords) + z_lat = sum(c[1] for c in coords) / len(coords) + if min_lon <= z_lon <= max_lon and min_lat <= z_lat <= max_lat: + zone_ids_in_bbox.append(z.parking_zone_id) + except Exception: + pass + query = query.filter(Forecast.zone_id.in_(zone_ids_in_bbox)) # latest_model_only: последняя генерация по каждой зоне + predicted_for elif latest_model_only: diff --git a/src/routers/occupancy.py b/src/routers/occupancy.py index 5576673..1563e76 100644 --- a/src/routers/occupancy.py +++ b/src/routers/occupancy.py @@ -4,7 +4,7 @@ from typing import Annotated, Union from fastapi import APIRouter, Depends, HTTPException, Query, status -from sqlalchemy import text +from sqlalchemy import text, func from sqlalchemy.orm import Session from ..database import get_db @@ -122,36 +122,32 @@ def list_occupancy( if to: query = query.filter(OccupancyObservation.observed_at <= to) - # Для view=map и latest_only: последнее наблюдение по каждой зоне if view == "map" or latest_only: - # Подзапрос: последний observed_at для каждой зоны - latest_sq = ( - db.query( - OccupancyObservation.zone_id, - text("MAX(observed_at) AS max_obs"), - ) - .group_by(OccupancyObservation.zone_id) - .subquery() - ) - if at: - # При at=... — последнее наблюдение с observed_at <= at latest_sq = ( db.query( - OccupancyObservation.zone_id, - text("MAX(observed_at) AS max_obs"), + OccupancyObservation.zone_id.label("zone_id"), + func.max(OccupancyObservation.observed_at).label("max_obs"), ) .filter(OccupancyObservation.observed_at <= at) .group_by(OccupancyObservation.zone_id) .subquery() ) + else: + latest_sq = ( + db.query( + OccupancyObservation.zone_id.label("zone_id"), + func.max(OccupancyObservation.observed_at).label("max_obs"), + ) + .group_by(OccupancyObservation.zone_id) + .subquery() + ) query = query.join( latest_sq, (OccupancyObservation.zone_id == latest_sq.c.zone_id) & (OccupancyObservation.observed_at == latest_sq.c.max_obs), ) - # bbox фильтр для view=map — через JOIN с parking_zones if bbox and view == "map": min_lon, min_lat, max_lon, max_lat = _parse_bbox(bbox)