# GapDrone Route Lab — Western Queensland research pilot

Open `index.html` directly in a browser (internet needed for Leaflet/CARTO tiles). All pilot data and the ABS rasters are local; **no API key is required**. Run `node test.js` for model checks. `fetch_data.py` refreshes the western-QLD ABS/OSM data; `fetch_channel_country.py` refreshes the Channel Country data plus real corridor-density sampling; `python3 build_data.py` rebuilds `data.js` for both regions, then `python3 build_html.py` bundles the scripts into directly-openable `index.html`. Public endpoints have fair-use/availability limits; do not repeatedly bulk-query them.

## What is sourced

- **10 urban centres/localities** in the western Queensland pilot: ABS [ASGS Edition 3 UCL point service](https://geo.abs.gov.au/arcgis/rest/services/ASGS2021/UCL/FeatureServer/2), with each 2021 population fetched from its individual [Census QuickStats](https://www.abs.gov.au/census/find-census-data/quickstats/2021/UCL315053) page. These UCLs **exclude rural population outside the urban locality**; the ABS says it does not publish an official list of towns. Their point coordinates are representative points, not landing coordinates. See `data/towns.json` for identifiers, population, coordinates and individual source URLs.
- **70 named airstrip/aerodrome candidates** from [OpenStreetMap](https://www.openstreetmap.org/) via [Overpass API](https://overpass-api.de/); see `data/airstrips.json` for OSM element IDs and locations. OSM accuracy, runway availability, ownership and suitability are unverified. Obtain current [Airservices general aviation data](https://data.airservicesaustralia.com/data-product/general-aviation-data) under its subscription/licence and verify with the aerodrome operator for operations. OSM data © OpenStreetMap contributors, ODbL.
- **2024 ABS modelled resident population grid** image clipped to the pilot region, from [Victorian Government ArcGIS service](https://spatial.planning.vic.gov.au/gis/rest/services/abs_population_grid_2024/MapServer/0), source ABS. `data/population-grid-2024.png` is display-only, not a cell-value dataset, and cannot support numeric maximum-density or AusSORA determinations. The [ABS 2025 grid](https://www.arcgis.com/home/item.html?id=d46f78acc85c4a238175e7ec71a77a72) exists but was not imported as numeric pixels. Avoid mistaking 2024 overlay for current exposure.
- [Australia Post FY2025 facts](https://auspost.com.au/about-us/news-media/fast-facts-about-australia-post) report 2.2 billion items nationally and 12.8 million delivery points, but **do not give this pilot's parcel mass or origin–destination demand**. The model's initial 0.2 parcels/person/week, 1.5 kg/parcel and 20% drone capture are **unvalidated scenario assumptions**, not inferred from those national totals. Its cost inputs are likewise arbitrary placeholders. [PAF/GeoPAF](https://auspost.com.au/business/services/data-services/address-data) are licensed delivery-address products, not publicly downloadable parcel flow or weight. No postcode, address, parcel or mail route database has been imported.

## Regions

The planner now supports two regions, switchable from the **Region** dropdown:

1. **Western Queensland (Longreach cluster)** — the original pilot (Longreach, Winton, Barcaldine, Blackall, Charleville etc.), area-average density proxy only.
2. **Channel Country (Birdsville / Bedourie / Boulia / Windorah)** — added to test the specific "lowest practical SAIL" corridor identified from CASA's current AusSORA population-density thresholds (see below). This region includes **real ABS 2024 1km-grid corridor sampling**, not just the town-area proxy.
3. **Australia-wide (postal network optimizer)** — all 1,809 ABS 2021 UCL towns with verified Census populations and 856 OSM-derived hub candidates within 15 km of a town, nationwide. See "Australia-wide dataset" and "SAIL calculator" and "Network optimizer" below.

## Australia-wide dataset

`fetch_australia.py` builds the nationwide region from two real, cited sources:

- **1,809 towns** from the ABS [ASGS2021 UCL point service](https://geo.abs.gov.au/arcgis/rest/services/ASGS2021/UCL/FeatureServer/2) (returned in Web Mercator — reprojected to lat/lon in the fetch script), joined against **verified 2021 Census total-person counts** from the official bulk DataPack `2021_GCP_UCL_for_AUS_short-header.zip` (`2021Census_G01_AUST_UCL.csv`, field `Tot_P_P`) — not per-town scraping, so every population figure is the same official count CASA/ABS would reference. "Remainder of State/Territory" pseudo-localities are excluded.
- **2,103 named aerodrome/airstrip candidates** nationwide from OpenStreetMap via a single country-wide Overpass query (`area["ISO3166-1"="AU"]`), narrowed in `build_data.py`/`app.js` to **856 hub candidates** within 15 km of at least one town.

This is the same provenance standard as the regional pilots — real government population counts and OSM geocoding, not synthetic or estimated figures — but it is still **not** Australia Post, logistics, or freight-volume data (see "What is sourced" above; none of that exists in this project). Nationwide town-marker rendering is intentionally suppressed above 400 towns in the UI (map/browser performance), and the exact pairing search is swapped for a faster bucketed heuristic (`planFast` in `planner.js`) above 60 towns per hub — see "Network optimizer" for the performance tradeoff this implies.

## SAIL calculator (AusSORA inputs panel)

The tool now computes an estimated SAIL end-to-end from the published AC 101-06 tables, not just the iGRC density band:

1. **iGRC** — `iGRCFromDensity(density, dimension, speed, controlled)` in `planner.js` looks up Table 6 using your aircraft's characteristic dimension and max commanded speed (ATLAS 2.0 defaults: 6.908 m from manual §1.4, speed left as an editable placeholder since the manual's airspeed table is blank) against the selected destination/corridor density, using the full published dimension/speed column set (1m/25m/s, 3m/35m/s, 8m/75m/s, 20m/150m/s, 40m/200m/s) and population band set (Controlled ground area through Assemblies of people). "Assemblies of people" is only scoreable in the 1m/25m/s column per the published table — the function throws for larger aircraft at that density, since AC 101-06 does not define an iGRC there (treat this as **outside AusSORA's drone-category scope**, not as "safe", if your route ever reaches it).
2. **fGRC** — `fGRC(igrc, m1Level, m2Level)` applies the published M1 (strategic mitigation, max −3) and M2 (reduced ground-impact effects, max −2) credits at None/Low/Medium/High robustness, floored at 1. These are editable dropdowns in the UI (section 04) — defaulted to **None**, since a genuinely isolated corridor should not need them, and claiming higher robustness in reality needs real evidence (testing/simulation/third-party validation), not a dropdown selection.
3. **SAIL** — `sailFromFGRCandARC(fgrc, arc)` crosses fGRC against your selected residual Air Risk Class (a/b/c/d) using the published JARUS SORA Step 7 table (fGRC ≤2/3 + ARC a or b = SAIL I/II; fGRC 4 is SAIL III at *any* ARC — no partial credit). ARC a/b (uncontrolled airspace, away from aerodromes) is what the Channel Country-style corridors realistically support; ARC assessment itself (airspace class, traffic density, strategic mitigations) is **not modelled** here — the dropdown is a user input, not a calculated output.

**This produces an estimate for sensitivity-checking route/mitigation choices against a SAIL II target — it is not a CASA submission, and the density/dimension/speed/ARC/mitigation inputs are this tool's assumptions, not verified or reviewed figures.**

The flight inspector (clicking any route) shows this SAIL estimate twice: first instantly from the precomputed density (area-average proxy or precomputed Channel Country corridor sample), then refined a few seconds later by a **live client-side corridor sample** — see below.

## Live corridor density sampling (any region, any route)

Rather than precomputing corridor samples for every possible hub-town pair nationwide (not feasible — hundreds of thousands of pairs), the tool samples the real **ABS 2024 1km population grid** live, in the browser, for whichever route you inspect. This uses the exact same `identify` endpoint and great-circle sampling method as `fetch_channel_country.py`, reimplemented client-side in `app.js`'s `liveCorridorSample()`. The Victorian Government ArcGIS service (`spatial.planning.vic.gov.au/.../abs_population_grid_2024/MapServer`) returns `Access-Control-Allow-Origin: *`, so this works from any browser without a proxy or API key. Results are cached per hub↔town pair for the session. This means **every region, including the nationwide one, can get a real corridor-sampled density on demand**, not just the two regions with precomputed corridor files.

## Network optimizer (greedy multi-hub heuristic)

Section 06 runs `GapPlan.network(hubCandidates, towns, opts, corridors, maxHubs, sailOpts)`: repeatedly picks whichever unused hub candidate currently captures the most unserved weekly cargo (pre-scored cheaply across all candidates, then the expensive exact/fast planner is only run on the top 15 by that cheap score, for runtime), assigns its best-fit towns via the same single-hub planner used everywhere else in the tool, removes served towns from the pool, and repeats up to the entered hub budget. Nationwide (1,809 towns, 856 hub candidates), this takes roughly 5–10 seconds in-browser for 8–12 hubs.

**"Optimize for SAIL target" checkbox (on by default):** when checked, a candidate route only counts toward a hub's score — and only "serves" its destination towns — if it independently screens as SAIL I or II under the AusSORA inputs in section 04 (see "Two-segment transit/terminal SAIL screen" below). Unchecked, the optimizer reverts to pure cargo-capture, which (as flagged below) is biased toward dense population hubs. **For a remote/outback-delivery strategy, leave this checked** — raw-cargo mode will otherwise recommend hubs that are completely infeasible under your SAIL II target.

**This is still a greedy heuristic, not a proven-optimal facility-location solve**, and even in SAIL mode it only considers towns/pairs it has an actual corridor sample for (nationwide, that currently means only destinations reachable through the two regions with precomputed corridor data — Western Queensland has none, so its SAIL-mode optimizer correctly finds **zero** qualifying routes, rather than fabricating a pass). Extending real corridor sampling to more candidate hub-town pairs is the main lever for making SAIL-mode optimization useful at larger scale; see "Two-segment transit/terminal SAIL screen" for why area-average town density alone can never pass.

## Two-segment transit/terminal SAIL screen

An earlier version of this tool scored each route's SAIL using one combined "worst density anywhere in the route" number. That made **every** multi-stop route fail, regardless of how isolated the transit was, because a destination town's own area-average density (always well above 0.5/km² for any town big enough to appear in the ABS UCL dataset) always dominated the single number — even the real Birdsville↔Bedourie corridor, which we'd already verified by hand sits at 0/km² for the entire transit, failed this way.

The fix splits every route into two independently-screened segments, matching how AC 101-06 is actually applied in practice:

- **Transit** — the long corridor between hub and town, screened using ONLY a real ABS-grid corridor sample (`corridorMax`, excluding points near either endpoint — see `fetch_channel_country.py`'s `transit_max_density_per_km2` field). If no corridor sample exists for a pair, the transit segment is explicitly marked unresolved (`sail: null`), never silently assumed isolated.
- **Terminal** — each destination town's own area-average density (its ABS UCL footprint), screened separately with a lighter default mitigation profile (Low M1, no M2 — editable in code via `sailOpts.terminalM1`/`terminalM2`, not yet exposed as a UI control) since a short declared-buffer approach segment realistically needs a different claim than a 150+ km transit.

A route only resolves to a target SAIL if **both** segments independently clear it. Re-verified against the real data: **Birdsville ↔ Bedourie now correctly resolves to SAIL II** (transit: 0.00/km² → iGRC 3 → fGRC 3 → SAIL II; each terminal town: no ABS UCL footprint area so treated as iGRC 3 → fGRC 2 → SAIL II with the Low M1 terminal default) — matching the hand-verified result from the original AusSORA research, now reproducible from inside the tool itself (`planner.js`'s `routeSegments`/`routeSailSegments`, exercised in `test.js`).

**This still does not model a real ground-risk-buffer/contingency volume, dynamic population, or roads** — it is two coarse numbers per route instead of one, not a submission-grade segmentation. Extending corridor sampling to more region pairs is what would let the SAIL-aware network optimizer actually explore beyond the Channel Country region.

## M1 mitigation detail (ground risk buffer, integrity, assurance — Tables 9–11)

The flight inspector's M1 box (separate from the SAIL badge) shows what a Low/Medium/High M1 robustness claim actually *requires*, sourced directly from AC 101-06 Tables 9–11, not just the numeric −1/−2/−3 credit:

- **Ground risk buffer sizing** — the published 1:1 rule: buffer must be at least as large as the operating altitude (e.g. 150m AGL needs a minimum 150m buffer). Section 04's "Operating altitude · m AGL" input drives this figure directly (`GapPlan.groundRiskBuffer`).
- **Integrity criteria** (what must be true) — Low/Medium/High definitions for both the buffer (does it also account for malfunctions, weather, latency, RPA behaviour/performance?) and the claimed population reduction (~90%/99%/99.9% per band, via persons-not-present and/or sheltering).
- **Assurance criteria** (how you prove it) — Low = bare declaration, Medium = supporting evidence (testing/analysis/simulation/inspection/design review/operational experience), High = competent third-party validation.
- **The 25kg sheltering threshold** (Table 11 Criterion #2): a critical mass-dependent fork that applies regardless of robustness level. RPA ≤25kg can simply *declare* it's unlikely to penetrate structures when claiming sheltering credit; RPA >25kg (ATLAS 2.0 is ~150kg MTOW) **must provide actual evidence**. "Aircraft gross mass · kg" in section 04 drives this check (`GapPlan.m1Detail`), and the panel surfaces an explicit warning when it applies.

**What this panel does NOT do:** compute or claim a containment/adjacent-area/decaying-population-density determination. Per AC 101-06 Instruction 3, that assessment is performed by a CASA officer using a weighted population-density model around your actual take-off point(s) — it is explicitly **not** something applicants self-assess, and this tool does not attempt to reproduce it. The panel only shows the published integrity/assurance *criteria* you'd need to satisfy for a given M1 robustness claim, as a reference while deciding which robustness level to target — not a computed containment result.

## Ground risk buffer / adjacent area shown on the map + self-estimated containment

Section 04's "Operating altitude" and the entered "Round-trip range" now drive a visible, illustrative containment screen, drawn on the map every time you inspect a flight:

- **Ground risk buffer** — a dashed band either side of the flight track, width = altitude (1:1 rule, `GapPlan.bufferPolyline`). Zoom in on a route segment to actually see it — at whole-of-Australia zoom a ~100m-scale band is invisibly thin next to a 300+ km route.
- **Adjacent area** — a dashed circle around the hub, radius = your entered round-trip range (used as a one-way proxy, since the tool has no separate one-way max-range input), per AC 101-06 Instruction 3's containment concept (`GapPlan.adjacentAreaRadiusKm`).
- **Weighted adjacent-area density** — a self-computed estimate (`GapPlan.weightedAdjacentDensity`), combining every real ABS-grid sample along the route *and* 8 radial spokes out from the hub, weighted by a simple **linear decay** from the hub (full weight) to the adjacent-area edge (zero weight). This is a disclosed, explicitly-labelled **approximation** of CASA's described-but-unpublished "decaying probability density function" — not a reproduction of it, and not something CASA requires or reviews applicant-side. It exists purely so you can see roughly where a route stands before engaging CASA's own containment assessment.

## "Unresolved" transit segments now actually resolve

Previously, any hub↔town pair without a precomputed corridor file (i.e. everywhere except the Channel Country sampling) permanently showed "No corridor sample — not resolved" in the SAIL screen, even after the live client-side ABS-grid sampler ran. Fixed: `routeSailSegments` now accepts an optional `liveTransitDensity` and uses it as the transit figure whenever no precomputed corridor exists, so the flight inspector resolves a real SAIL answer for *any* route, anywhere in Australia, a few seconds after you click it — not just the two hand-sampled pilot regions. The resolved answer is whatever it actually is (often a high SAIL, since most of Australia isn't an empty desert corridor) — this fixes the UI dead-end, it does not make routes look better than they are.

## Always resolves an actual SAIL (I through VI), not just a target pass/fail

A further fix on top of the above: `routeSailSegments` used to return `sail: null` any time a route screened WORSE than your I/II target, which looked identical to "data missing" — you couldn't tell SAIL III from truly-unresolved. Now `sail` is always the route's real worst-case resolved SAIL (the highest-severity result across the transit segment and every terminal segment, via `GapPlan.worseSail`), and a separate `inTarget` boolean tells you whether that resolved SAIL meets your target. `sail: null` now means only one thing: the transit density is genuinely unknown (no corridor sample and no live sample fetched yet) — not "fails your target". The flight inspector's verdict line is colour-coded by `GapPlan.sailTier(sail)`: green ("good", SAIL I/II), yellow ("warn", SAIL III), red ("bad", SAIL IV–VI).

## Section 07: National SAIL screen (map-wide filter + colour-coded scan)

A dedicated scan tool, separate from the single-route flight inspector: picks a scope (current hub only / filter by state, Australia-wide region only / the whole loaded region), resolves a SAIL for every candidate direct hub↔town hop in that scope (reusing precomputed density where available, fetching a live ABS-grid sample where it isn't — 6 requests in flight at a time, politeness-capped), then paints the map: **green dots/lines = SAIL I/II, yellow = SAIL III, red = SAIL IV+** (added to the map legend). A tier dropdown filters what's drawn (all / good-only / good+warn). Results are listed sorted best-SAIL-first, clickable to zoom the map to that hub↔town pair.

Two things worth being precise about:

- **This screens direct single-stop hops, not the paired multi-stop routes used elsewhere in the tool** (section 05's planner, the network optimizer). It's a fast per-town triage, not a route plan — a candidate that screens green here still needs to go through the actual planner/optimizer to check payload, cost and pairing feasibility.
- **For scope = state/whole-region, each town is screened from its single NEAREST candidate hub**, not necessarily the hub selected in section 01. This is deliberate — "show me everything across Queensland" needs each town matched to whichever hub is actually closest to it, not forced through one fixed hub.
- **Scope is capped at 400 towns** per scan (configurable in code via `MAX_SCAN` in `app.js`) for in-browser responsiveness; a true nationwide scan of all 1,809 towns would take too long live. Narrow by state, or use a single hub, for full coverage of a smaller area.

## AusSORA ground-risk thresholds (verified against the current draft AC 101-06)

Fetched and grepped directly from CASA's `AC 101-06 v1.0 — AusSORA Assessment requirements and criteria` (March 2026 draft, `consultation.casa.gov.au`), Table 7 defines the quantitative population-density bands used in the iGRC lookup (Table 6):

| Qualitative band | Quantitative threshold (people/km²) |
|---|---|
| Controlled ground area | N/A (active participants only) |
| **Isolated environment** | **< 0.5** |
| Scarcely populated | < 5 |
| Lightly populated | < 50 |
| Sparsely populated | < 500 |
| Suburban / low-density metropolitan | < 5,000 |
| High-density metropolitan | < 50,000 |
| Assemblies of people | > 50,000 |

ATLAS 2.0's 6.908 m wingspan (manual §1.4) falls in Table 6's "8 m / 75 m/s" dimension/speed column regardless of its actual ~31 m/s cruise speed — AusSORA does not credit a slower aircraft with a smaller iGRC column. In that column: Isolated = iGRC 3, Scarcely populated = iGRC 4, Lightly populated = iGRC 5, Sparsely populated = iGRC 6. A genuine BVLOS return mail run cannot claim the VLOS −1 deduction on the long leg. **The lowest realistically achievable iGRC for a 200+ km corridor is 3 (Isolated), achieved only if the highest density anywhere in the operational + ground-risk-buffer + contingency volume stays below 0.5 people/km².**

iGRC is scored on the highest density found **anywhere in those volumes**, not just at the endpoints — a single densely-populated town cell inside an otherwise empty corridor still sets the corridor's iGRC at the town's band, locally, while the long transit between towns can independently sit at Isolated. The Channel Country region's corridor sampling exists to show this split explicitly.

## Corridor-sampled density (Channel Country region only)

`fetch_channel_country.py` queries the real **ABS 2024 1km population grid as numeric pixel values** (via the `identify` endpoint of the Victorian Government's ABS population-grid ArcGIS service, not just the display raster) at ~10 km steps along the great-circle line between each candidate hub pair, and records the maximum sampled density per corridor in `data/channel_country_corridors.json`. The planner (`planner.js`'s `corridorMax`/`routeMaxDensity`) uses this sampled corridor maximum, combined with any town area-average proxy, whichever is higher, and labels which source was used (`corridor-sampled ABS grid`, `area-average proxy`, both, or neither).

Verified results for the discussed corridors:

| Corridor | Distance | Max sampled density |
|---|---|---|
| Birdsville ↔ Bedourie | 171.5 km | 85.8 /km² (town cell only; desert samples are 0) |
| Bedourie ↔ Boulia | 167.2 km | 204.5 /km² (Boulia town cell) |
| Birdsville ↔ Boulia | 337.0 km | 204.5 /km² (Boulia town cell) |
| Winton ↔ Bedourie | 425.6 km | 377.5 /km² (Winton town cell) |

Every non-town sample point across all four corridors returned **0 people/km²** in the 2024 grid — consistent with genuinely Isolated-band desert country, confirming the qualitative premise. The density spikes are entirely attributable to the single 1 km grid cell containing the town itself, which the real AusSORA process would treat as a separate, small, high-mitigation terminal segment (sheltering, defined approach corridor, landing-site selection) rather than representative of the transit. **This 1km grid cell is still a coarse average over the whole cell, not a direct measurement of in-town exposure** — the real terminal-segment iGRC still needs on-site/GNAF-level assessment, dynamic population and CASA review; this tool only screens the long transit.

**Birdsville ↔ Bedourie remains the standout pairing**: both ends have a confirmed OSM-derived airport (Birdsville Airport YBDV, Bedourie Airport YBIE), the corridor fits comfortably inside a 500 km return budget with reserve margin, and the sampled transit is entirely at 0/km² outside the two town cells.



The supplied `GAPDrone_ATLAS2_AFM_RevG.pdf` (controlled internal document, Rev G, 27 May 2026) states §1.4 wingspan 6.908 m; §2.2 MTOW 149.9 kg, empty weight 85 kg, useful load **65.5 kg shared between fuel and payload**, maximum payload 65 kg. The §6.5 full-tank + 50 kg payload example shows 151.64 kg, **above MTOW**; do not treat 50 kg as valid at full fuel. §5.1–5.5 takeoff, cruise, range/endurance and landing performance tables have `[INSERT]` placeholders. Thus the user-supplied 500 km return range is **not validated by the manual**, and no route is operationally feasible on this evidence. The planner caps payload at `min(requested payload, 65.5 - entered fuel kg)` but cannot verify fuel burn, reserve, CG, takeoff/landing or structural/operational limits.

The engine is a gasoline two-stroke (§7.2), **not a battery-electric aircraft**. Fuel cost, endurance and reserve must be verified with engineering data. A route that fits the nominal range is not an approved route. [CASA AC 101-06 AusSORA](https://www.casa.gov.au/aussora-assessment-requirements-and-criteria) requires risk and containment assessment beyond a visual residential grid; dynamic population, roads, gatherings, contingency volumes, adjacent exposure and mitigations are not implemented. No GRC, SAIL, air-risk or authorization is computed. **Do not dispatch aircraft from this tool.**

## Screening algorithm

For each ABS UCL: `weekly_kg = 2021_population × parcels_per_person_week × kg_per_parcel × drone_share`. A hub is selected from OSM airstrip candidates within 35 km of at least one sampled locality. Route distance is the sum of great-circle legs, multiplied by an editable detour factor; return to hub is mandatory. Candidate solo and two-stop trips must fit nominal return range and combined cargo capacity. Greedy pairing chooses savings versus two solo runs, then load and distance; stops with residual demand are repeated. It does **not** optimize globally across hubs, schedules, multiple aircraft, land transport or time windows. Cost = flights × fixed cost + adjusted kilometres × variable cost. This is a sensitivity tool, not a cost-benefit proof.

Each candidate flight also shows a **destination density band** — population ÷ the ABS UCL footprint area, bucketed into five indicative bands (Controlled/sparse ≤5/km² through Gathering-level >5000/km²) shown as a coloured chip, legend and numeric max/km² in the flight inspector. This is a same-order-of-magnitude screening proxy only. It is **not** the AC 101-06 Table 6 iGRC lookup: it uses resident population over a static urban-locality footprint, not the highest density across the actual operational, contingency and ground-risk-buffer volumes; it has no dynamic-population, roads, gathering or seasonal-visitor adjustment; and it cannot substitute for CASA's population-density-map acceptance criteria. Use it only to notice which destinations warrant closer AusSORA attention, never to assign a GRC.

## Next data needed

1. Controlled flight-test range/endurance at several fuel/payload combinations, takeoff/landing, reserve, C2, emergency and flight-termination evidence.
2. Actual origin–destination weekly parcel/mail **kg**, service frequency, time windows, commercial tariffs and last-mile arrangements from a logistics partner. Population alone cannot recover shipment volume or direction.
3. Licensed/current aeronautical site and airspace data, runway permissions, roads and dynamic-population data, and numeric 1 km ABS grid cells with fully defined operational/contingency/ground-risk-buffer volumes before quantitative AusSORA screening.
4. Extend to several Australian regions after the western Queensland pilot is checked. The Australia-wide region now covers nationwide ABS towns + OSM hubs for network-optimizer screening, but still carries none of the demand/freight/aeronautical validation gaps listed above — treat it as the same pilot-grade screening tool at national scale, not a validated national network design.

## Free mapping and keys

The prototype uses free open-source [Leaflet](https://leafletjs.com/) and [Esri's World Street Map basemap tiles](https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer) (`server.arcgisonline.com`); no key is used or required. **Tile provider history:** CARTO's basemaps began requiring a free API key from late August 2026 (switched away from CARTO). OpenStreetMap's own standard tile server (`tile.openstreetmap.org`) was used next, but its [fair-use tile policy](https://operations.osmfoundation.org/policies/tiles/) is meant for light personal use and started returning **HTTP 403** under repeated automated testing/hosting load — not suited to a deployed app, even a low-traffic one. Esri's ArcGIS Online basemap tiles are the current provider: no key, no referer requirement, and intended for this kind of attribution-included embedded use. Government ABS ArcGIS services (population grid, UCL boundaries) are accessible without a key for modest research queries. For sustained/production traffic beyond a personal project, consider a dedicated paid tile provider (Mapbox, Stadia Maps, MapTiler) or self-hosting tiles.

## Hosting on Cloudflare Pages

`index.html` is fully self-contained (data/planner/app all bundled in by `build_html.py`) and needs no build step or server — it's a static file.

- **Drag-and-drop:** Cloudflare dashboard → Workers & Pages → Create → Pages → Upload assets → upload this folder (or just `index.html`). Deploys instantly to a `*.pages.dev` URL.
- **Wrangler CLI:** `npx wrangler pages deploy . --project-name=gapdrone-route-lab` from this directory (first run prompts a browser OAuth login).
- **Git integration:** push to a GitHub repo, connect it in Pages, no build command, output directory `/`. Auto-deploys on push.

After any edit to `planner.js`/`app.js`/`template.html`/`data.js`, re-run `python3 build_html.py` to regenerate `index.html` before redeploying — Pages serves whatever `index.html` contains at upload time, not the source files.
