Copenhagen Phase 2: prepare.py merges + clips + colours -> master.gpkg
- prepare.py: merge per-mode OSM lines, resolve colours (GTFS route_color -> OSM colour tag -> keyed palette, robust to pd.NA), categorise buses (A/C/S/regular via regex), clip lines+stops to area, write master.gpkg layers (lines/stops/stations) - download_osm.py: add stations query (public_transport=station / railway=station nodes+areas, out center) -> stations.gpkg with proper human-readable names + subway/light_rail mode tags for labelling - verified: 135 lines (8 metro/16 s-tog/28 regional/83 bus), 90 named stations, zero NaN colours
This commit is contained in:
@@ -126,14 +126,64 @@ def parse(data, mode):
|
||||
return lines, stops
|
||||
|
||||
|
||||
def stations_query(bbox):
|
||||
s, n, w, e = bbox
|
||||
return f"""[out:json][timeout:180];
|
||||
(
|
||||
node["railway"="station"]({s},{w},{n},{e});
|
||||
node["public_transport"="station"]({s},{w},{n},{e});
|
||||
way["railway"="station"]({s},{w},{n},{e});
|
||||
way["public_transport"="station"]({s},{w},{n},{e});
|
||||
);
|
||||
out center;
|
||||
"""
|
||||
|
||||
|
||||
def parse_stations(data):
|
||||
"""Stations (nodes or area centroids) with proper human-readable names."""
|
||||
rows = []
|
||||
seen = set()
|
||||
for e in data["elements"]:
|
||||
if e["type"] not in ("node", "way"):
|
||||
continue
|
||||
tags = e.get("tags", {})
|
||||
name = tags.get("name")
|
||||
if not name:
|
||||
continue
|
||||
if e["type"] == "node":
|
||||
geom = Point(e["lon"], e["lat"])
|
||||
else:
|
||||
c = e.get("center")
|
||||
if not c:
|
||||
continue
|
||||
geom = Point(c["lon"], c["lat"])
|
||||
key = (name, round(geom.x, 5), round(geom.y, 5))
|
||||
if key in seen:
|
||||
continue
|
||||
seen.add(key)
|
||||
rows.append({
|
||||
"name": name,
|
||||
"railway": tags.get("railway"),
|
||||
"public_transport": tags.get("public_transport"),
|
||||
"station": tags.get("station"),
|
||||
"subway": tags.get("subway"),
|
||||
"light_rail": tags.get("light_rail"),
|
||||
"train": tags.get("train"),
|
||||
"uic_ref": tags.get("uic_ref"),
|
||||
"geometry": geom,
|
||||
})
|
||||
return rows
|
||||
|
||||
|
||||
def main():
|
||||
modes = yaml.safe_load((CONFIG / "modes.yaml").read_text())["modes"]
|
||||
area = gpd.read_file(PROCESSED / "area.gpkg")
|
||||
w, s, e, n = area.total_bounds # (minx, miny, maxx, maxy) = (west, south, east, north)
|
||||
bbox = (s, n, w, e)
|
||||
|
||||
OSM_RAW.mkdir(parents=True, exist_ok=True)
|
||||
for mode, cfg in modes.items():
|
||||
ql = build_query(cfg, (s, n, w, e))
|
||||
ql = build_query(cfg, bbox)
|
||||
(OSM_RAW / f"{mode}.overpassql").write_text(ql)
|
||||
print(f"[{mode}] querying Overpass bbox=({s:.4f},{w:.4f},{n:.4f},{e:.4f})...", flush=True)
|
||||
data = overpass(ql)
|
||||
@@ -146,6 +196,18 @@ def main():
|
||||
if stops:
|
||||
sgdf = gpd.GeoDataFrame(stops, crs="EPSG:4326")
|
||||
sgdf.to_file(OSM_RAW / f"{mode}.gpkg", driver="GPKG", layer="stops")
|
||||
|
||||
# stations (named station nodes/areas) for labelling
|
||||
sql = stations_query(bbox)
|
||||
(OSM_RAW / "stations.overpassql").write_text(sql)
|
||||
print("[stations] querying Overpass...", flush=True)
|
||||
sdata = overpass(sql)
|
||||
st = parse_stations(sdata)
|
||||
print(f" -> {len(st)} named stations", flush=True)
|
||||
if st:
|
||||
gpd.GeoDataFrame(st, crs="EPSG:4326").to_file(
|
||||
OSM_RAW / "stations.gpkg", driver="GPKG", layer="stations"
|
||||
)
|
||||
print("done.")
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user