From 483a4b75c63447bb0c1674172b544faff91b5d40 Mon Sep 17 00:00:00 2001 From: m3ultra Date: Thu, 16 Jul 2026 14:49:40 +1000 Subject: [PATCH] =?UTF-8?q?Lane=20E=20R17=20(v3.2):=20the=20real-map=20sco?= =?UTF-8?q?ut=20=E2=80=94=205=20AU=20town=20caches=20+=20publish=20--verif?= =?UTF-8?q?y=20fix?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ledger #4 (warm-up): publish.py --verify no longer false-flags non-procity_ published assets (sit.glb, vintage-cash-register). It compares the _published.json name record against the depot's full file list (fetch_depot_files) instead of a procity_ prefix filter; only OUR namespace flags as unrecorded. --verify now: all 39 recorded assets live. Ledger #6 (the scout): new pipeline/build_towns.py ports thriftgod build_city()'s Overpass route to per-town caches in A's procity-town-cache/1 contract. 5 real compact AU towns under web/assets/towns/ (katoomba/newtown/fremantle/bendigo/castlemaine, 6-21 shops, spans <2.4km): bounded Overpass query -> raw cached in-repo (re-runs never re-fetch) -> dedupe/suburb-fill/parody (trademarked chains only; indie shops keep real OSM names)/type-map. Deterministic (byte-identical re-run, no RNG). ODbL: SOURCES.md licence table + license/attribution on every cache. All 5 boot through the full structural+gig suites (selfcheck 16596/16596 green; synthetic golden 0x3fa36874 unmoved). Overpass fetch John-authorized (Standing #2). -> Lane A: caches live under web/assets/towns/; pin the per-town goldens selfcheck printed (katoomba 0x61760542 newtown 0x95978b45 fremantle 0xee9fd09a bendigo 0x5e3e76a6 castlemaine 0xf1a628a1) + feed through plan_osm. E->A handshake. Co-Authored-By: Claude Opus 4.8 --- docs/LANES/LANE_E_NOTES.md | 38 ++ pipeline/build_towns.py | 193 ++++++++++ pipeline/publish.py | 34 +- web/assets/towns/SOURCES.md | 40 ++ .../towns/_raw/bendigo_real.overpass.json | 218 +++++++++++ .../towns/_raw/castlemaine_real.overpass.json | 99 +++++ .../towns/_raw/fremantle_real.overpass.json | 354 ++++++++++++++++++ .../towns/_raw/katoomba_real.overpass.json | 299 +++++++++++++++ .../towns/_raw/newtown_real.overpass.json | 338 +++++++++++++++++ web/assets/towns/bendigo_real.json | 98 +++++ web/assets/towns/castlemaine_real.json | 74 ++++ web/assets/towns/fremantle_real.json | 194 ++++++++++ web/assets/towns/katoomba_real.json | 186 +++++++++ web/assets/towns/newtown_real.json | 170 +++++++++ 14 files changed, 2322 insertions(+), 13 deletions(-) create mode 100644 pipeline/build_towns.py create mode 100644 web/assets/towns/SOURCES.md create mode 100644 web/assets/towns/_raw/bendigo_real.overpass.json create mode 100644 web/assets/towns/_raw/castlemaine_real.overpass.json create mode 100644 web/assets/towns/_raw/fremantle_real.overpass.json create mode 100644 web/assets/towns/_raw/katoomba_real.overpass.json create mode 100644 web/assets/towns/_raw/newtown_real.overpass.json create mode 100644 web/assets/towns/bendigo_real.json create mode 100644 web/assets/towns/castlemaine_real.json create mode 100644 web/assets/towns/fremantle_real.json create mode 100644 web/assets/towns/katoomba_real.json create mode 100644 web/assets/towns/newtown_real.json diff --git a/docs/LANES/LANE_E_NOTES.md b/docs/LANES/LANE_E_NOTES.md index ccb1a66..b67295d 100644 --- a/docs/LANES/LANE_E_NOTES.md +++ b/docs/LANES/LANE_E_NOTES.md @@ -1,5 +1,42 @@ # LANE E — cross-lane notes +## Round 17 — v3.2 THE SCOUT: real town caches + the verify fix ⟨v3.2 amendment⟩ + +> The real-map scout for **v4 THE REAL MAP** (John's pick) — bounded data groundwork, no new game +> systems. **5 real AU town caches produced + the `--verify` false-drift fixed.** → **Lane A: caches +> are live under `web/assets/towns/`; pin the per-town goldens** (printed below by selfcheck) and feed +> them through `plan_osm` — E→A handshake. + +**Ledger #4 — `publish.py --verify` false-drift fixed (the warm-up).** The drift check filtered the +depot list to `procity_*`, so genuinely-published non-`procity_` assets (`sit.glb`, +`vintage-cash-register.glb`) were false-flagged "not live." Now it compares the published-NAME record +(`_published.json`) against the depot's full file list (`fetch_depot_files()`), confirming any name is +live — and only flags OUR `procity_` namespace as unrecorded, ignoring other projects' files on the +shared depot. `--verify` now: `✓ all 39 recorded assets live`. + +**Ledger #6 — the real-map scout (the round's biggest item).** `pipeline/build_towns.py` ports +thriftgod `build_city()`'s Overpass route to **per-town** caches in A's `procity-town-cache/1` contract +(`validateTownCache()`). Pipeline: **bounded** Overpass query → raw response cached in-repo → dedupe +(OSM node+way) → suburb-fill → **parody** the trademarked charity/franchise chains (indie shops keep +real OSM names) → OSM `shop=*` → registry SHOP_TYPE → `web/assets/towns/.json`. + +| cache | town | shops | span | types | selfcheck golden (→ A pins) | +|---|---|---|---|---|---| +| `katoomba_real` | Katoomba NSW | 20 | 1.11 km | book4 opshop13 record2 video1 | `0x61760542` | +| `newtown_real` | Newtown, Sydney | 18 | 2.36 km | book4 opshop6 record5 toy2 video1 | `0x95978b45` | +| `fremantle_real` | Fremantle WA | 21 | 1.89 km | book5 opshop10 pawn1 record1 toy3 video1 | `0xee9fd09a` | +| `bendigo_real` | Bendigo VIC | 9 | 2.06 km | book3 opshop3 pawn1 record1 video1 | `0x5e3e76a6` | +| `castlemaine_real` | Castlemaine VIC | 6 | 1.25 km | book2 opshop4 | `0xf1a628a1` | + +**Data discipline:** deterministic (same raw → byte-identical cache; shops id-sorted, no RNG — verified +re-run byte-identical); bounded (all spans < 2.4 km, well under the 5 km mega-strip floor); raw +responses committed to `web/assets/towns/_raw/` so **re-runs never re-fetch** (Overpass hit once per +town, John-authorized). `katoomba_real` is the fixture-comparison town (vs the hand-made `katoomba`). +All 5 boot through the full structural + gig suites (selfcheck **16596/16596 green, synthetic golden +`0x3fa36874` unmoved**). **Provenance:** OSM is **ODbL** — `SOURCES.md` (E-owned licence table) + +`license`/`attribution` fields on every cache; only public POI data (name/type/coord/suburb), no +private-individual data. + ## Round 16 — v3.1 THE FLIP: the drummer's sit clip (E asset half) ⟨v3.1 amendment⟩ > Ledger #2 asset half — the drummer finally sits. **E produces the clip; D wires it** (`_rotWithHips` @@ -69,6 +106,7 @@ loader wiring (`spawnRig`/fleet). Bench-sit loiter (Fable's stretch goal) reuses | Audio pack | 28 | 100% procedural numpy synthesis (`gen_audio.py`) | oscillator/noise render → ffmpeg OGG+M4A | 🟢 CC0-equiv, parody-safe | | Stock packs | 3 (350/311/273) | GODVERSE `dealgod` Postgres — **real** cover art | `build_stock_pack.py`: parody-transform metadata + atlas | 🟢 real-art / parody-metadata | | Ped clip **⟨v3.1⟩** | `sit.glb` (E R16; + pre-existing `walk`/`idle` from D/90sDJsim) | Adobe **Mixamo** `Sitting_Idle.fbx` | `fbx_to_clip.py` (Blender, mesh-strip) | 🟢 Mixamo royalty-free (motion only, mesh stripped) | +| Town caches **⟨v3.2⟩** | 5 real AU towns (Katoomba/Newtown/Fremantle/Bendigo/Castlemaine) | **OpenStreetMap** via Overpass API | `build_towns.py` (fetch → dedupe/suburb/parody/type-map) | 🟢 **ODbL 1.0** (© OSM contributors; `web/assets/towns/SOURCES.md`) | **GLB fleet = 33 manifest (23 fit + 10 furniture) + 5 `_hi` = 38 published.** Only external spend ever: ~$0.64 (fal-Hunyuan, R5). Everything else is $0 / on-device (electricity only). diff --git a/pipeline/build_towns.py b/pipeline/build_towns.py new file mode 100644 index 0000000..ae77361 --- /dev/null +++ b/pipeline/build_towns.py @@ -0,0 +1,193 @@ +#!/usr/bin/env python3 +"""build_towns.py — the real-map scout (ROUND17 ledger #6). A port of thriftgod `build_city()`'s +Overpass route to PER-TOWN real Australian caches in the Lane-A `procity-town-cache/1` contract. + + python3 pipeline/build_towns.py # build every town (cached raw reused, no re-fetch) + python3 pipeline/build_towns.py katoomba_real # one town + python3 pipeline/build_towns.py --refetch # ignore cached raw, re-hit Overpass (deliberate) + +Pipeline per town: bounded Overpass query → RAW response cached in-repo (`web/assets/towns/_raw/`, so +re-runs NEVER re-fetch) → dedupe (OSM maps a shop as node AND way) → suburb fill (untagged adopt the +nearest tagged neighbour) → parody the big trademarked charity/franchise names (indie shops keep their +real OSM name) → map OSM `shop=*` to a registry SHOP_TYPE → `web/assets/towns/.json`. + +DATA DISCIPLINE (ledger #6): deterministic (same raw → byte-identical cache; shops sorted by OSM id, +no RNG); bounded queries (a cache is ONE compact town, span < 5 km — the mega-strip risk); ODbL +attribution ships IN every cache (`license`/`attribution`) + `SOURCES.md`. Hours are NOT cached — the +registry supplies them at plan time (contract shop = {id,name,type,lat,lon,suburb}). Overpass is an +outward-facing fetch (John-authorized scout, once per town, raw cached). +""" +import os, sys, json, re, math, time, datetime, urllib.request, urllib.parse + +ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +TOWNS_DIR = os.path.join(ROOT, "web", "assets", "towns") +RAW_DIR = os.path.join(TOWNS_DIR, "_raw") +OVERPASS = "https://overpass-api.de/api/interpreter" +UA = "PROCITY-townscout/0.1 (real-map scout; OpenStreetMap ODbL data)" + +# Compact real AU towns: center (lat,lon) + query box span (km). `katoomba_real` is the fixture +# comparison (vs the hand-made `katoomba` in osm_fixture.js). Spans kept tight so each is ONE town. +TOWNS = { + "katoomba_real": {"town": "Katoomba", "center": (-33.7145, 150.3120), "span_km": 3.0}, + "newtown_real": {"town": "Newtown", "center": (-33.8985, 151.1790), "span_km": 2.6}, + "fremantle_real": {"town": "Fremantle", "center": (-32.0555, 115.7480), "span_km": 2.6}, + "bendigo_real": {"town": "Bendigo", "center": (-36.7580, 144.2810), "span_km": 3.4}, + "castlemaine_real": {"town": "Castlemaine", "center": (-37.0640, 144.2170), "span_km": 3.0}, +} + +# OSM shop tags we scout (on-theme: the game's secondhand/media/hobby world) → registry SHOP_TYPE. +SHOP_RE = "charity|second_hand|music|antiques|books|toys|games|video_games|pawnbroker" +TYPE_MAP = { + "books": "book", "music": "record", "toys": "toy", "games": "toy", + "video_games": "video", "video": "video", "pawnbroker": "pawn", + "charity": "opshop", "second_hand": "opshop", "antiques": "opshop", +} + +# The big trademarked charity/franchise chains get loving parodies (ported from thriftgod, John's call +# for the live game); independent shops keep their real OSM names (factual ODbL data). +PARODY = [ + (r"salvos( stores?)?|(the )?salvation army", "Slave-ohs"), + (r"lifeline", "Tightrope"), + (r"rspca", "RSYMCA"), + (r"vinnies|st\.? ?vincent('?s| de paul)?( society)?|svdp", "Sinnies"), + (r"(australian )?red cross", "Red Floss"), + (r"save the children", "Shave the Children"), + (r"anglicare", "Tanglecare"), + (r"uniting ?care|unitingcare|uniting", "Unknitting"), + (r"good sammys?|good samaritans?", "Okay Sammys"), + (r"mission australia", "Mission Improbable"), + (r"brotherhood of st\.? ?laurence", "Brotherhood of St Lozenge"), + (r"cash ?converters?", "Cashtheists"), +] + + +def parodize(name): + for pat, rep in PARODY: + name = re.sub(pat, rep, name, flags=re.I) + return name + + +def punct_blind(s): + return re.sub(r"[^a-z0-9]", "", s.lower()) + + +def bbox(center, span_km): + lat, lon = center + dlat = (span_km / 2.0) / 111.0 + dlon = (span_km / 2.0) / (111.0 * math.cos(math.radians(lat))) + return {"minLat": lat - dlat, "minLon": lon - dlon, "maxLat": lat + dlat, "maxLon": lon + dlon} + + +def haversine_m(a, b): + R = 6371000.0 + p1, p2 = math.radians(a[0]), math.radians(b[0]) + dp = math.radians(b[0] - a[0]); dl = math.radians(b[1] - a[1]) + h = math.sin(dp / 2) ** 2 + math.cos(p1) * math.cos(p2) * math.sin(dl / 2) ** 2 + return 2 * R * math.asin(min(1.0, math.sqrt(h))) + + +def fetch_raw(key, bb, refetch): + """Return the raw Overpass elements list, cached in-repo so re-runs never re-fetch.""" + os.makedirs(RAW_DIR, exist_ok=True) + path = os.path.join(RAW_DIR, key + ".overpass.json") + if os.path.exists(path) and not refetch: + w = json.load(open(path)) + return w["elements"], w.get("fetchedAt") + q = (f'[out:json][timeout:120];' + f'nwr["shop"~"^({SHOP_RE})$"]["name"]' + f'({bb["minLat"]:.5f},{bb["minLon"]:.5f},{bb["maxLat"]:.5f},{bb["maxLon"]:.5f});' + f'out center;') + print(f" [{key}] fetching Overpass (bounded {bb['minLat']:.3f},{bb['minLon']:.3f} … {bb['maxLat']:.3f},{bb['maxLon']:.3f})") + req = urllib.request.Request(OVERPASS, data=urllib.parse.urlencode({"data": q}).encode(), + headers={"User-Agent": UA}) + with urllib.request.urlopen(req, timeout=150) as r: + els = json.load(r).get("elements", []) + fetched = datetime.date.today().isoformat() + json.dump({"fetchedAt": fetched, "endpoint": OVERPASS, "query": q, "bbox": bb, "elements": els}, + open(path, "w"), indent=1) + time.sleep(2) # be a good Overpass citizen between towns + return els, fetched + + +def process(key, cfg, els, fetched): + center = cfg["center"] + rows, seen = [], set() + for el in els: + t = el.get("tags", {}) + name, osm_shop = t.get("name"), t.get("shop") + lat = el.get("lat", el.get("center", {}).get("lat")) + lon = el.get("lon", el.get("center", {}).get("lon")) + if not (name and osm_shop and isinstance(lat, (int, float)) and isinstance(lon, (int, float))): + continue + dkey = (punct_blind(name), round(lat, 3), round(lon, 3)) # node/way dup fuzz (~110 m) + if dkey in seen: + continue + seen.add(dkey) + suburb = next((t[k] for k in ("addr:suburb", "addr:city", "addr:town", "addr:place") if t.get(k)), "") + rows.append({"id": el["id"], "name": name[:60], "osm": osm_shop, "lat": lat, "lon": lon, "suburb": suburb}) + # suburb fill: untagged shops adopt the nearest tagged neighbour within ~3 km (that's a suburb) + tagged = [r for r in rows if r["suburb"]] + for r in rows: + if r["suburb"] or not tagged: + continue + near = min(tagged, key=lambda s: haversine_m((r["lat"], r["lon"]), (s["lat"], s["lon"]))) + if haversine_m((r["lat"], r["lon"]), (near["lat"], near["lon"])) <= 3000: + r["suburb"] = near["suburb"] + # parody + type-map + second dedupe (parodized name in same suburb = same shop); id-sorted = deterministic + shops, seen2 = [], set() + for r in sorted(rows, key=lambda r: r["id"]): + pname = parodize(r["name"])[:60] + k2 = (punct_blind(pname), r["suburb"]) + if k2 in seen2: + continue + seen2.add(k2) + shops.append({"id": r["id"], "name": pname, "type": TYPE_MAP.get(r["osm"], "opshop"), + "lat": round(r["lat"], 6), "lon": round(r["lon"], 6), "suburb": r["suburb"]}) + bb = bbox(center, cfg["span_km"]) + return { + "schema": "procity-town-cache/1", "key": key, "town": cfg["town"], "source": "osm", + "license": "ODbL 1.0", "attribution": "© OpenStreetMap contributors", + "generator": "pipeline/build_towns.py", "fetchedAt": fetched, + "center": {"lat": center[0], "lon": center[1]}, + "bbox": bb, "counts": {"raw": len(els), "shops": len(shops)}, + "shops": shops, + } + + +def span_km(shops): + if not shops: + return 0.0 + lats = [s["lat"] for s in shops]; lons = [s["lon"] for s in shops] + latm = haversine_m((min(lats), lons[0]), (max(lats), lons[0])) + lonm = haversine_m((lats[0], min(lons)), (lats[0], max(lons))) + return max(latm, lonm) / 1000.0 + + +def main(): + args = [a for a in sys.argv[1:] if not a.startswith("-")] + refetch = "--refetch" in sys.argv + keys = args or list(TOWNS) + os.makedirs(TOWNS_DIR, exist_ok=True) + kept, dropped = [], [] + for key in keys: + cfg = TOWNS[key] + els, fetched = fetch_raw(key, bbox(cfg["center"], cfg["span_km"]), refetch) + cache = process(key, cfg, els, fetched) + n, sp = len(cache["shops"]), span_km(cache["shops"]) + types = {} + for s in cache["shops"]: + types[s["type"]] = types.get(s["type"], 0) + 1 + status = "OK " if n >= 6 and sp < 5.0 else "LOW" + if n >= 6 and sp < 5.0: + json.dump(cache, open(os.path.join(TOWNS_DIR, key + ".json"), "w"), indent=1) + kept.append(key) + else: + dropped.append((key, n, sp)) + print(f" {status} {key:16} raw={cache['counts']['raw']:>4} shops={n:>3} span={sp:.2f}km {dict(sorted(types.items()))}") + print(f"\nBUILT {len(kept)} town cache(s) → web/assets/towns/: {', '.join(kept)}") + if dropped: + print(f"DROPPED (< 6 shops or span ≥ 5 km): {dropped}") + + +if __name__ == "__main__": + main() diff --git a/pipeline/publish.py b/pipeline/publish.py index 15ef220..e34920d 100644 --- a/pipeline/publish.py +++ b/pipeline/publish.py @@ -126,32 +126,40 @@ def main(): def drift_check(record): - """After publishing, compare _published.json against the live depot procity_* list; warn on - drift (a published file missing from the record, or a recorded file absent from the depot).""" + """Compare _published.json (the published-name RECORD) against the depot's actual file list. + A recorded name absent from the depot is real drift (deleted / never landed); a `procity_` file + live on the depot but unrecorded is a provenance gap. R17 ledger #4: the old check filtered the + depot list to `procity_*` and so false-flagged genuinely-published non-`procity_` assets + (sit.glb, the reused vintage-cash-register) as "not live" — now every published name is confirmed + by the name list itself, and only OTHER projects' non-`procity_` files on the shared depot are + ignored (they're expected, not ours).""" try: - live = fetch_depot_procity() + live = fetch_depot_files() except Exception as e: print(f" (drift check skipped — depot list unreachable: {e})") return rec = set(record) - missing_from_record = live - rec # live on depot but not recorded → record is stale - missing_from_depot = rec - live # recorded but not live → deleted/never landed - if missing_from_record: - print(f" DRIFT: {len(missing_from_record)} on depot not in _published.json: " - f"{sorted(missing_from_record)[:5]}") + missing_from_depot = rec - live # recorded but not live → real drift + unrecorded = {f for f in live if f.startswith("procity_")} - rec # our namespace live but unrecorded + if unrecorded: + print(f" DRIFT: {len(unrecorded)} procity_ on depot not in _published.json: " + f"{sorted(unrecorded)[:5]}") if missing_from_depot: print(f" DRIFT: {len(missing_from_depot)} in _published.json not live on depot: " f"{sorted(missing_from_depot)[:5]}") - if not missing_from_record and not missing_from_depot: - print(f" ✓ _published.json matches the live depot ({len(live)} procity assets).") + if not unrecorded and not missing_from_depot: + print(f" ✓ _published.json matches the live depot (all {len(rec)} recorded assets live).") -def fetch_depot_procity(): +def fetch_depot_files(): + """EVERY file name on the depot (not prefix-filtered), so the drift check confirms any published + name — including non-`procity_` clips/reuses — is live by the name itself, not a prefix heuristic.""" import urllib.request as _u - with _u.urlopen(DEPOT + "/api/list", timeout=15) as r: + req = _u.Request(DEPOT + "/api/list", headers={"User-Agent": "procity-validator/1.0"}) + with _u.urlopen(req, timeout=15) as r: d = json.loads(r.read().decode("utf-8")) assets = d.get("assets", d) if isinstance(d, dict) else d - return {a["file"] for a in assets if str(a.get("file", "")).startswith("procity_")} + return {a["file"] for a in assets if a.get("file")} if __name__ == "__main__": diff --git a/web/assets/towns/SOURCES.md b/web/assets/towns/SOURCES.md new file mode 100644 index 0000000..8625393 --- /dev/null +++ b/web/assets/towns/SOURCES.md @@ -0,0 +1,40 @@ +# Town-cache data sources & licence (`web/assets/towns/`) + +**Lane E owns this file** (ROUND17 ledger #6). Every real-town cache here is derived from +**OpenStreetMap** data and ships under OSM's licence. This notice + the `license`/`attribution` +fields on every `.json` are the attribution that travels with the data. + +## Licence — OpenStreetMap, ODbL 1.0 + +> © **OpenStreetMap contributors**. Data available under the **Open Database License (ODbL) 1.0**. +> · + +The caches are a **produced work** derived from OSM via the Overpass API (`overpass-api.de`), filtered +to on-theme `shop=*` points, deduped, suburb-filled, and type-mapped by `pipeline/build_towns.py`. +Attribution ships in-repo (this file) and in every cache (`"license":"ODbL 1.0"`, +`"attribution":"© OpenStreetMap contributors"`). If a rendered/public build surfaces this data, keep +the "© OpenStreetMap contributors (ODbL)" credit visible. + +## Provenance table + +| cache | town | source | licence | fetched | shops | raw response | +|---|---|---|---|---|---|---| +| `katoomba_real.json` | Katoomba, NSW | OSM Overpass (bounded) | ODbL 1.0 | 2026-07-16 | 20 | `_raw/katoomba_real.overpass.json` | +| `newtown_real.json` | Newtown, Sydney NSW | OSM Overpass (bounded) | ODbL 1.0 | 2026-07-16 | 18 | `_raw/newtown_real.overpass.json` | +| `fremantle_real.json` | Fremantle, WA | OSM Overpass (bounded) | ODbL 1.0 | 2026-07-16 | 21 | `_raw/fremantle_real.overpass.json` | +| `bendigo_real.json` | Bendigo, VIC | OSM Overpass (bounded) | ODbL 1.0 | 2026-07-16 | 9 | `_raw/bendigo_real.overpass.json` | +| `castlemaine_real.json` | Castlemaine, VIC | OSM Overpass (bounded) | ODbL 1.0 | 2026-07-16 | 6 | `_raw/castlemaine_real.overpass.json` | + +## Raw responses (`_raw/`) + +The raw Overpass JSON for each town is cached in `_raw/.overpass.json` (wrapped with `fetchedAt`, +`endpoint`, `query`, `bbox`). **Re-runs reuse the raw cache and never re-hit Overpass** — the fetch is +outward-facing network (John-authorized scout, once per town). `--refetch` forces a deliberate re-fetch. +`selfcheck.js` reads only top-level `*.json`, so `_raw/` is never loaded as a cache. + +## Names — parody vs factual + +Big **trademarked charity/franchise chains** are parodied in `build_towns.py` (Salvos→Slave-ohs, +Vinnies→Sinnies, Anglicare→Tanglecare, Cash Converters→Cashtheists, …) for the game's comic tone. +**Independent shops keep their real OSM name** — factual, publicly-mapped POI data under ODbL. No +private-individual data is carried (name/type/coordinate/suburb only). diff --git a/web/assets/towns/_raw/bendigo_real.overpass.json b/web/assets/towns/_raw/bendigo_real.overpass.json new file mode 100644 index 0000000..54093e7 --- /dev/null +++ b/web/assets/towns/_raw/bendigo_real.overpass.json @@ -0,0 +1,218 @@ +{ + "fetchedAt": "2026-07-16", + "endpoint": "https://overpass-api.de/api/interpreter", + "query": "[out:json][timeout:120];nwr[\"shop\"~\"^(charity|second_hand|music|antiques|books|toys|games|video_games|pawnbroker)$\"][\"name\"](-36.77332,144.26188,-36.74268,144.30012);out center;", + "bbox": { + "minLat": -36.773315315315315, + "minLon": 144.26188381955436, + "maxLat": -36.74268468468469, + "maxLon": 144.30011618044566 + }, + "elements": [ + { + "type": "node", + "id": 7816868915, + "lat": -36.7610632, + "lon": 144.2820458, + "tags": { + "addr:country": "AU", + "addr:housenumber": "293", + "addr:postcode": "3550", + "addr:state": "VIC", + "addr:street": "Lyttleton Terrace", + "addr:suburb": "Bendigo", + "name": "Australian Red Cross Shop", + "shop": "second_hand", + "website": "https://www.redcross.org.au/", + "wheelchair": "yes" + } + }, + { + "type": "node", + "id": 8040967386, + "lat": -36.7602319, + "lon": 144.2786923, + "tags": { + "brand": "Dymocks", + "brand:wikidata": "Q1280883", + "name": "Dymocks", + "shop": "books", + "wheelchair": "yes" + } + }, + { + "type": "node", + "id": 8067036585, + "lat": -36.7605894, + "lon": 144.2761859, + "tags": { + "addr:postcode": "3550", + "addr:street": "High Street", + "name": "Bookmark", + "opening_hours": "Mo-Su 10:00-17:00", + "shop": "books" + } + }, + { + "type": "node", + "id": 8225415317, + "lat": -36.7589648, + "lon": 144.2774687, + "tags": { + "addr:housenumber": "33", + "addr:postcode": "3550", + "addr:street": "View Street", + "name": "Bat City Music", + "opening_hours": "Mo-Fr 10:00-17:30; Sa 10:00-14:00; PH,Su off", + "phone": "+61 3 5442 3293", + "shop": "music", + "website": "http://batcitymusic.com.au" + } + }, + { + "type": "node", + "id": 8649203082, + "lat": -36.7656924, + "lon": 144.2808062, + "tags": { + "brand": "EB Games", + "brand:wikidata": "Q5322604", + "brand:wikipedia": "en:EB Games", + "name": "EB Games", + "shop": "video_games" + } + }, + { + "type": "node", + "id": 12421060314, + "lat": -36.7593735, + "lon": 144.2792703, + "tags": { + "addr:housenumber": "18-20", + "addr:postcode": "3550", + "addr:state": "VIC", + "addr:street": "Pall Mall", + "addr:suburb": "Bendigo", + "branch": "Bendigo", + "brand": "Cash Converters", + "brand:wikidata": "Q5048645", + "email": "BendigoVIC@cashconverters.com", + "name": "Cash Converters", + "phone": "+61 3 5444 4260", + "shop": "pawnbroker", + "website": "https://www.cashconverters.com.au/store-locator/bendigovic" + } + }, + { + "type": "way", + "id": 329659785, + "center": { + "lat": -36.7557918, + "lon": 144.2629357 + }, + "nodes": [ + 3365775841, + 3365775839, + 13944869958, + 3365775842, + 3365775836, + 9391007061, + 9391007062, + 13944642149, + 13944642156, + 13944642151, + 13944642155, + 13944642150, + 13944642154, + 13944642152, + 13944642153, + 13944642148, + 9391007063, + 9391007060, + 3365775849, + 3365775841 + ], + "tags": { + "addr:housenumber": "2-6", + "addr:postcode": "3550", + "addr:state": "VIC", + "addr:street": "Marong Road", + "addr:suburb": "Ironbark", + "branch": "Eaglehawk", + "brand": "Salvos", + "brand:wikidata": "Q120646407", + "building": "retail", + "name": "Salvos", + "operator": "The Salvation Army Australia", + "phone": "+61 3 5446 7928", + "second_hand": "yes", + "shop": "charity" + } + }, + { + "type": "way", + "id": 827550052, + "center": { + "lat": -36.7575264, + "lon": 144.2860172 + }, + "nodes": [ + 7725901667, + 7733874087, + 7725901660, + 7725901663, + 7725901659, + 7725901662, + 7725901661, + 7725901665, + 7725901666, + 7725901667 + ], + "tags": { + "addr:country": "AU", + "addr:housenumber": "31", + "addr:postcode": "3550", + "addr:state": "VIC", + "addr:street": "Chapel Street", + "addr:suburb": "Bendigo", + "branch": "Bendigo", + "brand": "Vinnies", + "brand:wikidata": "Q120646672", + "building": "yes", + "name": "Vinnies", + "opening_hours": "Mo-Sa 09:00-17:00; Su 10:00-16:00", + "phone": "+61 3 5443 5052", + "shop": "charity", + "website": "https://www.vinnies.org.au/vic/shops/vinnies-bendigo/" + } + }, + { + "type": "way", + "id": 1443367904, + "center": { + "lat": -36.7562608, + "lon": 144.282307 + }, + "nodes": [ + 13244868730, + 13244868731, + 13244868732, + 13244868733, + 13244868730 + ], + "tags": { + "addr:housenumber": "1", + "addr:postcode": "3550", + "addr:state": "VIC", + "addr:street": "Farmer Lane", + "addr:suburb": "Bendigo", + "building": "commercial", + "name": "Book Now", + "opening_hours": "We-Su 11:00-17:00", + "phone": "+61 3 5443 8587", + "shop": "books", + "website": "http://www.booknow.net.au/" + } + } + ] +} \ No newline at end of file diff --git a/web/assets/towns/_raw/castlemaine_real.overpass.json b/web/assets/towns/_raw/castlemaine_real.overpass.json new file mode 100644 index 0000000..1acb2c7 --- /dev/null +++ b/web/assets/towns/_raw/castlemaine_real.overpass.json @@ -0,0 +1,99 @@ +{ + "fetchedAt": "2026-07-16", + "endpoint": "https://overpass-api.de/api/interpreter", + "query": "[out:json][timeout:120];nwr[\"shop\"~\"^(charity|second_hand|music|antiques|books|toys|games|video_games|pawnbroker)$\"][\"name\"](-37.07751,144.20006,-37.05049,144.23394);out center;", + "bbox": { + "minLat": -37.077513513513516, + "minLon": 144.2000649826232, + "maxLat": -37.050486486486484, + "maxLon": 144.23393501737684 + }, + "elements": [ + { + "type": "node", + "id": 9231704552, + "lat": -37.0623631, + "lon": 144.2175259, + "tags": { + "addr:housenumber": "244", + "addr:street": "Barker Street", + "name": "Vinnies", + "opening_hours": "Tu-Su 10:00-16:00; Mo closed", + "phone": "+61 3 5472 4419", + "shop": "charity", + "website": "https://www.vinnies.org.au/shops/view/360" + } + }, + { + "type": "node", + "id": 11699200337, + "lat": -37.0632013, + "lon": 144.2181634, + "tags": { + "name": "Barrow Boy Traders", + "shop": "antiques" + } + }, + { + "type": "node", + "id": 11699208641, + "lat": -37.0616469, + "lon": 144.2172472, + "tags": { + "name": "Mount of Alex Bookshop", + "shop": "books" + } + }, + { + "type": "node", + "id": 11699237531, + "lat": -37.0729118, + "lon": 144.2064928, + "tags": { + "name": "XXXX Antiques", + "shop": "antiques" + } + }, + { + "type": "node", + "id": 11760538035, + "lat": -37.067129, + "lon": 144.2176252, + "tags": { + "addr:housenumber": "46", + "addr:street": "Forest Street", + "alt_name": "Castlemaine Thrift Shop", + "name": "Salvos", + "opening_hours": "Mo-Sa 10:00-16:00; Su off", + "operator": "The Salvation Army", + "operator:wikidata": "Q188307", + "phone": "+61 3 5470 5636", + "shop": "charity", + "website": "https://www.salvosstores.com.au/stores/vic/3450-castlemaine-5317" + } + }, + { + "type": "way", + "id": 315282080, + "center": { + "lat": -37.0657039, + "lon": 144.2196734 + }, + "nodes": [ + 3213928211, + 3213928212, + 3213928213, + 3213928216, + 3213928214, + 3213928215, + 3213928211 + ], + "tags": { + "building": "yes", + "name": "Stonemans Bookroom", + "shop": "books", + "wheelchair": "limited" + } + } + ] +} \ No newline at end of file diff --git a/web/assets/towns/_raw/fremantle_real.overpass.json b/web/assets/towns/_raw/fremantle_real.overpass.json new file mode 100644 index 0000000..6bd472b --- /dev/null +++ b/web/assets/towns/_raw/fremantle_real.overpass.json @@ -0,0 +1,354 @@ +{ + "fetchedAt": "2026-07-16", + "endpoint": "https://overpass-api.de/api/interpreter", + "query": "[out:json][timeout:120];nwr[\"shop\"~\"^(charity|second_hand|music|antiques|books|toys|games|video_games|pawnbroker)$\"][\"name\"](-32.06721,115.73418,-32.04379,115.76182);out center;", + "bbox": { + "minLat": -32.067211711711714, + "minLon": 115.73418143183595, + "maxLat": -32.04378828828829, + "maxLon": 115.76181856816406 + }, + "elements": [ + { + "type": "node", + "id": 3144618368, + "lat": -32.0553704, + "lon": 115.7471916, + "tags": { + "check_date": "2025-09-20", + "name": "Elizabeth's", + "opening_hours": "Mo-Fr 10:00-17:00, Sa-Su 09:00-17:00", + "second_hand": "yes", + "shop": "books", + "website": "https://www.elizabethsbookshop.com.au/", + "wheelchair": "no" + } + }, + { + "type": "node", + "id": 4601037692, + "lat": -32.0552806, + "lon": 115.744283, + "tags": { + "addr:housenumber": "41", + "addr:street": "High Street", + "name": "New Edition Bookshop", + "opening_hours": "Mo-Su 09:00-21:00", + "phone": "+61 8 9335 2383", + "shop": "books", + "website": "https://newedition.com.au/" + } + }, + { + "type": "node", + "id": 4601057991, + "lat": -32.0550425, + "lon": 115.7444055, + "tags": { + "addr:housenumber": "48", + "addr:street": "High Street", + "check_date": "2025-01-04", + "name": "Bill Campbell Books", + "opening_hours": "Mo,We-Fr 10:00-17:00; Tu off; Sa-Su 12:00-17:00", + "shop": "books", + "survey:date": "2024-01-20", + "wheelchair": "limited" + } + }, + { + "type": "node", + "id": 5026468221, + "lat": -32.055583, + "lon": 115.7478433, + "tags": { + "addr:housenumber": "36", + "addr:street": "South Terrace", + "name": "Mothballs Vintage", + "shop": "antiques" + } + }, + { + "type": "node", + "id": 5885985088, + "lat": -32.0550452, + "lon": 115.7487872, + "tags": { + "brand": "Cash Converters", + "brand:wikidata": "Q5048645", + "name": "Cash Converters", + "shop": "pawnbroker" + } + }, + { + "type": "node", + "id": 8153813968, + "lat": -32.0552357, + "lon": 115.7443688, + "tags": { + "addr:housenumber": "43", + "addr:street": "High Street", + "name": "The Record Finder", + "shop": "music", + "survey:date": "2022-07-10" + } + }, + { + "type": "node", + "id": 9156620320, + "lat": -32.0656871, + "lon": 115.7525895, + "tags": { + "name": "Save the Children", + "shop": "charity" + } + }, + { + "type": "node", + "id": 9794077228, + "lat": -32.065416, + "lon": 115.7525681, + "tags": { + "name": "Adorn Vintage", + "shop": "charity" + } + }, + { + "type": "node", + "id": 9795944064, + "lat": -32.056204, + "lon": 115.7447268, + "tags": { + "check_date": "2022-06-05", + "name": "Paper Bird", + "shop": "books" + } + }, + { + "type": "node", + "id": 10585254716, + "lat": -32.0533284, + "lon": 115.7496691, + "tags": { + "brand": "Vinnies", + "brand:wikidata": "Q120646672", + "check_date": "2025-09-28", + "name": "Vinnies", + "second_hand": "only", + "shop": "charity" + } + }, + { + "type": "node", + "id": 11323015046, + "lat": -32.0556114, + "lon": 115.7483058, + "tags": { + "name": "The Picker Concept", + "shop": "antiques" + } + }, + { + "type": "node", + "id": 11337031591, + "lat": -32.05392, + "lon": 115.7498109, + "tags": { + "name": "Nifty in Freo", + "shop": "second_hand" + } + }, + { + "type": "node", + "id": 11337040744, + "lat": -32.0539367, + "lon": 115.7460896, + "tags": { + "check_date": "2024-11-01", + "name": "Australian Red Cross Shop", + "shop": "second_hand" + } + }, + { + "type": "node", + "id": 12389445377, + "lat": -32.0514063, + "lon": 115.7494672, + "tags": { + "check_date": "2024-12-02", + "name": "St Pats", + "opening_hours": "Mo-Fr 09:00-16:00; Sa-Su off", + "shop": "charity", + "wheelchair": "yes" + } + }, + { + "type": "node", + "id": 12490289378, + "lat": -32.0522157, + "lon": 115.7492735, + "tags": { + "addr:housenumber": "17", + "addr:street": "Point Street", + "brand": "Warhammer", + "brand:wikidata": "Q587270", + "check_date": "2026-04-19", + "name": "Warhammer", + "shop": "games" + } + }, + { + "type": "node", + "id": 12787392414, + "lat": -32.0549882, + "lon": 115.74523, + "tags": { + "addr:housenumber": "61", + "addr:street": "High Street", + "check_date": "2025-04-26", + "name": "Vintage On High", + "name:en": "Ginger Pudding Vintage", + "opening_hours": "Tu-Su 10:00-16:00", + "shop": "second_hand", + "website": "https://www.gingerpuddingvintage.com.au/", + "wheelchair": "yes" + } + }, + { + "type": "node", + "id": 12878567802, + "lat": -32.0533663, + "lon": 115.7472611, + "tags": { + "check_date": "2025-06-01", + "name": "Atomic Alley", + "opening_hours": "We-Su 10:00-18:00", + "shop": "toys" + } + }, + { + "type": "way", + "id": 52013903, + "center": { + "lat": -32.0554336, + "lon": 115.7477488 + }, + "nodes": [ + 663126377, + 9470885315, + 663126379, + 3144618388, + 3144618389, + 663126377 + ], + "tags": { + "addr:postcode": "6160", + "addr:state": "WA", + "addr:street": "South Terrace", + "addr:suburb": "Fremantle", + "brand": "Timezone", + "brand:wikidata": "Q12521502", + "brand:wikipedia": "en:Timezone (video arcades)", + "building": "yes", + "check_date": "2026-05-03", + "check_date:opening_hours": "2023-06-17", + "leisure": "amusement_arcade", + "name": "Timezone", + "opening_hours": "Mo-Th 10:00-22:00; Fr 10:00-23:30; Sa 09:00-23:30; Su 09:00-22:00", + "payment:cash": "yes", + "payment:credit_cards": "yes", + "payment:debit_cards": "yes", + "phone": "+61 8 7701 9715", + "shop": "video_games", + "website": "https://www.timezonegames.com/en-au/locations/timezone-fremantle", + "wheelchair": "yes" + } + }, + { + "type": "way", + "id": 310008675, + "center": { + "lat": -32.056734, + "lon": 115.7484504 + }, + "nodes": [ + 3153646561, + 12088756234, + 12088756233, + 12088756235, + 12088756232, + 12088756236, + 3153646569, + 3153646567, + 3153646562, + 12088125693, + 3153646561 + ], + "tags": { + "addr:housenumber": "21", + "addr:street": "Essex Street", + "brand": "Salvos", + "brand:wikidata": "Q120646407", + "building": "retail", + "check_date": "2026-05-09", + "name": "Salvos", + "shop": "charity" + } + }, + { + "type": "way", + "id": 592578660, + "center": { + "lat": -32.0486758, + "lon": 115.7509014 + }, + "nodes": [ + 5655427730, + 5655427726, + 5655427729, + 5655427728, + 5655427727, + 5655427730 + ], + "tags": { + "brand": "Elizabeth's Bookstores", + "building": "yes", + "check_date": "2024-03-07", + "name": "Elizabeth's Warehouse", + "second_hand": "yes", + "shop": "books", + "wheelchair": "limited" + } + }, + { + "type": "way", + "id": 976912099, + "center": { + "lat": -32.0494787, + "lon": 115.7495694 + }, + "nodes": [ + 9040165737, + 9040165734, + 9040165733, + 9040165731, + 9040165729, + 9040165727, + 9040165726, + 9040165728, + 9040165730, + 9040165732, + 9040165735, + 9040165736, + 9040165737 + ], + "tags": { + "brand": "Toyworld", + "brand:wikidata": "Q95923071", + "building": "yes", + "name": "Toyworld", + "shop": "toys", + "wikidata": "Q130749344" + } + } + ] +} \ No newline at end of file diff --git a/web/assets/towns/_raw/katoomba_real.overpass.json b/web/assets/towns/_raw/katoomba_real.overpass.json new file mode 100644 index 0000000..2121492 --- /dev/null +++ b/web/assets/towns/_raw/katoomba_real.overpass.json @@ -0,0 +1,299 @@ +{ + "fetchedAt": "2026-07-16", + "endpoint": "https://overpass-api.de/api/interpreter", + "query": "[out:json][timeout:120];nwr[\"shop\"~\"^(charity|second_hand|music|antiques|books|toys|games|video_games|pawnbroker)$\"][\"name\"](-33.72801,150.29575,-33.70099,150.32825);out center;", + "bbox": { + "minLat": -33.72801351351352, + "minLon": 150.29575415811652, + "maxLat": -33.700986486486485, + "maxLon": 150.3282458418835 + }, + "elements": [ + { + "type": "node", + "id": 4047416904, + "lat": -33.7167381, + "lon": 150.3109194, + "tags": { + "check_date": "2023-07-16", + "name": "Anglicare", + "opening_hours": "Mo-Fr 09:00-04:45; Sa 10:00-03:45", + "shop": "charity", + "wheelchair": "yes" + } + }, + { + "type": "node", + "id": 4047416906, + "lat": -33.7174767, + "lon": 150.3118602, + "tags": { + "check_date": "2024-08-25", + "name": "Cat Defence Op Shop", + "opening_hours:signed": "no", + "shop": "charity", + "wheelchair": "yes" + } + }, + { + "type": "node", + "id": 4047416910, + "lat": -33.7172906, + "lon": 150.3095268, + "tags": { + "name": "Red Shield Family Store", + "opening_hours": "Mo-Fr 09:00-16:00", + "shop": "charity" + } + }, + { + "type": "node", + "id": 4047416911, + "lat": -33.7109342, + "lon": 150.3099305, + "tags": { + "name": "Steven Walter", + "shop": "charity" + } + }, + { + "type": "node", + "id": 4047416913, + "lat": -33.7174637, + "lon": 150.3117818, + "tags": { + "branch": "Vinnies", + "brand": "Vinnies", + "brand:wikidata": "Q120646672", + "check_date": "2025-07-16", + "name": "Vinnies", + "opening_hours": "Mo-Th 09:00-16:30; Fr 09:00-16:00; Sa 10:00-15:00", + "payment:cash": "yes", + "payment:credit_cards": "yes", + "payment:debit_cards": "yes", + "second_hand": "yes", + "shop": "charity", + "wheelchair": "no" + } + }, + { + "type": "node", + "id": 4958417278, + "lat": -33.7149492, + "lon": 150.3112571, + "tags": { + "addr:housenumber": "103", + "addr:street": "Katoomba Street", + "check_date": "2025-07-16", + "name": "Anglicare Op Shops", + "second_hand": "only", + "shop": "charity", + "wheelchair": "yes" + } + }, + { + "type": "node", + "id": 4958417724, + "lat": -33.7150739, + "lon": 150.3115321, + "tags": { + "addr:housenumber": "140", + "addr:street": "Katoomba Street", + "check_date": "2025-07-16", + "name": "Katoomba Music", + "opening_hours": "Tu,We,Fr 10:00-17:00; Th 10:00-18:00; Sa 10:00-16:00", + "shop": "music", + "wheelchair": "limited" + } + }, + { + "type": "node", + "id": 5237464912, + "lat": -33.7137293, + "lon": 150.3114025, + "tags": { + "addr:housenumber": "59-61", + "addr:street": "Katoomba Street", + "name": "Katoomba Vintage Emporium", + "opening_hours": "Mo-Su 10:00-18:00", + "phone": "+61 2 4782 6996", + "shop": "antiques", + "wheelchair": "yes" + } + }, + { + "type": "node", + "id": 6938379064, + "lat": -33.7122056, + "lon": 150.3106541, + "tags": { + "name": "Katoomba Antique Centre", + "shop": "antiques" + } + }, + { + "type": "node", + "id": 6938414604, + "lat": -33.7146541, + "lon": 150.3113164, + "tags": { + "addr:housenumber": "89", + "addr:postcode": "2780", + "addr:state": "NSW", + "addr:street": "Katoomba Street", + "addr:suburb": "Katoomba", + "brand": "Salvos", + "brand:wikidata": "Q120646407", + "check_date": "2025-07-16", + "name": "Salvos", + "second_hand": "only", + "shop": "charity", + "wheelchair": "limited" + } + }, + { + "type": "node", + "id": 6938414612, + "lat": -33.7148514, + "lon": 150.3112704, + "tags": { + "brand": "EB Games", + "brand:wikidata": "Q5322604", + "brand:wikipedia": "en:EB Games", + "name": "EB Games", + "opening_hours": "Mo-Sa 09:00-17:30", + "shop": "video_games", + "wheelchair": "no" + } + }, + { + "type": "node", + "id": 6938921533, + "lat": -33.7138899, + "lon": 150.3117659, + "tags": { + "addr:housenumber": "84", + "addr:street": "Katoomba Street", + "name": "MacArthur's Arcade", + "opening_hours": "Mo-Su 10:00-17:00", + "shop": "antiques", + "wheelchair": "no" + } + }, + { + "type": "node", + "id": 6938921534, + "lat": -33.7139151, + "lon": 150.3117612, + "tags": { + "name": "Mr Pickwick's Bookshop", + "opening_hours": "Mo-Su 10:00-17:00", + "shop": "books", + "wheelchair": "no" + } + }, + { + "type": "node", + "id": 7040012373, + "lat": -33.7098056, + "lon": 150.3092784, + "tags": { + "name": "Black Antiques", + "shop": "antiques" + } + }, + { + "type": "node", + "id": 7110813637, + "lat": -33.7128108, + "lon": 150.321286, + "tags": { + "addr:housenumber": "58", + "addr:postcode": "2780", + "addr:state": "NSW", + "addr:street": "Megalong Street", + "addr:suburb": "Katoomba", + "check_date:opening_hours": "2025-03-24", + "flickr": "https://www.flickr.com/photos/190060616@N03/50521764803", + "name": "Steven Walter Children's Cancer Foundation Warehouse", + "opening_hours": "We-Sa 10:00-15:00", + "second_hand": "yes", + "shop": "charity" + } + }, + { + "type": "node", + "id": 9231634025, + "lat": -33.7163933, + "lon": 150.3108347, + "tags": { + "check_date:opening_hours": "2023-10-06", + "name": "Little Lost Bookshop", + "opening_hours:signed": "no", + "shop": "books" + } + }, + { + "type": "node", + "id": 9577859712, + "lat": -33.7142947, + "lon": 150.311173, + "tags": { + "level": "0", + "name": "B'COS Agora Second Hand Bookshop", + "opening_hours": "Mo-Su 10:00-16:00", + "operator": "Blackheath Community Op Shops", + "shop": "books" + } + }, + { + "type": "node", + "id": 9791871398, + "lat": -33.7125441, + "lon": 150.3115655, + "tags": { + "check_date:opening_hours": "2023-11-08", + "name": "The Velvet Fog", + "opening_hours": "Tu-Sa 10:00-17:00", + "shop": "music" + } + }, + { + "type": "node", + "id": 11242974232, + "lat": -33.7157176, + "lon": 150.31095, + "tags": { + "name": "Sanctus Catholic Books and Gift Shop", + "opening_hours": "Mo-Fr 10:00-17:00; Sa 10:00-14:00", + "shop": "books" + } + }, + { + "type": "way", + "id": 753467867, + "center": { + "lat": -33.7075666, + "lon": 150.310861 + }, + "nodes": [ + 7040016210, + 7040016211, + 7040016212, + 7040016213, + 7040016210 + ], + "tags": { + "addr:housenumber": "25", + "addr:postcode": "2780", + "addr:state": "NSW", + "addr:street": "Whitton Street", + "addr:suburb": "Katoomba", + "building": "yes", + "building:levels": "1", + "name": "Blue Mountains Cancer Help Op Shop", + "shop": "charity" + } + } + ] +} \ No newline at end of file diff --git a/web/assets/towns/_raw/newtown_real.overpass.json b/web/assets/towns/_raw/newtown_real.overpass.json new file mode 100644 index 0000000..40af0ff --- /dev/null +++ b/web/assets/towns/_raw/newtown_real.overpass.json @@ -0,0 +1,338 @@ +{ + "fetchedAt": "2026-07-16", + "endpoint": "https://overpass-api.de/api/interpreter", + "query": "[out:json][timeout:120];nwr[\"shop\"~\"^(charity|second_hand|music|antiques|books|toys|games|video_games|pawnbroker)$\"][\"name\"](-33.91021,151.16489,-33.88679,151.19311);out center;", + "bbox": { + "minLat": -33.91021171171171, + "minLon": 151.1648899610343, + "maxLat": -33.88678828828829, + "maxLon": 151.1931100389657 + }, + "elements": [ + { + "type": "node", + "id": 2365230295, + "lat": -33.897238, + "lon": 151.179525, + "tags": { + "addr:housenumber": "3", + "addr:postcode": "2042", + "addr:street": "Wilson Street", + "addr:suburb": "Newtown", + "name": "Egg Records", + "opening_hours": "Mo-Su 11:00-17:00", + "shop": "music", + "wheelchair": "no" + } + }, + { + "type": "node", + "id": 3278485277, + "lat": -33.8875182, + "lon": 151.1707861, + "tags": { + "addr:housenumber": "31\u0430", + "addr:postcode": "2038", + "addr:street": "Parramatta Road", + "addr:suburb": "Annandale", + "check_date:opening_hours": "2026-02-18", + "name": "The Vintage Record", + "opening_hours": "Th-Su 11:00-17:00", + "shop": "music", + "wheelchair": "no" + } + }, + { + "type": "node", + "id": 3278488658, + "lat": -33.8877879, + "lon": 151.1713949, + "tags": { + "addr:housenumber": "18", + "addr:postcode": "2048", + "addr:street": "Parramatta Road", + "addr:suburb": "Stanmore", + "name": "Aalders", + "opening_hours": "Mo-Fr 10:00-16:00 \"by appointment\"", + "shop": "antiques", + "wheelchair": "no" + } + }, + { + "type": "node", + "id": 5931284422, + "lat": -33.9035515, + "lon": 151.1797675, + "tags": { + "check_date:opening_hours": "2025-07-27", + "name": "Towers Antiques", + "opening_hours:signed": "no", + "shop": "antiques", + "wheelchair": "no" + } + }, + { + "type": "node", + "id": 5931284426, + "lat": -33.9032933, + "lon": 151.1796014, + "tags": { + "check_date:opening_hours": "2025-07-17", + "name": "Old Wares", + "opening_hours:signed": "no", + "shop": "antiques", + "wheelchair": "no" + } + }, + { + "type": "node", + "id": 6204412995, + "lat": -33.9029178, + "lon": 151.1795869, + "tags": { + "name": "Goulds Books", + "opening_hours": "We-Mo 10:30-18:00", + "shop": "books", + "wheelchair": "yes" + } + }, + { + "type": "node", + "id": 6901192228, + "lat": -33.890884, + "lon": 151.1920277, + "tags": { + "name": "Co-op", + "shop": "books" + } + }, + { + "type": "node", + "id": 9518337417, + "lat": -33.8878728, + "lon": 151.1664969, + "tags": { + "addr:housenumber": "141", + "addr:street": "Parramatta Road", + "check_date:opening_hours": "2025-03-13", + "name": "Downtown Music", + "opening_hours": "Mo-Th 10:00-17:00; Fr-Su 10:00-16:00", + "phone": "+61 2 9569 2744", + "shop": "music", + "website": "https://downtownmusicsydney.com.au/", + "wheelchair": "no" + } + }, + { + "type": "node", + "id": 9888616491, + "lat": -33.8925979, + "lon": 151.1868158, + "tags": { + "name": "Haze Guitars", + "operator": "26 King St, Newtown NSW 2042", + "shop": "music" + } + }, + { + "type": "node", + "id": 11984912424, + "lat": -33.9011361, + "lon": 151.178408, + "tags": { + "name": "Thats So Mi", + "opening_hours": "We-Fr 11:00-16:00; Sa,Su 10:00-17:00; Mo,Tu off", + "shop": "antiques", + "wheelchair": "no" + } + }, + { + "type": "node", + "id": 12958032466, + "lat": -33.898898, + "lon": 151.1702724, + "tags": { + "name": "St Luke\u2019s Op Shop", + "shop": "charity" + } + }, + { + "type": "node", + "id": 13122105023, + "lat": -33.8869187, + "lon": 151.1767126, + "tags": { + "brand": "Savers", + "brand:wikidata": "Q7428188", + "check_date": "2026-02-18", + "check_date:opening_hours": "2026-02-18", + "name": "Savers", + "opening_hours": "Mo-We,Fr 09:00-19:00; Th 09:00-21:00; Sa 09:00-18:00; Su 10:00-18:00", + "shop": "second_hand" + } + }, + { + "type": "node", + "id": 13686752819, + "lat": -33.8961303, + "lon": 151.180366, + "tags": { + "email": "books@betterread.com.au", + "fax": "+61 2 9557 8560", + "name": "Better Read Than Dead", + "opening_hours": "Mo-Sa 09:30-21:00; Su 10:00-18:00", + "phone": "+61 2 9557 8700", + "shop": "books", + "website": "https://www.betterread.com.au/" + } + }, + { + "type": "node", + "id": 13686752821, + "lat": -33.8959321, + "lon": 151.1807185, + "tags": { + "addr:housenumber": "257", + "addr:postcode": "2042", + "addr:state": "NSW", + "addr:street": "King Street", + "addr:suburb": "Newtown", + "branch": "Newtown", + "name": "Elizabeth's Bookshop", + "opening_hours": "Mo-Fr 10:00-22:30; Sa-Su 09:30-22:30", + "phone": "+61 2 9550 5691", + "shop": "books", + "website": "https://www.elizabethsbookshop.com.au/" + } + }, + { + "type": "node", + "id": 13707141472, + "lat": -33.8986661, + "lon": 151.1742068, + "tags": { + "addr:housenumber": "109", + "addr:street": "Enmore Road", + "contact:instagram": "papadisquo.records", + "name": "Papa Disquo", + "shop": "music" + } + }, + { + "type": "way", + "id": 291725580, + "center": { + "lat": -33.8869917, + "lon": 151.1768855 + }, + "nodes": [ + 2952129488, + 2952129489, + 2952129490, + 5844915688, + 2952129491, + 5844914079, + 2952129488 + ], + "tags": { + "addr:housenumber": "93-99", + "addr:postcode": "2050", + "addr:street": "Parramatta Road", + "addr:suburb": "Camperdown", + "building": "retail", + "building:levels": "2", + "name": "Savers", + "opening_hours": "Mo-We,Fr 09:00-19:00; Th 09:00-21:00; Sa 09:00-18:00; Su 10:00-18:00", + "roof:shape": "flat", + "shop": "second_hand" + } + }, + { + "type": "way", + "id": 291725581, + "center": { + "lat": -33.8870857, + "lon": 151.1764593 + }, + "nodes": [ + 2952129492, + 5844915703, + 5844915702, + 5844915701, + 2952129493, + 5844915700, + 2952129494, + 2952129495, + 2952129492 + ], + "tags": { + "addr:housenumber": "101", + "addr:postcode": "2050", + "addr:street": "Parramatta Road", + "addr:suburb": "Camperdown", + "brand": "Kidstuff", + "brand:wikidata": "Q117746407", + "building": "retail", + "building:levels": "2", + "check_date": "2025-09", + "name": "Kidstuff", + "opening_hours": "Mo-Sa 09:00-17:30, Su 09:00-17:00", + "phone": "+61 2 9519 4848", + "shop": "toys", + "website": "https://www.kidstuff.com.au/", + "wheelchair": "yes" + } + }, + { + "type": "way", + "id": 605984545, + "center": { + "lat": -33.9064099, + "lon": 151.1717385 + }, + "nodes": [ + 5746331046, + 5746331047, + 5746331048, + 5746331049, + 1852656521, + 5746076963, + 5746331046 + ], + "tags": { + "brand": "EB Games", + "brand:wikidata": "Q5322604", + "name": "EB Games", + "opening_hours:signed": "no", + "shop": "video_games", + "wheelchair": "yes" + } + }, + { + "type": "way", + "id": 605984580, + "center": { + "lat": -33.9073712, + "lon": 151.1724377 + }, + "nodes": [ + 5746331119, + 9572458386, + 5746331037, + 5746331120, + 5746331030, + 5746331029, + 5746331163, + 5746331028, + 3489947034, + 5746331118, + 5746331119 + ], + "tags": { + "name": "Toys and Tales", + "shop": "toys", + "wheelchair": "yes" + } + } + ] +} \ No newline at end of file diff --git a/web/assets/towns/bendigo_real.json b/web/assets/towns/bendigo_real.json new file mode 100644 index 0000000..acbbdee --- /dev/null +++ b/web/assets/towns/bendigo_real.json @@ -0,0 +1,98 @@ +{ + "schema": "procity-town-cache/1", + "key": "bendigo_real", + "town": "Bendigo", + "source": "osm", + "license": "ODbL 1.0", + "attribution": "\u00a9 OpenStreetMap contributors", + "generator": "pipeline/build_towns.py", + "fetchedAt": "2026-07-16", + "center": { + "lat": -36.758, + "lon": 144.281 + }, + "bbox": { + "minLat": -36.773315315315315, + "minLon": 144.26188381955436, + "maxLat": -36.74268468468469, + "maxLon": 144.30011618044566 + }, + "counts": { + "raw": 9, + "shops": 9 + }, + "shops": [ + { + "id": 329659785, + "name": "Slave-ohs", + "type": "opshop", + "lat": -36.755792, + "lon": 144.262936, + "suburb": "Ironbark" + }, + { + "id": 827550052, + "name": "Sinnies", + "type": "opshop", + "lat": -36.757526, + "lon": 144.286017, + "suburb": "Bendigo" + }, + { + "id": 1443367904, + "name": "Book Now", + "type": "book", + "lat": -36.756261, + "lon": 144.282307, + "suburb": "Bendigo" + }, + { + "id": 7816868915, + "name": "Red Floss Shop", + "type": "opshop", + "lat": -36.761063, + "lon": 144.282046, + "suburb": "Bendigo" + }, + { + "id": 8040967386, + "name": "Dymocks", + "type": "book", + "lat": -36.760232, + "lon": 144.278692, + "suburb": "Bendigo" + }, + { + "id": 8067036585, + "name": "Bookmark", + "type": "book", + "lat": -36.760589, + "lon": 144.276186, + "suburb": "Bendigo" + }, + { + "id": 8225415317, + "name": "Bat City Music", + "type": "record", + "lat": -36.758965, + "lon": 144.277469, + "suburb": "Bendigo" + }, + { + "id": 8649203082, + "name": "EB Games", + "type": "video", + "lat": -36.765692, + "lon": 144.280806, + "suburb": "Bendigo" + }, + { + "id": 12421060314, + "name": "Cashtheists", + "type": "pawn", + "lat": -36.759374, + "lon": 144.27927, + "suburb": "Bendigo" + } + ] +} \ No newline at end of file diff --git a/web/assets/towns/castlemaine_real.json b/web/assets/towns/castlemaine_real.json new file mode 100644 index 0000000..f39e03e --- /dev/null +++ b/web/assets/towns/castlemaine_real.json @@ -0,0 +1,74 @@ +{ + "schema": "procity-town-cache/1", + "key": "castlemaine_real", + "town": "Castlemaine", + "source": "osm", + "license": "ODbL 1.0", + "attribution": "\u00a9 OpenStreetMap contributors", + "generator": "pipeline/build_towns.py", + "fetchedAt": "2026-07-16", + "center": { + "lat": -37.064, + "lon": 144.217 + }, + "bbox": { + "minLat": -37.077513513513516, + "minLon": 144.2000649826232, + "maxLat": -37.050486486486484, + "maxLon": 144.23393501737684 + }, + "counts": { + "raw": 6, + "shops": 6 + }, + "shops": [ + { + "id": 315282080, + "name": "Stonemans Bookroom", + "type": "book", + "lat": -37.065704, + "lon": 144.219673, + "suburb": "" + }, + { + "id": 9231704552, + "name": "Sinnies", + "type": "opshop", + "lat": -37.062363, + "lon": 144.217526, + "suburb": "" + }, + { + "id": 11699200337, + "name": "Barrow Boy Traders", + "type": "opshop", + "lat": -37.063201, + "lon": 144.218163, + "suburb": "" + }, + { + "id": 11699208641, + "name": "Mount of Alex Bookshop", + "type": "book", + "lat": -37.061647, + "lon": 144.217247, + "suburb": "" + }, + { + "id": 11699237531, + "name": "XXXX Antiques", + "type": "opshop", + "lat": -37.072912, + "lon": 144.206493, + "suburb": "" + }, + { + "id": 11760538035, + "name": "Slave-ohs", + "type": "opshop", + "lat": -37.067129, + "lon": 144.217625, + "suburb": "" + } + ] +} \ No newline at end of file diff --git a/web/assets/towns/fremantle_real.json b/web/assets/towns/fremantle_real.json new file mode 100644 index 0000000..cd55373 --- /dev/null +++ b/web/assets/towns/fremantle_real.json @@ -0,0 +1,194 @@ +{ + "schema": "procity-town-cache/1", + "key": "fremantle_real", + "town": "Fremantle", + "source": "osm", + "license": "ODbL 1.0", + "attribution": "\u00a9 OpenStreetMap contributors", + "generator": "pipeline/build_towns.py", + "fetchedAt": "2026-07-16", + "center": { + "lat": -32.0555, + "lon": 115.748 + }, + "bbox": { + "minLat": -32.067211711711714, + "minLon": 115.73418143183595, + "maxLat": -32.04378828828829, + "maxLon": 115.76181856816406 + }, + "counts": { + "raw": 21, + "shops": 21 + }, + "shops": [ + { + "id": 52013903, + "name": "Timezone", + "type": "video", + "lat": -32.055434, + "lon": 115.747749, + "suburb": "Fremantle" + }, + { + "id": 310008675, + "name": "Slave-ohs", + "type": "opshop", + "lat": -32.056734, + "lon": 115.74845, + "suburb": "Fremantle" + }, + { + "id": 592578660, + "name": "Elizabeth's Warehouse", + "type": "book", + "lat": -32.048676, + "lon": 115.750901, + "suburb": "Fremantle" + }, + { + "id": 976912099, + "name": "Toyworld", + "type": "toy", + "lat": -32.049479, + "lon": 115.749569, + "suburb": "Fremantle" + }, + { + "id": 3144618368, + "name": "Elizabeth's", + "type": "book", + "lat": -32.05537, + "lon": 115.747192, + "suburb": "Fremantle" + }, + { + "id": 4601037692, + "name": "New Edition Bookshop", + "type": "book", + "lat": -32.055281, + "lon": 115.744283, + "suburb": "Fremantle" + }, + { + "id": 4601057991, + "name": "Bill Campbell Books", + "type": "book", + "lat": -32.055042, + "lon": 115.744405, + "suburb": "Fremantle" + }, + { + "id": 5026468221, + "name": "Mothballs Vintage", + "type": "opshop", + "lat": -32.055583, + "lon": 115.747843, + "suburb": "Fremantle" + }, + { + "id": 5885985088, + "name": "Cashtheists", + "type": "pawn", + "lat": -32.055045, + "lon": 115.748787, + "suburb": "Fremantle" + }, + { + "id": 8153813968, + "name": "The Record Finder", + "type": "record", + "lat": -32.055236, + "lon": 115.744369, + "suburb": "Fremantle" + }, + { + "id": 9156620320, + "name": "Shave the Children", + "type": "opshop", + "lat": -32.065687, + "lon": 115.752589, + "suburb": "Fremantle" + }, + { + "id": 9794077228, + "name": "Adorn Vintage", + "type": "opshop", + "lat": -32.065416, + "lon": 115.752568, + "suburb": "Fremantle" + }, + { + "id": 9795944064, + "name": "Paper Bird", + "type": "book", + "lat": -32.056204, + "lon": 115.744727, + "suburb": "Fremantle" + }, + { + "id": 10585254716, + "name": "Sinnies", + "type": "opshop", + "lat": -32.053328, + "lon": 115.749669, + "suburb": "Fremantle" + }, + { + "id": 11323015046, + "name": "The Picker Concept", + "type": "opshop", + "lat": -32.055611, + "lon": 115.748306, + "suburb": "Fremantle" + }, + { + "id": 11337031591, + "name": "Nifty in Freo", + "type": "opshop", + "lat": -32.05392, + "lon": 115.749811, + "suburb": "Fremantle" + }, + { + "id": 11337040744, + "name": "Red Floss Shop", + "type": "opshop", + "lat": -32.053937, + "lon": 115.74609, + "suburb": "Fremantle" + }, + { + "id": 12389445377, + "name": "St Pats", + "type": "opshop", + "lat": -32.051406, + "lon": 115.749467, + "suburb": "Fremantle" + }, + { + "id": 12490289378, + "name": "Warhammer", + "type": "toy", + "lat": -32.052216, + "lon": 115.749274, + "suburb": "Fremantle" + }, + { + "id": 12787392414, + "name": "Vintage On High", + "type": "opshop", + "lat": -32.054988, + "lon": 115.74523, + "suburb": "Fremantle" + }, + { + "id": 12878567802, + "name": "Atomic Alley", + "type": "toy", + "lat": -32.053366, + "lon": 115.747261, + "suburb": "Fremantle" + } + ] +} \ No newline at end of file diff --git a/web/assets/towns/katoomba_real.json b/web/assets/towns/katoomba_real.json new file mode 100644 index 0000000..10c2b30 --- /dev/null +++ b/web/assets/towns/katoomba_real.json @@ -0,0 +1,186 @@ +{ + "schema": "procity-town-cache/1", + "key": "katoomba_real", + "town": "Katoomba", + "source": "osm", + "license": "ODbL 1.0", + "attribution": "\u00a9 OpenStreetMap contributors", + "generator": "pipeline/build_towns.py", + "fetchedAt": "2026-07-16", + "center": { + "lat": -33.7145, + "lon": 150.312 + }, + "bbox": { + "minLat": -33.72801351351352, + "minLon": 150.29575415811652, + "maxLat": -33.700986486486485, + "maxLon": 150.3282458418835 + }, + "counts": { + "raw": 20, + "shops": 20 + }, + "shops": [ + { + "id": 753467867, + "name": "Blue Mountains Cancer Help Op Shop", + "type": "opshop", + "lat": -33.707567, + "lon": 150.310861, + "suburb": "Katoomba" + }, + { + "id": 4047416904, + "name": "Tanglecare", + "type": "opshop", + "lat": -33.716738, + "lon": 150.310919, + "suburb": "Katoomba" + }, + { + "id": 4047416906, + "name": "Cat Defence Op Shop", + "type": "opshop", + "lat": -33.717477, + "lon": 150.31186, + "suburb": "Katoomba" + }, + { + "id": 4047416910, + "name": "Red Shield Family Store", + "type": "opshop", + "lat": -33.717291, + "lon": 150.309527, + "suburb": "Katoomba" + }, + { + "id": 4047416911, + "name": "Steven Walter", + "type": "opshop", + "lat": -33.710934, + "lon": 150.309931, + "suburb": "Katoomba" + }, + { + "id": 4047416913, + "name": "Sinnies", + "type": "opshop", + "lat": -33.717464, + "lon": 150.311782, + "suburb": "Katoomba" + }, + { + "id": 4958417278, + "name": "Tanglecare Op Shops", + "type": "opshop", + "lat": -33.714949, + "lon": 150.311257, + "suburb": "Katoomba" + }, + { + "id": 4958417724, + "name": "Katoomba Music", + "type": "record", + "lat": -33.715074, + "lon": 150.311532, + "suburb": "Katoomba" + }, + { + "id": 5237464912, + "name": "Katoomba Vintage Emporium", + "type": "opshop", + "lat": -33.713729, + "lon": 150.311403, + "suburb": "Katoomba" + }, + { + "id": 6938379064, + "name": "Katoomba Antique Centre", + "type": "opshop", + "lat": -33.712206, + "lon": 150.310654, + "suburb": "Katoomba" + }, + { + "id": 6938414604, + "name": "Slave-ohs", + "type": "opshop", + "lat": -33.714654, + "lon": 150.311316, + "suburb": "Katoomba" + }, + { + "id": 6938414612, + "name": "EB Games", + "type": "video", + "lat": -33.714851, + "lon": 150.31127, + "suburb": "Katoomba" + }, + { + "id": 6938921533, + "name": "MacArthur's Arcade", + "type": "opshop", + "lat": -33.71389, + "lon": 150.311766, + "suburb": "Katoomba" + }, + { + "id": 6938921534, + "name": "Mr Pickwick's Bookshop", + "type": "book", + "lat": -33.713915, + "lon": 150.311761, + "suburb": "Katoomba" + }, + { + "id": 7040012373, + "name": "Black Antiques", + "type": "opshop", + "lat": -33.709806, + "lon": 150.309278, + "suburb": "Katoomba" + }, + { + "id": 7110813637, + "name": "Steven Walter Children's Cancer Foundation Warehouse", + "type": "opshop", + "lat": -33.712811, + "lon": 150.321286, + "suburb": "Katoomba" + }, + { + "id": 9231634025, + "name": "Little Lost Bookshop", + "type": "book", + "lat": -33.716393, + "lon": 150.310835, + "suburb": "Katoomba" + }, + { + "id": 9577859712, + "name": "B'COS Agora Second Hand Bookshop", + "type": "book", + "lat": -33.714295, + "lon": 150.311173, + "suburb": "Katoomba" + }, + { + "id": 9791871398, + "name": "The Velvet Fog", + "type": "record", + "lat": -33.712544, + "lon": 150.311566, + "suburb": "Katoomba" + }, + { + "id": 11242974232, + "name": "Sanctus Catholic Books and Gift Shop", + "type": "book", + "lat": -33.715718, + "lon": 150.31095, + "suburb": "Katoomba" + } + ] +} \ No newline at end of file diff --git a/web/assets/towns/newtown_real.json b/web/assets/towns/newtown_real.json new file mode 100644 index 0000000..c911b7b --- /dev/null +++ b/web/assets/towns/newtown_real.json @@ -0,0 +1,170 @@ +{ + "schema": "procity-town-cache/1", + "key": "newtown_real", + "town": "Newtown", + "source": "osm", + "license": "ODbL 1.0", + "attribution": "\u00a9 OpenStreetMap contributors", + "generator": "pipeline/build_towns.py", + "fetchedAt": "2026-07-16", + "center": { + "lat": -33.8985, + "lon": 151.179 + }, + "bbox": { + "minLat": -33.91021171171171, + "minLon": 151.1648899610343, + "maxLat": -33.88678828828829, + "maxLon": 151.1931100389657 + }, + "counts": { + "raw": 19, + "shops": 18 + }, + "shops": [ + { + "id": 291725581, + "name": "Kidstuff", + "type": "toy", + "lat": -33.887086, + "lon": 151.176459, + "suburb": "Camperdown" + }, + { + "id": 605984545, + "name": "EB Games", + "type": "video", + "lat": -33.90641, + "lon": 151.171739, + "suburb": "Newtown" + }, + { + "id": 605984580, + "name": "Toys and Tales", + "type": "toy", + "lat": -33.907371, + "lon": 151.172438, + "suburb": "Newtown" + }, + { + "id": 2365230295, + "name": "Egg Records", + "type": "record", + "lat": -33.897238, + "lon": 151.179525, + "suburb": "Newtown" + }, + { + "id": 3278485277, + "name": "The Vintage Record", + "type": "record", + "lat": -33.887518, + "lon": 151.170786, + "suburb": "Annandale" + }, + { + "id": 3278488658, + "name": "Aalders", + "type": "opshop", + "lat": -33.887788, + "lon": 151.171395, + "suburb": "Stanmore" + }, + { + "id": 5931284422, + "name": "Towers Antiques", + "type": "opshop", + "lat": -33.903551, + "lon": 151.179767, + "suburb": "Newtown" + }, + { + "id": 5931284426, + "name": "Old Wares", + "type": "opshop", + "lat": -33.903293, + "lon": 151.179601, + "suburb": "Newtown" + }, + { + "id": 6204412995, + "name": "Goulds Books", + "type": "book", + "lat": -33.902918, + "lon": 151.179587, + "suburb": "Newtown" + }, + { + "id": 6901192228, + "name": "Co-op", + "type": "book", + "lat": -33.890884, + "lon": 151.192028, + "suburb": "Newtown" + }, + { + "id": 9518337417, + "name": "Downtown Music", + "type": "record", + "lat": -33.887873, + "lon": 151.166497, + "suburb": "Annandale" + }, + { + "id": 9888616491, + "name": "Haze Guitars", + "type": "record", + "lat": -33.892598, + "lon": 151.186816, + "suburb": "Newtown" + }, + { + "id": 11984912424, + "name": "Thats So Mi", + "type": "opshop", + "lat": -33.901136, + "lon": 151.178408, + "suburb": "Newtown" + }, + { + "id": 12958032466, + "name": "St Luke\u2019s Op Shop", + "type": "opshop", + "lat": -33.898898, + "lon": 151.170272, + "suburb": "Newtown" + }, + { + "id": 13122105023, + "name": "Savers", + "type": "opshop", + "lat": -33.886919, + "lon": 151.176713, + "suburb": "Camperdown" + }, + { + "id": 13686752819, + "name": "Better Read Than Dead", + "type": "book", + "lat": -33.89613, + "lon": 151.180366, + "suburb": "Newtown" + }, + { + "id": 13686752821, + "name": "Elizabeth's Bookshop", + "type": "book", + "lat": -33.895932, + "lon": 151.180719, + "suburb": "Newtown" + }, + { + "id": 13707141472, + "name": "Papa Disquo", + "type": "record", + "lat": -33.898666, + "lon": 151.174207, + "suburb": "Newtown" + } + ] +} \ No newline at end of file