Builder now uses the furniture OSM already carries (footways, lamps, benches, bins, bollards, bus stops, crossings): raised chamfered footpaths, shopfront bands, awnings in three fabrics, parapets, layered fig/palm trees, zebra bars, dashed centre lines, Queen St Mall winged canopies. ACES tonemap + SSAO + fog in game.gd. flux_texture.sh gains size/luma clamping (FLUX renders "pale" as blown-out white); gen_textures.sh + check_textures.py manage the set. Woolies carpark rebuilt textured, parking the real fleet. Fixes: fractional OSM layer tag crash, zero-length-normal UV projection, blood-red mall pavers. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
96 lines
3.8 KiB
Python
96 lines
3.8 KiB
Python
#!/usr/bin/env python3
|
|
"""Convert an OSM API XML dump to overpass-style JSON for build_level_osm.py.
|
|
|
|
Usage: osm_xml_to_json.py <in.xml> <out.json>
|
|
|
|
Emits ways (tags+nodes+geometry), street-furniture nodes (trees, lamps, benches,
|
|
bins, bollards, bus stops, public art), and assembles water multipolygon
|
|
relations (the Brisbane River is a relation, not a way) into synthetic closed
|
|
ways tagged natural=water.
|
|
"""
|
|
import json
|
|
import sys
|
|
import xml.etree.ElementTree as ET
|
|
|
|
# node tags the level builder turns into props -- everything else is dropped
|
|
FURNITURE = {
|
|
"natural": ("tree",),
|
|
"highway": ("street_lamp", "bus_stop"),
|
|
"amenity": ("bench", "waste_basket", "drinking_water", "bicycle_parking", "post_box"),
|
|
"barrier": ("bollard",),
|
|
"tourism": ("artwork",),
|
|
}
|
|
WAY_KEYS = ("building", "highway", "leisure", "landuse", "natural", "water",
|
|
"waterway", "barrier", "man_made", "amenity", "tourism")
|
|
|
|
root = ET.parse(sys.argv[1]).getroot()
|
|
nodes = {}
|
|
elements = []
|
|
|
|
for n in root.iter("node"):
|
|
nid = int(n.get("id"))
|
|
lat, lon = float(n.get("lat")), float(n.get("lon"))
|
|
nodes[nid] = (lat, lon)
|
|
tags = {t.get("k"): t.get("v") for t in n.findall("tag")}
|
|
if any(tags.get(k) in v for k, v in FURNITURE.items()):
|
|
elements.append({"type": "node", "id": nid, "tags": tags, "lat": lat, "lon": lon})
|
|
|
|
ways = {}
|
|
for w in root.iter("way"):
|
|
wid = int(w.get("id"))
|
|
tags = {t.get("k"): t.get("v") for t in w.findall("tag")}
|
|
nds = [int(nd.get("ref")) for nd in w.findall("nd")]
|
|
if not all(r in nodes for r in nds) or len(nds) < 2:
|
|
continue
|
|
ways[wid] = nds
|
|
if any(tags.get(k) for k in WAY_KEYS):
|
|
elements.append({"type": "way", "id": wid, "tags": tags, "nodes": nds,
|
|
"geometry": [{"lat": nodes[r][0], "lon": nodes[r][1]} for r in nds]})
|
|
|
|
# water multipolygons: join outer member ways into rings
|
|
def assemble(rings_nodes):
|
|
segs = [list(s) for s in rings_nodes if len(s) >= 2]
|
|
rings = []
|
|
while segs:
|
|
ring = segs.pop()
|
|
grew = True
|
|
while grew and ring[0] != ring[-1]:
|
|
grew = False
|
|
for i, s in enumerate(segs):
|
|
if s[0] == ring[-1]:
|
|
ring += s[1:]
|
|
elif s[-1] == ring[-1]:
|
|
ring += list(reversed(s))[1:]
|
|
elif s[-1] == ring[0]:
|
|
ring = s + ring[1:]
|
|
elif s[0] == ring[0]:
|
|
ring = list(reversed(s)) + ring[1:]
|
|
else:
|
|
continue
|
|
segs.pop(i)
|
|
grew = True
|
|
break
|
|
if ring[0] == ring[-1] and len(ring) > 3:
|
|
rings.append(ring)
|
|
return rings
|
|
|
|
synth = 1
|
|
for r in root.iter("relation"):
|
|
tags = {t.get("k"): t.get("v") for t in r.findall("tag")}
|
|
if not (tags.get("natural") == "water" or tags.get("water") or tags.get("waterway") == "riverbank"):
|
|
continue
|
|
outers = [ways[int(m.get("ref"))] for m in r.findall("member")
|
|
if m.get("type") == "way" and m.get("role") in ("outer", "") and int(m.get("ref")) in ways]
|
|
for ring in assemble(outers):
|
|
elements.append({"type": "way", "id": 10**10 + synth, "tags": {"natural": "water"},
|
|
"nodes": ring, "geometry": [{"lat": nodes[n][0], "lon": nodes[n][1]} for n in ring]})
|
|
synth += 1
|
|
|
|
json.dump({"elements": elements}, open(sys.argv[2], "w"))
|
|
b = sum(1 for e in elements if e.get("tags", {}).get("building"))
|
|
h = sum(1 for e in elements if e.get("tags", {}).get("highway"))
|
|
w = sum(1 for e in elements if e.get("tags", {}).get("natural") == "water" or e.get("tags", {}).get("water"))
|
|
t = sum(1 for e in elements if e["type"] == "node" and e["tags"].get("natural") == "tree")
|
|
p = sum(1 for e in elements if e["type"] == "node") - t
|
|
print("converted: %d buildings, %d highways, %d water polys, %d trees, %d props" % (b, h, w, t, p))
|