refactor: ditch Nominatim, use reverse_geocoder exclusively for geocoding
Nominatim was slow (rate-limited, 1 req/sec, ~4s timeouts) and unreliable from Docker/Cloudflare (blocked or throttled). It was the root cause of location labels failing to appear during trip scrubbing. - server/app.py: _geocode_cached now uses reverse_geocoder exclusively (offline, instant, city/province level). Removed Nominatim HTTP calls, ThreadPoolExecutor, concurrent.futures import. - app.py (Pi): same — reverse_geocoder imported at module level, Nominatim + urllib.request removed. - Batch endpoint simplified to sequential loop with lru_cache + dedup. No network dependency at all — geocoding is instant and always works. reverse_geocoder provides city/province labels (e.g. 'Montreal, Quebec') which is the right granularity for trip scrubbing.
This commit is contained in:
@@ -163,36 +163,23 @@ def api_delete_trip(trip_id):
|
||||
return jsonify(status="deleted"), 200
|
||||
|
||||
import functools
|
||||
import urllib.request
|
||||
import reverse_geocoder as rg
|
||||
|
||||
@functools.lru_cache(maxsize=512)
|
||||
def _geocode_cached(lat4, lon4):
|
||||
"""Try Nominatim first (street-level), fall back to reverse_geocoder (city),
|
||||
then raw coordinates. Always returns a non-empty label string."""
|
||||
"""Reverse-geocode a coordinate to city/province using the offline
|
||||
reverse_geocoder library. Always returns a non-empty label string."""
|
||||
lat = lat4 / 10000.0
|
||||
lon = lon4 / 10000.0
|
||||
try:
|
||||
url = f"https://nominatim.openstreetmap.org/reverse?format=jsonv2&lat={lat}&lon={lon}&accept-language=en"
|
||||
req = urllib.request.Request(url, headers={"User-Agent": "yaris-obd/1.0"})
|
||||
with urllib.request.urlopen(req, timeout=4) as resp:
|
||||
data = json.loads(resp.read())
|
||||
a = data.get("address", {})
|
||||
road = a.get("road") or a.get("hamlet") or a.get("suburb") or ""
|
||||
city = a.get("city") or a.get("town") or a.get("village") or a.get("municipality") or ""
|
||||
label = ", ".join(p for p in [road, city] if p)
|
||||
if label:
|
||||
return label
|
||||
results = rg.search([(lat, lon)], mode=1, verbose=False)
|
||||
if results:
|
||||
r = results[0]
|
||||
label = ", ".join(p for p in [r.get("name", ""), r.get("admin1", "")] if p)
|
||||
if label:
|
||||
return label
|
||||
except Exception:
|
||||
pass
|
||||
# Offline fallback
|
||||
import reverse_geocoder as rg
|
||||
results = rg.search([(lat, lon)], mode=1, verbose=False)
|
||||
if results:
|
||||
r = results[0]
|
||||
label = ", ".join(p for p in [r.get("name", ""), r.get("admin1", "")] if p)
|
||||
if label:
|
||||
return label
|
||||
# Last resort — raw coordinates so the frontend never gets an empty label
|
||||
return f"{lat:.5f}, {lon:.5f}"
|
||||
|
||||
@app.route('/api/geocode')
|
||||
|
||||
Reference in New Issue
Block a user