"""Add more POI categories: parking, gym/fitness, theater, taxi, marketplaces, etc.""" import sqlite3, pathlib, requests, time, json, math, urllib3 urllib3.disable_warnings() DB = pathlib.Path(__file__).parent / "analysis.db" CACHE = pathlib.Path(__file__).parent / "cache" / "overpass_raw.json" NEW_QUERIES = [ ("parking", '["amenity"="parking"]'), ("gym", '["leisure"="fitness_centre"]'), ("theater", '["amenity"="theatre"]'), ("cinema", '["amenity"="cinema"]'), ("marketplace", '["amenity"="marketplace"]'), ("museum", '["tourism"="museum"]'), ("hotel", '["tourism"~"^(hotel|hostel|apartment)$"]'), ("nightclub", '["amenity"~"^(nightclub|bar|pub)$"]'), ("car_wash", '["amenity"="car_wash"]'), ("car_rental", '["amenity"~"^(car_rental|taxi)$"]'), ("vet", '["amenity"="veterinary"]'), ("courier", '["amenity"~"^(parcel_locker|post_box)$"]'), ("kindergarten_priv", '["amenity"="kindergarten"]["fee"="yes"]'), # platnyye ("dentist", '["amenity"="dentist"]'), ("childcare", '["amenity"="childcare"]'), ] ENDPOINTS = [ "https://overpass-api.de/api/interpreter", "https://overpass.kumi.systems/api/interpreter", ] def hav(la1,lo1,la2,lo2): R=6371000;p1,p2=math.radians(la1),math.radians(la2) dp=math.radians(la2-la1);dl=math.radians(lo2-lo1) a=math.sin(dp/2)**2+math.cos(p1)*math.cos(p2)*math.sin(dl/2)**2 return 2*R*math.asin(math.sqrt(a)) def overpass(filt, b): q = f'[out:json][timeout:120];(node{filt}({b[0]},{b[1]},{b[2]},{b[3]});way{filt}({b[0]},{b[1]},{b[2]},{b[3]}););out center tags;' last = None for ep in ENDPOINTS: try: r = requests.post(ep, data={"data": q}, timeout=180, verify=False, headers={"User-Agent":"gendesign/0.7"}) if r.ok: return r.json().get("elements", []) last = f"{ep} {r.status_code}" time.sleep(2) except Exception as e: last = f"{ep} {e}"; time.sleep(2) raise RuntimeError(last) def main(): cache = json.loads(CACHE.read_text()) if CACHE.exists() else {} conn = sqlite3.connect(DB) sites = conn.execute("SELECT site_id, lat, lon FROM sites").fetchall() lats=[s[1] for s in sites]; lons=[s[2] for s in sites] b = (min(lats)-0.05, min(lons)-0.05, max(lats)+0.05, max(lons)+0.05) for cat, filt in NEW_QUERIES: if cat in cache: print(f" {cat:<20} cached: {len(cache[cat])}") continue print(f" {cat:<20} fetching...", end=" ", flush=True) try: elems = overpass(filt, b) cache[cat] = elems print(f"{len(elems)}") except Exception as e: print(f"FAIL {e}") cache[cat] = [] time.sleep(2) CACHE.write_text(json.dumps(cache, ensure_ascii=False)) print(f"\nCache: {len(cache)} categories, {CACHE.stat().st_size/1024:.0f} KB") # rebuild pois table conn.execute("DELETE FROM pois") n = 0 for cat, elems in cache.items(): for el in elems: if el["type"] == "node": la, lo = el.get("lat"), el.get("lon") else: c = el.get("center") or {} la, lo = c.get("lat"), c.get("lon") if la is None: continue tags = el.get("tags") or {} name = tags.get("name") or tags.get("operator") or "" for sid, sla, slo in sites: d = hav(sla, slo, la, lo) if d <= 2000: conn.execute("""INSERT INTO pois(site_id,category,osm_type,osm_id,name,lat,lon,distance_m,raw_tags) VALUES (?,?,?,?,?,?,?,?,?)""", (sid, cat, el["type"], str(el["id"]), name, la, lo, d, json.dumps(tags, ensure_ascii=False))) n += 1 conn.commit() print(f"recomputed {n} site-poi pairs") print("\nTop categories:") for c, n in conn.execute("SELECT category, count(*) FROM pois GROUP BY 1 ORDER BY 2 DESC LIMIT 25").fetchall(): print(f" {c:<20} {n}") conn.close() if __name__ == "__main__": main()