feat(site-finder): v3.1 — cad_parcels_geom, analyze fallback, POI lat/lon, OSM expand
- nspd_geo: add _save_parcel() for thematic_id=1 → cad_parcels_geom (UPSERT, ST_Transform from Web Mercator); _persist_target now handles 1/2/5 - parcels.py: analyze endpoint geom lookup extended with cad_parcels_geom as 3rd fallback source (after cad_quarters_geom, cad_buildings); both SELECT and WKT subqueries updated - parcels.py: POI score_breakdown items now include lat/lon for map markers - poi_loader: OSM_CATEGORIES expanded — college+university→school, hypermarket→shop_supermarket; coverage +3 tag pairs
This commit is contained in:
parent
ea3d58fa7d
commit
f419900968
3 changed files with 48 additions and 4 deletions
|
|
@ -68,6 +68,12 @@ def analyze_parcel(
|
|||
'cad_building' AS source
|
||||
FROM cad_buildings b
|
||||
WHERE b.cad_num = :c
|
||||
UNION ALL
|
||||
SELECT ST_AsGeoJSON(p.geom) AS geom_geojson,
|
||||
p.geom AS geom_wkb,
|
||||
'cad_parcel' AS source
|
||||
FROM cad_parcels_geom p
|
||||
WHERE p.cad_num = :c
|
||||
LIMIT 1
|
||||
"""),
|
||||
{"c": cad_num},
|
||||
|
|
@ -95,6 +101,8 @@ def analyze_parcel(
|
|||
SELECT g.geom FROM cad_quarters_geom g WHERE g.cad_number = :c
|
||||
UNION ALL
|
||||
SELECT b.geom FROM cad_buildings b WHERE b.cad_num = :c
|
||||
UNION ALL
|
||||
SELECT p.geom FROM cad_parcels_geom p WHERE p.cad_num = :c
|
||||
) g
|
||||
LIMIT 1
|
||||
"""),
|
||||
|
|
@ -171,6 +179,8 @@ def analyze_parcel(
|
|||
{
|
||||
"name": p["name"],
|
||||
"distance_m": round(float(p["distance_m"])),
|
||||
"lat": float(p["lat"]) if p["lat"] is not None else None,
|
||||
"lon": float(p["lon"]) if p["lon"] is not None else None,
|
||||
"last_edit": (
|
||||
p["last_osm_edit_date"].isoformat() if p["last_osm_edit_date"] else None
|
||||
),
|
||||
|
|
|
|||
|
|
@ -21,20 +21,23 @@ EKB_BBOX = (56.7, 60.5, 56.95, 60.75) # (south, west, north, east)
|
|||
|
||||
# Маппинг OSM-тег → нормализованная category
|
||||
OSM_CATEGORIES: dict[tuple[str, str], str] = {
|
||||
# amenity tags
|
||||
# amenity tags — школы расширены (school/college/university)
|
||||
("amenity", "school"): "school",
|
||||
("amenity", "college"): "school",
|
||||
("amenity", "university"): "school",
|
||||
("amenity", "kindergarten"): "kindergarten",
|
||||
("amenity", "pharmacy"): "pharmacy",
|
||||
("amenity", "hospital"): "hospital",
|
||||
("amenity", "clinic"): "hospital",
|
||||
# shop tags
|
||||
# shop tags — supermarket расширен
|
||||
("shop", "mall"): "shop_mall",
|
||||
("shop", "supermarket"): "shop_supermarket",
|
||||
("shop", "hypermarket"): "shop_supermarket",
|
||||
("shop", "convenience"): "shop_small",
|
||||
("shop", "bakery"): "shop_small",
|
||||
# leisure
|
||||
("leisure", "park"): "park",
|
||||
# transit (railway tram_stop приоритет над public_transport)
|
||||
# transit
|
||||
("railway", "tram_stop"): "tram_stop",
|
||||
("highway", "bus_stop"): "bus_stop",
|
||||
# метро (одна линия в ЕКБ, но добавляем для полноты)
|
||||
|
|
|
|||
|
|
@ -207,13 +207,44 @@ def _save_building(db: Session, payload: dict, cad_num: str) -> int:
|
|||
return n
|
||||
|
||||
|
||||
def _save_parcel(db: Session, payload: dict, cad_num: str) -> int:
|
||||
"""Парсит NSPD-response для земельного участка и UPSERT'ит в cad_parcels_geom."""
|
||||
feats = (payload.get("data") or {}).get("features") or []
|
||||
if not feats:
|
||||
return 0
|
||||
f = feats[0]
|
||||
geom_geojson = f.get("geometry")
|
||||
if not geom_geojson:
|
||||
return 0
|
||||
db.execute(
|
||||
text(
|
||||
"""
|
||||
INSERT INTO cad_parcels_geom (cad_num, geom, raw_props, fetched_at)
|
||||
VALUES (:cad, ST_Transform(ST_SetSRID(ST_GeomFromGeoJSON(:g), 3857), 4326),
|
||||
CAST(:props AS jsonb), NOW())
|
||||
ON CONFLICT (cad_num) DO UPDATE
|
||||
SET geom = EXCLUDED.geom,
|
||||
raw_props = EXCLUDED.raw_props,
|
||||
fetched_at = NOW()
|
||||
"""
|
||||
),
|
||||
{
|
||||
"cad": cad_num,
|
||||
"g": json.dumps(geom_geojson, ensure_ascii=False),
|
||||
"props": json.dumps(f.get("properties") or {}, ensure_ascii=False),
|
||||
},
|
||||
)
|
||||
return 1
|
||||
|
||||
|
||||
def _persist_target(db: Session, thematic_id: int, payload: dict, cad_num: str) -> tuple[int, str]:
|
||||
"""Returns (n_features_saved, table_name). Полиморфно по thematic_id."""
|
||||
if thematic_id == 1:
|
||||
return _save_parcel(db, payload, cad_num), "cad_parcels_geom"
|
||||
if thematic_id == 2:
|
||||
return _save_quarter(db, payload, cad_num), "cad_quarters_geom"
|
||||
if thematic_id == 5:
|
||||
return _save_building(db, payload, cad_num), "cad_buildings"
|
||||
# Для parcel (1) пока нет нашей normalized-таблицы — только raw в targets.result_features
|
||||
return 0, ""
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue