Lane E R17 (v3.2): the real-map scout — 5 AU town caches + publish --verify fix

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 <noreply@anthropic.com>
This commit is contained in:
m3ultra 2026-07-16 14:49:40 +10:00
parent 6671cf08f4
commit 483a4b75c6
14 changed files with 2322 additions and 13 deletions

View File

@ -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/<key>.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).

193
pipeline/build_towns.py Normal file
View File

@ -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/<key>.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()

View File

@ -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__":

View File

@ -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 `<key>.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**.
> <https://www.openstreetmap.org/copyright> · <https://opendatacommons.org/licenses/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/<key>.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).

View File

@ -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/"
}
}
]
}

View File

@ -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"
}
}
]
}

View File

@ -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"
}
}
]
}

View File

@ -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"
}
}
]
}

View File

@ -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"
}
}
]
}

View File

@ -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"
}
]
}

View File

@ -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": ""
}
]
}

View File

@ -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"
}
]
}

View File

@ -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"
}
]
}

View File

@ -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"
}
]
}