mirror of
https://github.com/zvx-echo6/recon.git
synced 2026-06-10 00:44:37 +02:00
cleanup: remove orphaned lib/overture.py + lib/osm_categories.py (post-#27 dead code)
Both modules were flagged in cleanup #27 (PR #16) as fully orphaned once the
place_detail orchestrator cluster was deleted; Matt confirmed scope in chat.
- lib/overture.py (170L): only consumer was place_detail._enrich_with_overture
(deleted in #27).
- lib/osm_categories.py (143L): humanize_category's only callers were
place_detail._parse_nominatim / _parse_overpass (both deleted in #27).
Re-probed against master 79d7b2b: zero import/usage references anywhere outside
the modules themselves, zero template/JS refs, no test files. compileall lib/
passes.
Note: scripts/overture_import.py (the Overture-Maps→PostGIS ETL script) is
independent — imports nothing from lib/ — and is left untouched. After this PR
the `overture` PostGIS DB it populates has no remaining recon reader; that's a
data-ops follow-up, not code touched here.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
79d7b2b343
commit
aa6e972260
2 changed files with 0 additions and 313 deletions
|
|
@ -1,143 +0,0 @@
|
|||
"""
|
||||
Human-readable category names for OSM class/type pairs.
|
||||
|
||||
Used by the place detail proxy to turn ("amenity", "cafe") into "Coffee shop".
|
||||
Covers ~50 common categories; unmapped pairs fall back to title-cased class:type.
|
||||
"""
|
||||
|
||||
# Exact (class, type) → label
|
||||
CATEGORY_MAP = {
|
||||
# Amenity
|
||||
("amenity", "cafe"): "Coffee shop",
|
||||
("amenity", "restaurant"): "Restaurant",
|
||||
("amenity", "fast_food"): "Fast food restaurant",
|
||||
("amenity", "bar"): "Bar",
|
||||
("amenity", "pub"): "Pub",
|
||||
("amenity", "biergarten"): "Beer garden",
|
||||
("amenity", "ice_cream"): "Ice cream shop",
|
||||
("amenity", "fuel"): "Gas station",
|
||||
("amenity", "charging_station"): "EV charging station",
|
||||
("amenity", "parking"): "Parking",
|
||||
("amenity", "bank"): "Bank",
|
||||
("amenity", "atm"): "ATM",
|
||||
("amenity", "pharmacy"): "Pharmacy",
|
||||
("amenity", "hospital"): "Hospital",
|
||||
("amenity", "clinic"): "Clinic",
|
||||
("amenity", "dentist"): "Dentist",
|
||||
("amenity", "doctors"): "Doctor's office",
|
||||
("amenity", "veterinary"): "Veterinarian",
|
||||
("amenity", "school"): "School",
|
||||
("amenity", "university"): "University",
|
||||
("amenity", "college"): "College",
|
||||
("amenity", "library"): "Library",
|
||||
("amenity", "post_office"): "Post office",
|
||||
("amenity", "fire_station"): "Fire station",
|
||||
("amenity", "police"): "Police station",
|
||||
("amenity", "townhall"): "Town hall",
|
||||
("amenity", "place_of_worship"): "Place of worship",
|
||||
("amenity", "theatre"): "Theatre",
|
||||
("amenity", "cinema"): "Cinema",
|
||||
("amenity", "community_centre"): "Community center",
|
||||
("amenity", "toilets"): "Restrooms",
|
||||
("amenity", "drinking_water"): "Drinking water",
|
||||
("amenity", "shelter"): "Shelter",
|
||||
("amenity", "camping"): "Campground",
|
||||
# Shop
|
||||
("shop", "supermarket"): "Supermarket",
|
||||
("shop", "convenience"): "Convenience store",
|
||||
("shop", "hardware"): "Hardware store",
|
||||
("shop", "clothes"): "Clothing store",
|
||||
("shop", "car_repair"): "Auto repair",
|
||||
("shop", "car"): "Car dealership",
|
||||
("shop", "bakery"): "Bakery",
|
||||
("shop", "butcher"): "Butcher",
|
||||
# Leisure
|
||||
("leisure", "park"): "Park",
|
||||
("leisure", "playground"): "Playground",
|
||||
("leisure", "sports_centre"): "Sports center",
|
||||
("leisure", "swimming_pool"): "Swimming pool",
|
||||
("leisure", "golf_course"): "Golf course",
|
||||
("leisure", "nature_reserve"): "Nature reserve",
|
||||
("leisure", "campsite"): "Campsite",
|
||||
# Tourism
|
||||
("tourism", "hotel"): "Hotel",
|
||||
("tourism", "motel"): "Motel",
|
||||
("tourism", "guest_house"): "Guest house",
|
||||
("tourism", "hostel"): "Hostel",
|
||||
("tourism", "camp_site"): "Campsite",
|
||||
("tourism", "viewpoint"): "Viewpoint",
|
||||
("tourism", "museum"): "Museum",
|
||||
("tourism", "information"): "Information",
|
||||
("tourism", "attraction"): "Tourist attraction",
|
||||
("tourism", "picnic_site"): "Picnic site",
|
||||
# Natural
|
||||
("natural", "peak"): "Peak",
|
||||
("natural", "spring"): "Spring",
|
||||
("natural", "hot_spring"): "Hot spring",
|
||||
("natural", "lake"): "Lake",
|
||||
("natural", "water"): "Water body",
|
||||
("natural", "cliff"): "Cliff",
|
||||
("natural", "cave_entrance"): "Cave",
|
||||
# Highway
|
||||
("highway", "bus_stop"): "Bus stop",
|
||||
("highway", "rest_area"): "Rest area",
|
||||
# Boundary
|
||||
("boundary", "administrative"): "Administrative boundary",
|
||||
("boundary", "protected_area"): "Protected area",
|
||||
("boundary", "national_park"): "National park",
|
||||
# Place
|
||||
("place", "city"): "City",
|
||||
("place", "town"): "Town",
|
||||
("place", "village"): "Village",
|
||||
("place", "hamlet"): "Hamlet",
|
||||
("place", "suburb"): "Suburb",
|
||||
("place", "neighbourhood"): "Neighborhood",
|
||||
# Building
|
||||
("building", "yes"): "Building",
|
||||
# Waterway
|
||||
("waterway", "river"): "River",
|
||||
("waterway", "stream"): "Stream",
|
||||
("waterway", "waterfall"): "Waterfall",
|
||||
# Landuse
|
||||
("landuse", "cemetery"): "Cemetery",
|
||||
("landuse", "forest"): "Forest",
|
||||
# Historic
|
||||
("historic", "monument"): "Monument",
|
||||
("historic", "memorial"): "Memorial",
|
||||
("historic", "ruins"): "Ruins",
|
||||
}
|
||||
|
||||
# Class-level wildcard fallbacks (when exact type isn't mapped)
|
||||
CLASS_FALLBACKS = {
|
||||
"shop": "Shop",
|
||||
"amenity": "Amenity",
|
||||
"leisure": "Leisure",
|
||||
"tourism": "Tourism",
|
||||
"natural": "Natural feature",
|
||||
"historic": "Historic site",
|
||||
}
|
||||
|
||||
|
||||
def humanize_category(osm_class, osm_type):
|
||||
"""Return a human-readable category string for an OSM class/type pair."""
|
||||
if not osm_class or not osm_type:
|
||||
return "Place"
|
||||
|
||||
osm_class = osm_class.lower()
|
||||
osm_type = osm_type.lower()
|
||||
|
||||
# Exact match
|
||||
label = CATEGORY_MAP.get((osm_class, osm_type))
|
||||
if label:
|
||||
return label
|
||||
|
||||
# Class-level wildcard with formatted type
|
||||
prefix = CLASS_FALLBACKS.get(osm_class)
|
||||
if prefix:
|
||||
nice_type = osm_type.replace("_", " ").title()
|
||||
return f"{prefix}: {nice_type}" if prefix != nice_type else prefix
|
||||
|
||||
# Generic fallback
|
||||
nice_class = osm_class.replace("_", " ").title()
|
||||
nice_type = osm_type.replace("_", " ").title()
|
||||
return f"{nice_class}: {nice_type}"
|
||||
170
lib/overture.py
170
lib/overture.py
|
|
@ -1,170 +0,0 @@
|
|||
"""
|
||||
Overture Maps enrichment layer.
|
||||
|
||||
Provides lookup functions against the local PostgreSQL Overture Places database.
|
||||
Two strategies:
|
||||
1. find_by_osm_id — exact match via OSM cross-reference index
|
||||
2. find_by_coords_and_name — spatial + fuzzy name fallback
|
||||
|
||||
Connection pool is lazy-initialized on first call. If PostgreSQL is unreachable,
|
||||
functions return None gracefully (feature degrades, doesn't crash).
|
||||
"""
|
||||
import json
|
||||
import os
|
||||
|
||||
import psycopg2
|
||||
import psycopg2.pool
|
||||
|
||||
from .utils import setup_logging
|
||||
|
||||
logger = setup_logging('recon.overture')
|
||||
|
||||
_pool = None
|
||||
_pool_failed = False
|
||||
|
||||
# Map full OSM type names to single-letter codes used in Overture sources
|
||||
OSM_TYPE_MAP = {
|
||||
'N': 'n', 'W': 'w', 'R': 'r',
|
||||
'node': 'n', 'way': 'w', 'relation': 'r',
|
||||
'n': 'n', 'w': 'w', 'r': 'r',
|
||||
}
|
||||
|
||||
|
||||
def _get_pool():
|
||||
"""Lazy-init the connection pool. Returns None if Postgres is unreachable."""
|
||||
global _pool, _pool_failed
|
||||
if _pool is not None:
|
||||
return _pool
|
||||
if _pool_failed:
|
||||
return None
|
||||
|
||||
try:
|
||||
_pool = psycopg2.pool.SimpleConnectionPool(
|
||||
minconn=1,
|
||||
maxconn=3,
|
||||
host=os.environ.get('OVERTURE_DB_HOST', 'localhost'),
|
||||
port=int(os.environ.get('OVERTURE_DB_PORT', '5432')),
|
||||
dbname=os.environ.get('OVERTURE_DB_NAME', 'overture'),
|
||||
user=os.environ.get('OVERTURE_DB_USER', 'overture'),
|
||||
password=os.environ.get('OVERTURE_DB_PASSWORD', ''),
|
||||
connect_timeout=5,
|
||||
)
|
||||
logger.info("Overture PostgreSQL connection pool initialized")
|
||||
return _pool
|
||||
except Exception as e:
|
||||
_pool_failed = True
|
||||
logger.warning(f"Overture PostgreSQL unavailable, enrichment disabled: {e}")
|
||||
return None
|
||||
|
||||
|
||||
def _query(sql, params):
|
||||
"""Execute a query and return the first row as a dict, or None."""
|
||||
pool = _get_pool()
|
||||
if pool is None:
|
||||
return None
|
||||
|
||||
conn = None
|
||||
try:
|
||||
conn = pool.getconn()
|
||||
with conn.cursor() as cur:
|
||||
cur.execute(sql, params)
|
||||
row = cur.fetchone()
|
||||
if row is None:
|
||||
return None
|
||||
cols = [desc[0] for desc in cur.description]
|
||||
return dict(zip(cols, row))
|
||||
except Exception as e:
|
||||
logger.warning(f"Overture query error: {e}")
|
||||
if conn:
|
||||
try:
|
||||
conn.rollback()
|
||||
except Exception:
|
||||
pass
|
||||
return None
|
||||
finally:
|
||||
if conn:
|
||||
try:
|
||||
pool.putconn(conn)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def _format_result(row, match_method):
|
||||
"""Convert a database row dict to the enrichment result shape."""
|
||||
if not row:
|
||||
return None
|
||||
|
||||
socials = row.get('socials')
|
||||
if isinstance(socials, str):
|
||||
try:
|
||||
socials = json.loads(socials)
|
||||
except (json.JSONDecodeError, TypeError):
|
||||
socials = None
|
||||
|
||||
return {
|
||||
'phone': row.get('phone'),
|
||||
'website': row.get('website'),
|
||||
'socials': socials,
|
||||
'brand_name': row.get('brand_name'),
|
||||
'brand_wikidata': row.get('brand_wikidata'),
|
||||
'basic_category': row.get('basic_category'),
|
||||
'confidence': row.get('confidence'),
|
||||
'gers_id': row.get('id'),
|
||||
'match_method': match_method,
|
||||
}
|
||||
|
||||
|
||||
def find_by_osm_id(osm_type, osm_id):
|
||||
"""
|
||||
Look up an Overture place by its OSM cross-reference.
|
||||
|
||||
Args:
|
||||
osm_type: OSM type — 'N', 'W', 'R', 'node', 'way', 'relation', or single letter
|
||||
osm_id: OSM numeric ID
|
||||
|
||||
Returns:
|
||||
Enrichment dict or None
|
||||
"""
|
||||
type_letter = OSM_TYPE_MAP.get(osm_type)
|
||||
if not type_letter:
|
||||
return None
|
||||
|
||||
row = _query(
|
||||
"""SELECT id, name, basic_category, confidence,
|
||||
phone, website, socials, brand_name, brand_wikidata
|
||||
FROM places
|
||||
WHERE osm_type = %s AND osm_id = %s
|
||||
LIMIT 1""",
|
||||
(type_letter, int(osm_id))
|
||||
)
|
||||
return _format_result(row, 'osm_xref')
|
||||
|
||||
|
||||
def find_by_coords_and_name(lat, lon, name, radius_m=100):
|
||||
"""
|
||||
Look up an Overture place by spatial proximity + fuzzy name match.
|
||||
|
||||
Args:
|
||||
lat: Latitude
|
||||
lon: Longitude
|
||||
name: Place name to fuzzy-match
|
||||
radius_m: Search radius in meters (default 100)
|
||||
|
||||
Returns:
|
||||
Enrichment dict or None
|
||||
"""
|
||||
if not name or not lat or not lon:
|
||||
return None
|
||||
|
||||
row = _query(
|
||||
"""SELECT id, name, basic_category, confidence,
|
||||
phone, website, socials, brand_name, brand_wikidata,
|
||||
similarity(name, %s) AS sim
|
||||
FROM places
|
||||
WHERE ST_DWithin(geometry::geography, ST_MakePoint(%s, %s)::geography, %s)
|
||||
AND similarity(name, %s) > 0.4
|
||||
ORDER BY sim DESC, ST_Distance(geometry::geography, ST_MakePoint(%s, %s)::geography) ASC
|
||||
LIMIT 1""",
|
||||
(name, lon, lat, radius_m, name, lon, lat)
|
||||
)
|
||||
return _format_result(row, 'coord_name_fuzzy')
|
||||
Loading…
Add table
Add a link
Reference in a new issue