#!/usr/bin/env python3 """Compute Spawn + RacePath args for build_level_osm.py from Overpass JSON. Usage: osm_level_args.py Spawn: a point on the named way that is NOT inside any building footprint (CBD malls have awnings/buildings mapped over them), bearing taken from that point's own segment. RacePath: intersection nodes of consecutive loop streets. Prints two shell-ready args: "lat,lon,bearing" and "lat,lon;lat,lon;..." """ import json import math import sys data = json.load(open(sys.argv[1])) spawn_name = sys.argv[2] loop = sys.argv[3].split(",") M_LAT = 110574.0 def xy(lat, lon, lat0): return (lon * 111320.0 * math.cos(math.radians(lat0)), lat * M_LAT) ways = {} polys = [] for e in data["elements"]: tags = e.get("tags", {}) name = tags.get("name") if name and e.get("nodes") and e.get("geometry"): ways.setdefault(name, []).append(e) if tags.get("building") and e.get("geometry"): polys.append([(g["lat"], g["lon"]) for g in e["geometry"]]) def inside(lat, lon, poly): n = len(poly) hit = False for i in range(n - 1): (y1, x1), (y2, x2) = poly[i], poly[i + 1] if (y1 > lat) != (y2 > lat) and lon < (x2 - x1) * (lat - y1) / (y2 - y1) + x1: hit = not hit return hit def in_any_building(lat, lon): return any(inside(lat, lon, p) for p in polys) sw = ways.get(spawn_name) assert sw, "no way named %r" % spawn_name candidates = [] for w in sw: geo = w["geometry"] for i in range(1, len(geo) - 1): g = geo[i] if in_any_building(g["lat"], g["lon"]): continue a, b = geo[i - 1], geo[i + 1] bearing = math.degrees(math.atan2( (b["lon"] - a["lon"]) * math.cos(math.radians(g["lat"])), b["lat"] - a["lat"])) % 360 candidates.append((g["lat"], g["lon"], bearing)) assert candidates, "every point of %r is inside a building" % spawn_name clat = sum(c[0] for c in candidates) / len(candidates) clon = sum(c[1] for c in candidates) / len(candidates) lat, lon, bearing = min(candidates, key=lambda c: (c[0] - clat) ** 2 + (c[1] - clon) ** 2) print("%.6f,%.6f,%.1f" % (lat, lon, bearing)) def nodes_of(name): out = {} for w in ways.get(name, []): for nid, g in zip(w["nodes"], w["geometry"]): out[nid] = (g["lat"], g["lon"]) return out if loop[0].startswith("trace:"): # follow the longest way of this name; the curve closes itself back tname = loop[0][6:] assert tname in ways, "no way named %r" % tname geo = max(ways[tname], key=lambda w: len(w["geometry"]))["geometry"] step = max(1, len(geo) // 12) picked = geo[::step] if picked[-1] != geo[-1]: picked.append(geo[-1]) print(";".join("%.6f,%.6f" % (g["lat"], g["lon"]) for g in picked)) sys.exit(0) corners = [] for i, street in enumerate(loop): nxt = loop[(i + 1) % len(loop)] shared = set(nodes_of(street)) & set(nodes_of(nxt)) assert shared, "no intersection %s x %s" % (street, nxt) lat, lon = nodes_of(street)[sorted(shared)[0]] corners.append("%.6f,%.6f" % (lat, lon)) print(";".join(corners))