MEGA-WEBSITE/app/scripts/build-sitemaps.py
David 702f5956c9 Add project progress modal, Why Mega scroll panel and three new projects
Projects can now be opened from the deck: a modal shows live progress per
section, the services on site and a plan map drawn from the site model.

- ProjectModal with per-section progress and site maps (src/data/sitemaps),
  generated by app/scripts/build-sitemaps.py
- progress.json holds the per-project section states
- Why Mega becomes a scroll-driven list with a sticky panel that follows the
  point in the middle of the screen, plus new pillar photographs
- PRJ-06 Munyaka, PRJ-07 Cedar Gate and PRJ-08 The Hills join the deck, so
  the track length and the site count now follow the number of projects
- Projects moves above Why Mega and the section labels renumber
- Contact section rebuilt on an asymmetric grid
- Intro counters keep their thousands separator and no longer wrap
- Dev server listens on the LAN so a phone on the same Wi-Fi can open the site
2026-11-03 19:26:10 +02:00

43 lines
1.9 KiB
Python

#!/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/<slug>.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)