#!/usr/bin/env python3 """Builds the small public plan maps used by the project popup. Reads a site model's bushveld.json (road/paving areas grouped by section) and writes src/data/sitemaps/.json: sections in build order, each with simplified outline paths in a y-down viewBox. No levels, quantities or costs. """ import json, sys, pathlib MODELS = pathlib.Path.home() / 'DEVELOPMENT/MEGAIFRASTRUCTURE/reports/site-models' SOURCES = { 'munyaka': MODELS / 'munyaka/2026-10-28/bushveld.json', 'cedar-gate': MODELS / 'cedar-gate/2026-10-20/bushveld.json', # Only stored in the app's database; exported from the active site_models record. 'the-hills': pathlib.Path(__file__).resolve().parent / 'source/hills-bushveld.json', } OUT = pathlib.Path(__file__).resolve().parent.parent / 'src/data/sitemaps' def build(slug, src): d = json.load(open(src)) order = [s['key'] for s in d['sections']] names = {s['key']: s.get('name') or f"Chainage {s['from']:g}-{s['to']:g} m" for s in d['sections']} rings = {k: [] for k in order} for a in d['areas']: if a['section'] in rings: rings[a['section']] += a['rings'] pts = [p for rs in rings.values() for r in rs for p in r] x0, x1 = min(p[0] for p in pts), max(p[0] for p in pts) y0, y1 = min(p[1] for p in pts), max(p[1] for p in pts) pad = max(x1 - x0, y1 - y0) * 0.04 w, h = x1 - x0 + 2 * pad, y1 - y0 + 2 * pad def path(ring): return 'M' + 'L'.join(f'{p[0]-x0+pad:.1f} {y1-p[1]+pad:.1f}' for p in ring[:-1]) + 'Z' sections = [ {'key': k, 'name': names[k], 'd': ' '.join(path(r) for r in rings[k])} for k in order if rings[k] ] json.dump({'viewBox': [0, 0, round(w, 1), round(h, 1)], 'sections': sections}, open(OUT / f'{slug}.json', 'w'), separators=(',', ':')) print(slug, len(sections), 'sections', round(w), 'x', round(h)) for slug, src in SOURCES.items(): build(slug, src)