fix(parcels): guard NULL geom + None lat/lon в isochrones/poi-score (#1201) #1251

Merged
bot-backend merged 1 commit from fix/isochrones-poi-null-geom-guard into main 2026-06-13 05:24:25 +00:00

View file

@ -2933,18 +2933,25 @@ def get_isochrones(
detail=f"times_min значения вне диапазона 1-60: {invalid_times}",
)
# Получить координаты центроида из доступных геометрий участка
# Получить координаты центроида из доступных геометрий участка.
# `geom IS NOT NULL` во ВСЕХ ветках UNION (#1201): cad_parcels_geom — VIEW
# над cad_parcels с nullable geom, 964 prod-строки. Без фильтра NULL-строка
# ранней ветки скрывает валидную из поздней + ST_Centroid(NULL) → NULL → 500.
# Зеркало паттерна analyze (parcels.py:1418-1438).
coord_row = (
db.execute(
text("""
SELECT ST_X(ST_Centroid(g.geom)) AS lon,
ST_Y(ST_Centroid(g.geom)) AS lat
FROM (
SELECT geom FROM cad_quarters_geom WHERE cad_number = :c
SELECT geom FROM cad_quarters_geom
WHERE cad_number = :c AND geom IS NOT NULL
UNION ALL
SELECT geom FROM cad_buildings WHERE cad_num = :c
SELECT geom FROM cad_buildings
WHERE cad_num = :c AND geom IS NOT NULL
UNION ALL
SELECT geom FROM cad_parcels_geom WHERE cad_num = :c
SELECT geom FROM cad_parcels_geom
WHERE cad_num = :c AND geom IS NOT NULL
) g
LIMIT 1
"""),
@ -2954,7 +2961,11 @@ def get_isochrones(
.first()
)
if not coord_row:
# belt-and-suspenders: row может вернуться с NULL lat/lon только если в БД
# есть строка с НЕ-NULL geom но битый ST_Centroid (топологически невалидный
# polygon). Граница `not coord_row` truthy-проверка на RowMapping её бы НЕ
# поймала (RowMapping с None-полями всё ещё truthy) → float(None) → 500.
if not coord_row or coord_row.get("lat") is None or coord_row.get("lon") is None:
raise HTTPException(
status_code=404,
detail=f"Геометрия для {cad_num} не найдена.",
@ -3029,18 +3040,24 @@ async def get_poi_score(
POI берутся из osm_poi_ekb в заданном радиусе (default 2000м).
Отсортированы по weight DESC наиболее значимые объекты первыми.
"""
# Получить координаты центроида участка из геометрических таблиц
# Получить координаты центроида участка из геометрических таблиц.
# См. парный комментарий в get_isochrones — `geom IS NOT NULL` во ВСЕХ ветках
# UNION (#1201): NULL-строка ранней ветки скрывает валидную из поздней +
# ST_Centroid(NULL) → NULL → float(None) → 500. Зеркало analyze (parcels.py:1418-1438).
coord_row = (
db.execute(
text("""
SELECT ST_X(ST_Centroid(g.geom)) AS lon,
ST_Y(ST_Centroid(g.geom)) AS lat
FROM (
SELECT geom FROM cad_quarters_geom WHERE cad_number = :c
SELECT geom FROM cad_quarters_geom
WHERE cad_number = :c AND geom IS NOT NULL
UNION ALL
SELECT geom FROM cad_buildings WHERE cad_num = :c
SELECT geom FROM cad_buildings
WHERE cad_num = :c AND geom IS NOT NULL
UNION ALL
SELECT geom FROM cad_parcels_geom WHERE cad_num = :c
SELECT geom FROM cad_parcels_geom
WHERE cad_num = :c AND geom IS NOT NULL
) g
LIMIT 1
"""),
@ -3050,7 +3067,8 @@ async def get_poi_score(
.first()
)
if not coord_row:
# belt-and-suspenders: см. get_isochrones — RowMapping с None-полями truthy.
if not coord_row or coord_row.get("lat") is None or coord_row.get("lon") is None:
raise HTTPException(
status_code=404,
detail=f"Геометрия для {cad_num} не найдена.",