From 2dd21f68b301bb46506f2774aa4e445a814add5c Mon Sep 17 00:00:00 2001 From: lekss361 Date: Mon, 18 May 2026 02:06:08 +0300 Subject: [PATCH] =?UTF-8?q?fix(api):=20/parcels/by-bbox=20=E2=80=94=20deri?= =?UTF-8?q?ve=20area=5Fm2=20via=20ST=5FArea=20+=20drop=20missing=20land=5F?= =?UTF-8?q?category?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit P0 prod 500 (GlitchTip backend, deploy #135+ on main): endpoint queried cad_parcels.area_m2 and cad_parcels.land_category, but the table only has (cad_num, geom). PR #336 (SF-B1) shipped with wrong column assumption. Repro: curl 'https://gendsgn.ru/api/v1/parcels/by-bbox?min_lat=...&min_lon=...' -> 500 ProgrammingError: column p.area_m2 does not exist Fix: compute area from geometry via ST_Area(p.geom::geography) (meter accuracy on a geography cast), return NULL::text for land_category until EGRN enrichment is wired (tracking: B5 EGRN contract follow-up). Verified inline through postgres MCP: SQL returns expected rows (66:41:0001001:18 area 2945931 m^2, etc.) on production schema. --- backend/app/api/v1/parcels.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/backend/app/api/v1/parcels.py b/backend/app/api/v1/parcels.py index f63ed9a2..918eba14 100644 --- a/backend/app/api/v1/parcels.py +++ b/backend/app/api/v1/parcels.py @@ -1034,6 +1034,9 @@ async def get_parcels_by_bbox( lon_km = (max_lon - min_lon) * 111.32 * math.cos(math.radians(lat_mid)) bbox_area_km2 = round(lat_km * lon_km, 4) + # cad_parcels has only (cad_num, geom) — area derived via ST_Area on geography + # cast for meter accuracy. land_category not stored on this table; returned NULL + # until enrichment from EGRN is wired (tracking: vault B5 EGRN contract note). sql = text(""" WITH bbox AS ( SELECT ST_MakeEnvelope( @@ -1048,8 +1051,8 @@ async def get_parcels_by_bbox( p.cad_num, ST_Y(ST_Centroid(p.geom)) AS centroid_lat, ST_X(ST_Centroid(p.geom)) AS centroid_lon, - p.area_m2, - p.land_category, + ST_Area(p.geom::geography) AS area_m2, + NULL::text AS land_category, pus.status AS user_status FROM cad_parcels p CROSS JOIN bbox @@ -1058,7 +1061,7 @@ async def get_parcels_by_bbox( AND pus.user_id = CAST(:user_id AS text) WHERE p.geom IS NOT NULL AND ST_Intersects(p.geom, bbox.env) - ORDER BY p.area_m2 DESC NULLS LAST + ORDER BY ST_Area(p.geom::geography) DESC NULLS LAST LIMIT CAST(:limit AS int) """) -- 2.45.3