fix(best-layouts): replace non-existent geom_3857 with on-the-fly geography (#113 PR C)
domrf_kn_objects has no geom_3857 column (only latitude/longitude NUMERIC).
Switch _COMPETITORS_IN_RADIUS_SQL and _SUPPLY_BATCH_SQL to use
ST_SetSRID(ST_MakePoint(longitude, latitude), 4326)::geography — consistent
with competitors.py pattern. Rename centroid params x_3857/y_3857 →
center_lon/center_lat. Remove dead _round_to_5 function.
Fixes review-bot 🔴 BLOCK on fdc6456 PR #196.
Tests: 10/10 pass (mock keys updated к center_lon/center_lat), 43/43 regression OK.
This commit is contained in:
parent
fdc64569d2
commit
a88917f0cf
2 changed files with 26 additions and 24 deletions
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
Источники:
|
||||
cad_parcels_geom / cad_quarters_geom — центроид участка
|
||||
domrf_kn_objects — ЖК в радиусе (geom_3857)
|
||||
domrf_kn_objects — ЖК в радиусе (latitude/longitude → geography)
|
||||
mv_layout_velocity — (obj_id, room_bucket) → агрегат продаж 24 мес
|
||||
domrf_kn_flats — supply count по (room_bucket, area_bin)
|
||||
|
||||
|
|
@ -49,8 +49,8 @@ _VELOCITY_DIVISORS: dict[str, float] = {
|
|||
# ── SQL: центроид участка ─────────────────────────────────────────────────────
|
||||
|
||||
_PARCEL_CENTROID_SQL = text("""
|
||||
SELECT ST_X(ST_Transform(pt, 3857)) AS x_3857,
|
||||
ST_Y(ST_Transform(pt, 3857)) AS y_3857
|
||||
SELECT ST_X(pt) AS center_lon,
|
||||
ST_Y(pt) AS center_lat
|
||||
FROM (
|
||||
SELECT ST_Centroid(geom) AS pt
|
||||
FROM cad_parcels_geom
|
||||
|
|
@ -64,18 +64,22 @@ _PARCEL_CENTROID_SQL = text("""
|
|||
""")
|
||||
|
||||
# ── SQL: obj_id конкурентов в радиусе ─────────────────────────────────────────
|
||||
# Примечание: геометрия domrf_kn_objects.geom_3857 уже в EPSG:3857.
|
||||
# ST_DWithin на EPSG:3857 — метры (проекция, не geography).
|
||||
# Геометрия domrf_kn_objects вычисляется on-the-fly из (latitude, longitude)
|
||||
# как ST_SetSRID(ST_MakePoint(longitude, latitude), 4326)::geography
|
||||
# (consistency с competitors.py).
|
||||
# obj_class_filter: NULL = все классы.
|
||||
# filter_competitor_obj_ids: NULL = не фильтровать по списку.
|
||||
|
||||
_COMPETITORS_IN_RADIUS_SQL = text("""
|
||||
SELECT DISTINCT ON (obj_id) obj_id
|
||||
FROM domrf_kn_objects
|
||||
WHERE geom_3857 IS NOT NULL
|
||||
WHERE latitude IS NOT NULL AND longitude IS NOT NULL
|
||||
AND ST_DWithin(
|
||||
geom_3857,
|
||||
ST_SetSRID(ST_MakePoint(CAST(:x_3857 AS float), CAST(:y_3857 AS float)), 3857),
|
||||
ST_SetSRID(ST_MakePoint(longitude, latitude), 4326)::geography,
|
||||
ST_SetSRID(
|
||||
ST_MakePoint(CAST(:center_lon AS float), CAST(:center_lat AS float)),
|
||||
4326
|
||||
)::geography,
|
||||
CAST(:radius_m AS float)
|
||||
)
|
||||
AND (
|
||||
|
|
@ -126,10 +130,13 @@ _SUPPLY_BATCH_SQL = text("""
|
|||
COUNT(*) AS units
|
||||
FROM domrf_kn_flats f
|
||||
JOIN domrf_kn_objects o ON f.obj_id = o.obj_id
|
||||
WHERE o.geom_3857 IS NOT NULL
|
||||
WHERE o.latitude IS NOT NULL AND o.longitude IS NOT NULL
|
||||
AND ST_DWithin(
|
||||
o.geom_3857,
|
||||
ST_SetSRID(ST_MakePoint(CAST(:x_3857 AS float), CAST(:y_3857 AS float)), 3857),
|
||||
ST_SetSRID(ST_MakePoint(o.longitude, o.latitude), 4326)::geography,
|
||||
ST_SetSRID(
|
||||
ST_MakePoint(CAST(:center_lon AS float), CAST(:center_lat AS float)),
|
||||
4326
|
||||
)::geography,
|
||||
CAST(:radius_m AS float)
|
||||
)
|
||||
AND f.snapshot_date = (SELECT MAX(snapshot_date) FROM domrf_kn_flats)
|
||||
|
|
@ -179,11 +186,6 @@ def _normalize_pct(buckets: dict[str, float]) -> dict[str, int]:
|
|||
return floors
|
||||
|
||||
|
||||
def _round_to_5(pct: int) -> int:
|
||||
"""Округлить до ближайших 5%."""
|
||||
return int(round(pct / 5.0) * 5)
|
||||
|
||||
|
||||
# ── Главная функция ───────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
|
|
@ -217,8 +219,8 @@ def get_best_layouts(
|
|||
if not coord_row:
|
||||
raise ValueError(f"Геометрия для {cad_num} не найдена")
|
||||
|
||||
x_3857 = float(coord_row["x_3857"])
|
||||
y_3857 = float(coord_row["y_3857"])
|
||||
center_lon = float(coord_row["center_lon"])
|
||||
center_lat = float(coord_row["center_lat"])
|
||||
|
||||
# ── Step 2: obj_id конкурентов в радиусе ────────────────────────────────
|
||||
try:
|
||||
|
|
@ -226,8 +228,8 @@ def get_best_layouts(
|
|||
db.execute(
|
||||
_COMPETITORS_IN_RADIUS_SQL,
|
||||
{
|
||||
"x_3857": x_3857,
|
||||
"y_3857": y_3857,
|
||||
"center_lon": center_lon,
|
||||
"center_lat": center_lat,
|
||||
"radius_m": radius_m,
|
||||
"obj_class_filter": request.obj_class_filter,
|
||||
},
|
||||
|
|
@ -281,7 +283,7 @@ def get_best_layouts(
|
|||
supply_rows = (
|
||||
db.execute(
|
||||
_SUPPLY_BATCH_SQL,
|
||||
{"x_3857": x_3857, "y_3857": y_3857, "radius_m": radius_m},
|
||||
{"center_lon": center_lon, "center_lat": center_lat, "radius_m": radius_m},
|
||||
)
|
||||
.mappings()
|
||||
.all()
|
||||
|
|
|
|||
|
|
@ -26,10 +26,10 @@ CAD_NUM = "66:41:0303161:123"
|
|||
_TODAY = dt.date.today()
|
||||
|
||||
|
||||
def _coord_row(x: float = 6747000.0, y: float = 7846000.0) -> MagicMock:
|
||||
"""Центроид участка в EPSG:3857."""
|
||||
def _coord_row(lon: float = 60.6, lat: float = 56.85) -> MagicMock:
|
||||
"""Центроид участка (EPSG:4326 lon/lat)."""
|
||||
r = MagicMock()
|
||||
r.__getitem__ = lambda self, k: {"x_3857": x, "y_3857": y}[k]
|
||||
r.__getitem__ = lambda self, k: {"center_lon": lon, "center_lat": lat}[k]
|
||||
return r
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue