mirror of
https://github.com/tiennm99/vngeoguessr.git
synced 2026-10-11 03:13:56 +00:00
feat(regions): restructure cities into a country > province > district tree
Replaces the hand-maintained flat CITIES map with a generated 67-node tree: one country, five provinces, 61 districts and towns. Names, centres and extents now come from the boundary build rather than being duplicated in src/lib/game.js, so there is one source instead of two that a test had to police for drift. Boundaries are built per district and unioned upward. A single-child province byte-copies its child rather than re-simplifying it, so the two cannot disagree along a border and strand panoramas in the gap. The country has no polygon of its own: the real Vietnam outline is vastly larger than the covered area and would mislead on a map, so its extent is the envelope of its provinces. OpenStreetMap has applied the 2025 merger, so the pre-2025 units survive only as boundary/historic relations whose rendered parent is the current province. A qualified lookup misses them entirely and a bare one can match a same-named unit elsewhere in the country, so each leaf tries both forms and the hit is validated against the parent pre-2025 extent. 60 of 61 leaves resolve; Cu Chi has no boundary relation left at all, which is why the previous outline already recorded a missing part and no panorama in the index sits there. The generated barrels now sanitise import identifiers and quote object keys. Region codes contain hyphens, which are legal in a filename but not in a bare identifier, and the barrel is rewritten after every region -- emitting them raw would have broken the build from the first district onward. Panorama indexes are re-clipped against the rebuilt outlines, dropping 74 of 424,691 entries that fell outside the retightened boundaries. Da Lat and Duc Hoa keep their bare codes so their leaderboard history stays attached, and their index files move to the province codes that now own them. Scoring is untouched: a guess still credits one city and Vietnam. The rollup through the tree lands in a later change.
This commit is contained in:
1 parent
3235312f7a
commit
f41ba32f30
93 files changed
+93974
-4845
No files matched your search
@@ -0,0 +1,334 @@
|
||||
---
|
||||
phase: 1
|
||||
title: "Region tree model and boundaries"
|
||||
status: completed
|
||||
priority: P1
|
||||
effort: "1.5d"
|
||||
dependencies: []
|
||||
---
|
||||
|
||||
# Phase 1: Region tree model and boundaries
|
||||
|
||||
## Outcome (recorded after execution)
|
||||
|
||||
**60 of 61 leaves resolved.** Cu Chi is the only unresolved one, matching the
|
||||
`"missingParts": 1` the shipped TPHCM boundary already carried. Ha Noi unions to
|
||||
3,363 km² (real Ha Noi ≈ 3,359), Ho Chi Minh to 1,629 km² — identical to the
|
||||
pre-change file.
|
||||
|
||||
Four things differ from the plan as written:
|
||||
|
||||
1. **Nominatim needed a two-form lookup.** The first run resolved only 42 of 61,
|
||||
and every failure was a `Huyện` or `Thị xã` — no `Quận` failed. The cause is
|
||||
not rate limiting: OSM has applied the 2025 merger, so the pre-2025 rural
|
||||
districts survive only as `boundary/historic` relations whose rendered parent
|
||||
is the *current* province. `Huyện Ba Vì, Hà Nội, Việt Nam` matches nothing;
|
||||
`Huyện Ba Vì, Việt Nam` returns relation 7115003, correctly shaped but
|
||||
labelled "Ba Vì District, Phú Thọ Province". The fix is to try the qualified
|
||||
form, then the bare one, and validate the hit's centroid against the parent's
|
||||
pre-2025 bbox (`legacyBbox` in the config) so a same-named unit elsewhere in
|
||||
the country is rejected. Cu Chi has no boundary relation left at all — only
|
||||
cemeteries and bus stops — so it is genuinely unresolvable.
|
||||
2. **Boundaries are grouped by province** (`<province>/<code>.json`) at the
|
||||
user's request; 66 files in one directory was unreadable.
|
||||
3. **The re-clip pass was pulled forward from Phase 2**, which is the response
|
||||
this phase's own risk register pre-decided for H7. It dropped 74 panoramas of
|
||||
424,691 (0.017%) that fell outside the retightened outlines. It lives in
|
||||
`scripts/assign-pano-districts.mjs`, which Phase 2 extends with district
|
||||
assignment.
|
||||
4. **`CITY_BOUNDARIES` was renamed `REGION_BOUNDARIES`**, with its one consumer
|
||||
updated.
|
||||
|
||||
`scripts/build-region-boundaries.mjs --regenerate` rebuilds provinces, the barrel
|
||||
and the tree from the leaf polygons on disk without touching Nominatim. Added
|
||||
after the subfolder move, so reshaping outputs never costs another 60-request
|
||||
pass against a courtesy-rate-limited public service.
|
||||
|
||||
**Measured:** `src/data/boundaries/` is 1.3 MB against the 1.5 MB budget — about
|
||||
9× the old 131 KB, confirming that the pre-red-team "well under 125 KB" estimate
|
||||
was impossible. `npm test` 135/135, `npm run build:check` and `npm run lint`
|
||||
clean, with no assertion weakened.
|
||||
|
||||
## Overview
|
||||
|
||||
Turn the hand-maintained flat `CITIES` map into a generated three-level region
|
||||
tree, and build a boundary polygon for all 61 leaves. Everything downstream reads
|
||||
the tree, so this phase lands first and alone.
|
||||
|
||||
**Red-team: three Critical findings land here.** Fix the barrel identifier
|
||||
generation *before* the first Nominatim run (C1), keep the existing test suite
|
||||
green (C3), and keep `src/lib/regions.js` free of panorama imports (C5).
|
||||
|
||||
## Requirements
|
||||
|
||||
**Functional**
|
||||
- One country, five provinces, 61 leaves, all reachable from one generated map.
|
||||
- Every leaf Nominatim resolves carries a boundary polygon.
|
||||
- A province polygon is the union of its children — except a single-child
|
||||
province, which byte-copies its child.
|
||||
- `VN` has no polygon; its bbox and center derive from its children's bboxes.
|
||||
- Lam Dong and Long An carry `partialCoverage: 'one town covered'`.
|
||||
- The five existing hand-picked centers survive as overrides.
|
||||
|
||||
**Non-functional**
|
||||
- Both generated barrels must parse. Hyphenated codes are illegal as bare JS
|
||||
identifiers *and* as unquoted object keys.
|
||||
- `src/lib/regions.js` imports only `src/data/regions/*`. Client components
|
||||
import it; the panorama data is 25 MB of exact answers.
|
||||
- `npm test` and `npm run build:check` pass **at the end of this phase**, with no
|
||||
assertion weakened to achieve it.
|
||||
- Nominatim's one-request-per-second courtesy limit is respected (61 queries
|
||||
≈ 70 s).
|
||||
|
||||
## Architecture
|
||||
|
||||
`scripts/build-city-boundaries.mjs` is renamed `scripts/build-region-boundaries.mjs`
|
||||
and its `CITIES` config becomes a `REGIONS` config where a leaf carries a
|
||||
Nominatim `query` and a province carries only a name plus its children.
|
||||
|
||||
```js
|
||||
const REGIONS = {
|
||||
VN: { name: 'Vietnam', level: 'country' },
|
||||
|
||||
TPHCM: { name: 'Ho Chi Minh', level: 'province', parent: 'VN',
|
||||
center: [10.8231, 106.6297] }, // override, see below
|
||||
'TPHCM-Q1': { name: 'District 1', level: 'district', parent: 'TPHCM',
|
||||
query: 'Quận 1, Thành phố Hồ Chí Minh, Việt Nam' },
|
||||
// ...
|
||||
|
||||
LD: { name: 'Lam Dong', level: 'province', parent: 'VN',
|
||||
partialCoverage: 'one town covered' },
|
||||
DL: { name: 'Da Lat', level: 'district', parent: 'LD',
|
||||
query: 'Thành phố Đà Lạt, Việt Nam', center: [11.9404, 108.4583] },
|
||||
|
||||
LA: { name: 'Long An', level: 'province', parent: 'VN',
|
||||
partialCoverage: 'one town covered' },
|
||||
DH: { name: 'Duc Hoa', level: 'district', parent: 'LA',
|
||||
query: 'Đức Hòa, Việt Nam', center: [10.8888, 106.3825] },
|
||||
};
|
||||
```
|
||||
|
||||
The existing `parts` lists for `TPHCM` (22) and `DN` (7) already enumerate exactly
|
||||
the districts needed — lift them across as leaf queries. `HN` currently has a
|
||||
single part and needs its 30 units enumerated.
|
||||
|
||||
### C1 — Barrel identifier generation (do this first)
|
||||
|
||||
Both writers derive the import identifier *and* the object key from the filename:
|
||||
|
||||
```js
|
||||
// build-city-boundaries.mjs:190-192, 200-203 — and the identical block in
|
||||
// build-pano-index.mjs:273-287
|
||||
ident: name.replace(/\.json$/, ''),
|
||||
`import ${e.ident} from './${e.file}';`
|
||||
` ${e.code}: ${e.ident},`
|
||||
```
|
||||
|
||||
For `tphcm-q1.json` that emits `import tphcm-q1 from './tphcm-q1.json';` and
|
||||
`TPHCM-Q1: tphcm-q1,` — two parse errors. Because the barrel is rewritten after
|
||||
every region (`build-city-boundaries.mjs:210`), the repo breaks from leaf #1 and
|
||||
stays broken for the rest of a 70-second network run.
|
||||
|
||||
Fix both scripts before running either:
|
||||
|
||||
```js
|
||||
ident: name.replace(/\.json$/, '').replace(/[^a-z0-9]/g, '_'),
|
||||
`import ${e.ident} from './${e.file}';`
|
||||
` ${JSON.stringify(e.code)}: ${e.ident},` // quoted key
|
||||
```
|
||||
|
||||
Add a test that `import()`s each generated barrel.
|
||||
|
||||
### H4 — Centers are overridable, not replaced
|
||||
|
||||
`src/lib/game.js:9-12` records why the five centers are hand-picked: a centroid
|
||||
"for an irregular outline can land somewhere no one associates with the place",
|
||||
and `GameClient.js:102` seeds the map from it. The build computes
|
||||
`turf.centerOfMass` (`build-city-boundaries.mjs:150`); use it only when the
|
||||
`REGIONS` entry has no `center`. Seed the five existing values as overrides and
|
||||
keep a test asserting they are unchanged.
|
||||
|
||||
### H3 — Simplification, with a measured budget
|
||||
|
||||
`SIMPLIFY_TOLERANCE = 0.0005` (~55 m) currently applies to five whole-city
|
||||
outlines totalling **130,519 bytes**. At district scale that tolerance opens
|
||||
gaps along shared borders that Phase 2 must assign panoramas across.
|
||||
|
||||
Simplify **leaves** at `0.0001` (~11 m) and keep `0.0005` for unioned province
|
||||
outlines. The earlier draft claimed this would "stay well under 125 KB", which is
|
||||
arithmetically impossible: 5× the vertex density over more total arc length, with
|
||||
every interior border now drawn twice (once per neighbour). **Budget: 1.5 MB for
|
||||
`src/data/boundaries/` combined.** Step 9 checks it and fails the phase if
|
||||
exceeded. Do not loosen the tolerance to hit the budget — Phase 2's unassigned
|
||||
tally is the quality bar, and loosening reopens the gaps.
|
||||
|
||||
### H7 — The Ha Noi union changes HN's outline
|
||||
|
||||
HN today is one OSM relation (`build-city-boundaries.mjs:32`). Rebuilding it as a
|
||||
union of 30 districts moves the outline, so panoramas clipped against the old
|
||||
polygon may now fall outside — breaking two containment assertions
|
||||
(`tests/pano-index.test.js:34-42`, `:44-59`) that no re-clip repairs, because
|
||||
Phase 2 does not re-fetch tiles.
|
||||
|
||||
Resolve it here: Phase 2 gains a **re-clip pass** over the existing panoramas
|
||||
(no network — it drops out-of-boundary entries and rewrites the header
|
||||
`bbox`/`count`). This phase records the expectation; Phase 2 owns the step. The
|
||||
containment assertions stay in force throughout — do not weaken them.
|
||||
|
||||
### C3 — Keeping the test suite green
|
||||
|
||||
`tests/pano-index.test.js:5,7` derives its parameter list from `CITIES`:
|
||||
|
||||
```js
|
||||
import { CITIES } from '../src/lib/game.js';
|
||||
const CODES = Object.keys(CITIES);
|
||||
```
|
||||
|
||||
Two changes make this work at the end of *this* phase:
|
||||
|
||||
1. **`CITIES` becomes every node in the tree**, keyed by code — not the five
|
||||
provinces. `cityNames['DL']`, `cityCenters['HN']`, and `CITIES['DL']?.bbox`
|
||||
all keep resolving, so `/game?location=DL` and `GameClient.js:463` survive.
|
||||
`cities` (the UI list) is the province filter.
|
||||
2. **Move the panorama file rename into this phase.** `dl.json` → `ld.json` and
|
||||
`dh.json` → `la.json`, so every index file is a province. It is a pure rename
|
||||
with no network cost, and leaving it in Phase 2 is what stranded `LD`/`LA`
|
||||
without an index.
|
||||
|
||||
`tests/pano-index.test.js` then derives `CODES` from `indexedCities()` rather
|
||||
than `CITIES`, and its seven hardcoded `'DL'` sites (`:68, 69, 73, 76, 80, 81,
|
||||
86`) become `'LD'`. Add the file to this phase's list.
|
||||
|
||||
### Region codes
|
||||
|
||||
| Province | Leaf codes |
|
||||
|---|---|
|
||||
| `HN` (30) | `HN-BADINH` `HN-HOANKIEM` `HN-TAYHO` `HN-LONGBIEN` `HN-CAUGIAY` `HN-DONGDA` `HN-HAIBATRUNG` `HN-HOANGMAI` `HN-THANHXUAN` `HN-BACTULIEM` `HN-NAMTULIEM` `HN-HADONG` `HN-SONTAY` `HN-BAVI` `HN-CHUONGMY` `HN-DANPHUONG` `HN-DONGANH` `HN-GIALAM` `HN-HOAIDUC` `HN-MELINH` `HN-MYDUC` `HN-PHUXUYEN` `HN-PHUCTHO` `HN-QUOCOAI` `HN-SOCSON` `HN-THACHTHAT` `HN-THANHOAI` `HN-THANHTRI` `HN-THUONGTIN` `HN-UNGHOA` |
|
||||
| `TPHCM` (22) | `TPHCM-Q1` `TPHCM-Q3` `TPHCM-Q4` `TPHCM-Q5` `TPHCM-Q6` `TPHCM-Q7` `TPHCM-Q8` `TPHCM-Q10` `TPHCM-Q11` `TPHCM-Q12` `TPHCM-BINHTAN` `TPHCM-BINHTHANH` `TPHCM-GOVAP` `TPHCM-PHUNHUAN` `TPHCM-TANBINH` `TPHCM-TANPHU` `TPHCM-THUDUC` `TPHCM-BINHCHANH` `TPHCM-CANGIO` `TPHCM-CUCHI` `TPHCM-HOCMON` `TPHCM-NHABE` |
|
||||
| `DN` (7) | `DN-HAICHAU` `DN-THANHKHE` `DN-SONTRA` `DN-NGUHANHSON` `DN-LIENCHIEU` `DN-CAMLE` `DN-HOAVANG` |
|
||||
| `LD` (1) | `DL` |
|
||||
| `LA` (1) | `DH` |
|
||||
|
||||
Districts 2 and 9 are absent from Ho Chi Minh City on purpose: both merged into
|
||||
Thu Duc in 2021, which the existing `parts` list already reflects.
|
||||
|
||||
`TPHCM-CUCHI` is listed but **expected to fail resolution** — the shipped
|
||||
boundary already records `"missingParts": 1` for it, which is why zero of
|
||||
TPHCM's 184,992 panoramas sit in Cu Chi. Record the failure; do not chase it.
|
||||
|
||||
`DL` and `DH` keep their bare codes so their existing leaderboard keys stay valid.
|
||||
Hoang Sa stays excluded from Da Nang for the reason already in the build script.
|
||||
|
||||
### Missing leaves are recorded, not fatal
|
||||
|
||||
The empirical Nominatim hit rate on this repo's own queries is 28/29; over 61
|
||||
leaves expect one or two failures beyond Cu Chi. A missing leaf:
|
||||
|
||||
- is written to the tree with `coverage: 'unresolved'` and no bbox,
|
||||
- increments the parent's `missingParts`,
|
||||
- is excluded from playability,
|
||||
- does **not** fail the build.
|
||||
|
||||
### Traversal helpers
|
||||
|
||||
`src/lib/regions.js` (new, hand-written, client-safe):
|
||||
|
||||
| Function | Returns |
|
||||
|---|---|
|
||||
| `getRegion(code)` | Node, or throws with the list of known codes |
|
||||
| `ancestorsOf(code)` | `[self, ...parents]` up to and including `VN` |
|
||||
| `childrenOf(code)` | Direct children, empty for a leaf |
|
||||
| `leavesUnder(code)` | Every leaf at or below the node |
|
||||
| `provinceOf(code)` | The province ancestor, or `null` for `VN` |
|
||||
| `regionPath(code)` | Ancestor names, country first — for UI headers |
|
||||
|
||||
It imports `src/data/regions/index.js` and (from Phase 2) `counts.js`. Nothing
|
||||
else. A test walks its import graph and fails if `src/data/panos/` appears.
|
||||
|
||||
## Related Code Files
|
||||
|
||||
- Create: `scripts/build-region-boundaries.mjs` (from `build-city-boundaries.mjs`)
|
||||
- Create: `src/lib/regions.js`
|
||||
- Create: `src/data/regions/index.js` (generated)
|
||||
- Create: `src/data/boundaries/<leaf codes>.json` (generated)
|
||||
- Create: `tests/regions.test.js`
|
||||
- Modify: `scripts/build-pano-index.mjs` — barrel identifier fix (C1) only
|
||||
- Modify: `src/lib/game.js` — derive exports from the tree; `CITIES` = all nodes
|
||||
- Modify: `src/data/boundaries/index.js`, `src/data/panos/index.js` (regenerated)
|
||||
- Rename: `src/data/panos/dl.json` → `ld.json`, `dh.json` → `la.json`
|
||||
- Modify: `tests/pano-index.test.js` — `CODES` from `indexedCities()`; 7 `'DL'` sites → `'LD'`
|
||||
- Modify: `tests/game.test.js` — city-configuration block becomes tree assertions
|
||||
- Delete: `scripts/build-city-boundaries.mjs`
|
||||
|
||||
## Implementation Steps
|
||||
|
||||
1. **Fix both barrel writers first** (C1): sanitise `ident`, quote the object key.
|
||||
Add the barrel-parses test. Do this before any network run.
|
||||
2. Copy `build-city-boundaries.mjs` to `build-region-boundaries.mjs`; replace
|
||||
`CITIES` with the `REGIONS` config, lifting the existing `TPHCM` and `DN` part
|
||||
lists and enumerating Ha Noi's 30. Seed the five `center` overrides.
|
||||
3. Split `buildCity` into `buildLeaf` (one query, tolerance `0.0001`) and
|
||||
`buildProvince` (union children at `0.0005`; byte-copy for a single child).
|
||||
4. Add the `VN` step: no query, bbox as the envelope of the five province bboxes.
|
||||
5. Extend both `writeBarrel`s to emit `src/data/boundaries/index.js` and
|
||||
`src/data/regions/index.js`.
|
||||
6. Run it. Log unresolved leaves; record them, do not abort.
|
||||
7. Rename the two panorama files and regenerate the panorama barrel.
|
||||
8. Write `src/lib/regions.js`; rewrite `src/lib/game.js`'s region exports as
|
||||
derivations over every node.
|
||||
9. Write `tests/regions.test.js`; update `tests/pano-index.test.js` and the
|
||||
`city configuration` block in `tests/game.test.js`. Check the boundary
|
||||
directory against the 1.5 MB budget.
|
||||
10. `npm test`, then `npm run build:check`.
|
||||
|
||||
## Validation
|
||||
|
||||
- Both generated barrels parse under `import()`.
|
||||
- `src/data/boundaries/` is under 1.5 MB.
|
||||
- `tests/regions.test.js` asserts: every `parent` resolves; no cycles; exactly one
|
||||
node with `parent: null`; every resolved leaf's bbox sits inside its province's;
|
||||
`ancestorsOf` on every leaf has length 3 and ends at `VN`; 61 unique leaf codes;
|
||||
`DL`/`DH` parents are `LD`/`LA`.
|
||||
- The five legacy centers are byte-identical to today's values.
|
||||
- `src/lib/regions.js`'s transitive imports never reach `src/data/panos/`.
|
||||
- `getRegion('DL')`, `cityNames['DL']`, and `CITIES['DL'].bbox` all resolve.
|
||||
- A resolved province's area is within 2% of the sum of its children's.
|
||||
- A single-child province's polygon is byte-identical to its child's.
|
||||
- `npm test` passes with **no assertion weakened** — in particular the two
|
||||
containment assertions in `tests/pano-index.test.js` remain in force.
|
||||
|
||||
## Risk Assessment
|
||||
|
||||
**Nominatim cannot resolve some leaves.** Expected: one or two beyond Cu Chi, at
|
||||
the observed 28/29 rate. *Signal:* the build logs `no boundary found — SKIPPED`.
|
||||
*Response:* refine the query string; if still unresolvable, record
|
||||
`coverage: 'unresolved'` and move on. Not a build failure.
|
||||
|
||||
**Leaf boundaries exceed the 1.5 MB budget.** *Signal:* step 9's check fails.
|
||||
*Response:* raise the leaf tolerance to `0.0002` and re-measure — but only after
|
||||
confirming with a Phase 2 dry run that the unassigned tally stays under 2%.
|
||||
Loosening tolerance to hit a size budget is the trade that strands panoramas.
|
||||
|
||||
**The HN union invalidates containment tests before Phase 2's re-clip.** *Signal:*
|
||||
`HN panoramas sit inside the city boundary` fails at the end of this phase.
|
||||
*Response:* pull the re-clip pass forward into this phase rather than weakening
|
||||
the assertion. It needs no network.
|
||||
|
||||
**Renaming the build script breaks a reference.** Verified reference set is five
|
||||
sites, all in `scripts/` and one generated header — `grep -rn
|
||||
"build-city-boundaries" docs/ CLAUDE.md` returns nothing, contrary to the earlier
|
||||
draft. *Response:* update the five; Phase 6 re-greps.
|
||||
|
||||
## Success Criteria
|
||||
|
||||
- [ ] Both generated barrels parse; a test enforces it
|
||||
- [ ] `src/data/regions/index.js` exports 67 nodes: 1 country, 5 provinces, 61 leaves
|
||||
- [ ] Unresolved leaves are recorded with `coverage: 'unresolved'`, not fatal
|
||||
- [ ] Every resolved province polygon is the union of its children; single-child provinces byte-copy
|
||||
- [ ] The five legacy centers are unchanged
|
||||
- [ ] `LD` and `LA` carry `partialCoverage: 'one town covered'`
|
||||
- [ ] `ancestorsOf('DL')` is `['DL', 'LD', 'VN']`; `ancestorsOf('DH')` is `['DH', 'LA', 'VN']`
|
||||
- [ ] `src/lib/regions.js` never imports panorama data
|
||||
- [ ] Panorama files renamed to `ld.json` / `la.json`; barrel regenerated
|
||||
- [ ] `src/data/boundaries/` under 1.5 MB
|
||||
- [ ] `npm test` and `npm run build:check` pass with no assertion weakened
|
||||
@@ -0,0 +1,247 @@
|
||||
---
|
||||
phase: 2
|
||||
title: "Panorama district partition"
|
||||
status: todo
|
||||
priority: P1
|
||||
effort: "1d"
|
||||
dependencies: [1]
|
||||
---
|
||||
|
||||
# Phase 2: Panorama district partition
|
||||
|
||||
## Overview
|
||||
|
||||
Assign every panorama already on disk to the district it sits in, re-clip against
|
||||
the rebuilt province outlines, and teach `src/lib/pano-index.js` to draw from any
|
||||
node in the tree. No new Mapillary requests.
|
||||
|
||||
## Requirements
|
||||
|
||||
**Functional**
|
||||
- Every panorama in the five province indexes carries a district assignment, or
|
||||
is counted in a reported unassigned tally.
|
||||
- Panoramas now outside their rebuilt province outline are dropped, and the
|
||||
header `bbox`/`count` rewritten (H7).
|
||||
- `pickRandomPano(code)` accepts a leaf, a province, or `VN`, and reports the
|
||||
resolving leaf.
|
||||
- `fetchCityPanorama` propagates that leaf from the **winning** attempt (C2).
|
||||
- Drawing at `VN` picks a province uniformly first, then a panorama within it.
|
||||
- Drawing at a province stays uniform over its panoramas (today's behaviour).
|
||||
- Per-node counts and playability are emitted as `src/data/regions/counts.js`.
|
||||
|
||||
**Non-functional**
|
||||
- Zero Mapillary tile requests. The daily cap is 50,000 and a full province
|
||||
rebuild costs ~2,800.
|
||||
- Server-side only. `src/lib/regions.js` must stay free of these imports.
|
||||
- Bucket construction is lazy and memoised per province — a Da Lat request must
|
||||
not point-in-polygon 226k Ha Noi entries.
|
||||
|
||||
## Architecture
|
||||
|
||||
### The partition script
|
||||
|
||||
`scripts/assign-pano-districts.mjs` — network-free, built on a shared module:
|
||||
|
||||
```
|
||||
for each province in [HN, DN, TPHCM, LD, LA]:
|
||||
load src/data/panos/<province>.json
|
||||
re-clip against the rebuilt province outline; drop strays; rewrite bbox/count
|
||||
load each child leaf boundary + precompute its bbox
|
||||
for each pano:
|
||||
candidates = leaves whose bbox contains the point # cheap reject
|
||||
d = first candidate where booleanPointInPolygon # early exit
|
||||
rewrite the file with districts[] / districtCounts / districtCells / unassigned
|
||||
write src/data/regions/counts.js
|
||||
```
|
||||
|
||||
**Cost.** 226k Ha Noi points × 30 leaves is 6.8 M bbox tests (trivial) plus ~1-2
|
||||
`booleanPointInPolygon` calls per point after the reject. Single-digit minutes,
|
||||
which is why the bbox prefilter is not optional.
|
||||
|
||||
**M1 — the shared module.** Phase 2 previously promised a "shared assignment
|
||||
function" with nowhere to put it. Create `scripts/lib/assign-districts.mjs` and
|
||||
import it from both `assign-pano-districts.mjs` and `build-pano-index.mjs`, so
|
||||
the fallback rules cannot drift between two copies.
|
||||
|
||||
**M1 — the stale default list.** `build-pano-index.mjs` ends with
|
||||
`const codes = requested.length ? requested : ['HN','TPHCM','DN','DL','DH'];`
|
||||
and `writeBarrel()` scans the directory rather than a declared list. After Phase
|
||||
1's rename, an argument-less run rebuilds `DL`/`DH`, writes `dl.json`/`dh.json`
|
||||
*beside* `ld.json`/`la.json`, and the barrel silently gains duplicate provinces —
|
||||
`countPanos('VN')` then double-counts ~12k panoramas. Update the default to
|
||||
`['HN','TPHCM','DN','LD','LA']` and make `writeBarrel` reject a filename that is
|
||||
not a known province code.
|
||||
|
||||
**Single-child provinces.** `LD`'s polygon is byte-copied from `DL` in Phase 1
|
||||
precisely so this step is degenerate. Assert `unassigned === 0` for them — a
|
||||
non-zero tally means the byte-copy did not happen and the tolerance mismatch is
|
||||
back.
|
||||
|
||||
### File format
|
||||
|
||||
Header gains `districts`, `districtCounts`, `districtCells`, `unassigned`; each
|
||||
entry gains `d`, an index into `districts`:
|
||||
|
||||
```json
|
||||
"code": "TPHCM",
|
||||
"districts": ["TPHCM-Q1","TPHCM-Q3", ...],
|
||||
"districtCounts": {"TPHCM-Q1": 3211, ...},
|
||||
"districtCells": {"TPHCM-Q1": 274, ...},
|
||||
"unassigned": 412,
|
||||
"panos": [
|
||||
{"id":"1234","lat":10.7712,"lng":106.7003,"d":0},
|
||||
{"id":"5678","lat":10.7799,"lng":106.7101}
|
||||
]
|
||||
```
|
||||
|
||||
**H3 — the size budget was wrong.** `,"d":12` is 8 bytes, not 5. Measured against
|
||||
real entry counts: HN 225,985 + TPHCM 184,992 + DH 11,718 + DN 1,521 + DL 475
|
||||
gives **≈ 2.8-3.4 MB** of growth, against the earlier 2.5 MB gate. **Budget:
|
||||
3.5 MB.** The old "2.1 MB saved" line compared against an alternative that was
|
||||
never on the table and is deleted.
|
||||
|
||||
The one-panorama-per-line writer is preserved: a 226k-entry file must stay
|
||||
diffable.
|
||||
|
||||
### M2 — playability is measured in places, not entries
|
||||
|
||||
`GRID_DEG = 0.0003` (~33 m) means a single street corridor yields dozens of
|
||||
entries, so raw counts overstate distinct *places* by roughly 30×. Measured:
|
||||
Da Nang's 1,521 panoramas occupy **39** distinct 0.01° (~1.1 km) cells across 7
|
||||
districts; Da Lat's 475 occupy **8**.
|
||||
|
||||
`districtCells` records the distinct-cell count per leaf. A node is playable when
|
||||
it has **≥ 3 distinct cells and ≥ 3 panoramas**. The panorama floor matters
|
||||
because `fetchCityPanorama` retries up to `MAX_ATTEMPTS = 3` with a different
|
||||
candidate each time (`mapillary.js:86-91`); a 1-panorama leaf whose image has
|
||||
been deleted upstream throws on attempt 2.
|
||||
|
||||
Leaves between 3 and 10 cells are flagged `thin: true` so Phase 5 can label them
|
||||
rather than silently offering a two-street district.
|
||||
|
||||
### Runtime API
|
||||
|
||||
`src/lib/pano-index.js`:
|
||||
|
||||
| Function | Behaviour |
|
||||
|---|---|
|
||||
| `getRegionPanos(code)` | Leaf → memoised bucket. Province → the whole index. `VN` → never materialised |
|
||||
| `pickRandomPano(code, excludeIds)` | Returns `{ id, lat, lng, regionCode }` — the leaf, or the province when unassigned |
|
||||
| `countPanos(code)` | Works at every level |
|
||||
| `playableRegions()` | Every node meeting the cells-and-count threshold |
|
||||
|
||||
Buckets are built once per province on first leaf access and cached in a
|
||||
module-level `Map`. This is process-global and never invalidated — correct,
|
||||
because the data is static and bundled, but state it plainly: ~425k retained
|
||||
references, bounded, no leak.
|
||||
|
||||
**`VN` draws** never concatenate 424,691 entries. Pick a province uniformly, then
|
||||
delegate. Uniform over panoramas would make Vietnam 53% Ha Noi and 44% Ho Chi
|
||||
Minh, with Da Lat at 0.1%.
|
||||
|
||||
### C2 — `src/lib/mapillary.js` is the missing link
|
||||
|
||||
`/api/new-game` never calls `pickRandomPano`. It calls `fetchCityPanorama`
|
||||
(`new-game/route.js:33`), the sole production caller (`mapillary.js:14,90`),
|
||||
which returns `{...image, lat, lng}` and **discards everything else on the
|
||||
candidate**. Without a change here `session.regionCode` can only ever be the
|
||||
picked code, every district board stays empty forever, and Phase 3's tests still
|
||||
pass because they call `submitScore` directly.
|
||||
|
||||
Two changes:
|
||||
|
||||
1. `fetchCityPanorama(regionCode)` returns `data.regionCode` taken from the
|
||||
**winning** candidate — not the first. The retry loop draws a fresh candidate
|
||||
per attempt, potentially from a different district.
|
||||
2. Move the `pickRandomPano` call inside the `try` (it sits on `mapillary.js:90`,
|
||||
outside the `try` that opens on `:93`), so pool exhaustion returns
|
||||
`{success: false}` instead of escaping to the route's generic 500.
|
||||
|
||||
## Related Code Files
|
||||
|
||||
- Create: `scripts/assign-pano-districts.mjs`
|
||||
- Create: `scripts/lib/assign-districts.mjs` (shared with the index build)
|
||||
- Create: `src/data/regions/counts.js` (generated)
|
||||
- Modify: `src/data/panos/hn.json`, `dn.json`, `tphcm.json`, `ld.json`, `la.json`
|
||||
- Modify: `src/data/panos/index.js` (regenerated; rejects unknown province codes)
|
||||
- Modify: `src/lib/pano-index.js`
|
||||
- Modify: `src/lib/mapillary.js` — propagate `regionCode`; move the draw inside `try`
|
||||
- Modify: `scripts/build-pano-index.mjs` — shared module; default list → `['HN','TPHCM','DN','LD','LA']`
|
||||
- Modify: `tests/pano-index.test.js`
|
||||
|
||||
## Implementation Steps
|
||||
|
||||
1. Write `scripts/lib/assign-districts.mjs`: bbox prefilter, early exit, the
|
||||
nearest-centroid fallback for stranded points, and the cell counter.
|
||||
2. Write `scripts/assign-pano-districts.mjs` around it, including the re-clip
|
||||
pass and the header `bbox`/`count` rewrite (H7).
|
||||
3. Run it. Check the unassigned tally: above 2% for a province means Phase 1's
|
||||
leaf tolerance is too loose — tighten and re-run, no network cost.
|
||||
4. Verify single-child provinces report `unassigned === 0`.
|
||||
5. Record leaves below the playability threshold. Da Nang's rural leaves and
|
||||
`TPHCM-CUCHI` (zero panoramas, unresolved boundary) are expected.
|
||||
6. Emit `src/data/regions/counts.js`.
|
||||
7. Update `build-pano-index.mjs`: shared module, corrected default list, barrel
|
||||
guard against unknown province files.
|
||||
8. Rewrite `src/lib/pano-index.js` per the table above.
|
||||
9. Update `src/lib/mapillary.js` for C2.
|
||||
10. Extend `tests/pano-index.test.js`.
|
||||
11. `npm test`, then `npm run build:check`.
|
||||
|
||||
## Validation
|
||||
|
||||
- Per-province unassigned tally under 2%, asserted against the header field.
|
||||
- `sum(districtCounts) + unassigned === panos.length` for every province.
|
||||
- Single-child provinces: `unassigned === 0`.
|
||||
- Panoramas assigned to leaf L are inside L's boundary, sampled at the existing
|
||||
~200-point stride.
|
||||
- The two containment assertions in `tests/pano-index.test.js` pass **unweakened**
|
||||
after the re-clip.
|
||||
- `countPanos('VN')` equals the sum of the five provinces — and does not
|
||||
double-count, verified by asserting `PANO_INDEXES` has exactly five keys.
|
||||
- 200 draws at `VN` hit at least four distinct provinces.
|
||||
- 100 draws at a leaf all report that leaf as `regionCode`.
|
||||
- A `VN` draw's `regionCode` is a leaf code, never `'VN'`.
|
||||
- `fetchCityPanorama` returns the `regionCode` of the attempt that succeeded:
|
||||
force attempt 1 to fail and assert the reported leaf matches attempt 2's
|
||||
candidate.
|
||||
- Pool exhaustion returns `{success: false}`, not a thrown error.
|
||||
- `src/data/panos/` grows by less than 3.5 MB.
|
||||
|
||||
## Risk Assessment
|
||||
|
||||
**Gaps between simplified district outlines strand panoramas.** *Signal:*
|
||||
unassigned above 2%. *Response:* tighten Phase 1's leaf tolerance and re-run —
|
||||
free, no network. Residual strays fall back to the nearest leaf centroid rather
|
||||
than staying province-only.
|
||||
|
||||
**Point-in-polygon is slower than estimated.** *Signal:* the Ha Noi pass exceeds
|
||||
~10 minutes. *Response:* a coarse grid index over leaf bboxes. Build-time only,
|
||||
no runtime effect.
|
||||
|
||||
**The re-clip drops more panoramas than expected.** *Signal:* a province's count
|
||||
falls by more than a few percent. *Response:* that is the HN union changing
|
||||
shape; inspect whether the union is missing a district before accepting the loss.
|
||||
|
||||
**A leaf passes the threshold but is still one street.** *Signal:* `districtCells`
|
||||
between 3 and 10. *Response:* the `thin` flag exists for exactly this; Phase 5
|
||||
labels it rather than hiding it.
|
||||
|
||||
**`build-pano-index.mjs` is run argument-less and resurrects the old files.**
|
||||
*Signal:* `PANO_INDEXES` gains `DL`/`DH` keys. *Response:* the barrel guard
|
||||
rejects unknown province filenames; the five-key assertion catches it in tests.
|
||||
|
||||
## Success Criteria
|
||||
|
||||
- [ ] All five province indexes carry `districts`, `districtCounts`, `districtCells`, `unassigned`, and per-entry `d`
|
||||
- [ ] Unassigned under 2% per province; zero for single-child provinces
|
||||
- [ ] The re-clip pass ran and the containment assertions pass unweakened
|
||||
- [ ] Zero new Mapillary requests
|
||||
- [ ] `pickRandomPano` works at leaf, province, and country level and reports the resolving leaf
|
||||
- [ ] `fetchCityPanorama` propagates the **winning** attempt's leaf
|
||||
- [ ] A `VN` draw is province-uniform, not panorama-uniform
|
||||
- [ ] `playableRegions()` gates on distinct cells and panorama count, not `count > 0`
|
||||
- [ ] `PANO_INDEXES` has exactly five keys
|
||||
- [ ] `src/data/regions/counts.js` generated
|
||||
- [ ] `src/data/panos/` growth under 3.5 MB
|
||||
- [ ] `npm test` and `npm run build:check` pass
|
||||
@@ -0,0 +1,268 @@
|
||||
---
|
||||
phase: 3
|
||||
title: "Leaderboard fan-out and migration"
|
||||
status: todo
|
||||
priority: P1
|
||||
effort: "1.5d"
|
||||
dependencies: [1]
|
||||
---
|
||||
|
||||
# Phase 3: Leaderboard fan-out and migration
|
||||
|
||||
## Overview
|
||||
|
||||
Make a single guess credit its leaf, its province, and Vietnam — without moving,
|
||||
renaming, or resetting a single existing key. Then seed the two brand-new province
|
||||
nodes from the children whose history they inherit.
|
||||
|
||||
This is the phase where data can be lost. Two red-team findings here were about
|
||||
the *recovery* story being worse than the failure it recovers from — read H2
|
||||
before writing the migration.
|
||||
|
||||
## Requirements
|
||||
|
||||
**Functional**
|
||||
- `submitScore` and `submitDistanceRecord` write to every ancestor of the region
|
||||
passed in.
|
||||
- `getLeaderboard` accepts any region code, validated against the tree.
|
||||
- `VN` reads and writes the existing `leaderboard:vietnam` / `distance:vietnam`.
|
||||
- `leaderboard:city:ld` seeded from `:dl`, `:la` from `:dh` — score and distance.
|
||||
- Every pre-existing key keeps its exact contents.
|
||||
|
||||
**Non-functional**
|
||||
- The migration is idempotent, defaults to dry-run, and **aborts if its export
|
||||
contains zero keys**.
|
||||
- It prints the resolved `KEY_PREFIX` before touching anything.
|
||||
- Validation lives in the library, not only in the routes.
|
||||
|
||||
## Architecture
|
||||
|
||||
### C4 — Key names in this plan are *logical*
|
||||
|
||||
`src/lib/upstash.js:12` states it: "every physical Upstash key carries
|
||||
KEY_PREFIX". `DEFAULT_KEY_PREFIX = 'vngeoguessr:'` (`:17`), overridable by
|
||||
`process.env.KEY_PREFIX` (`:34`), applied inside `pkey()` (`:40-42`). Callers
|
||||
never see it.
|
||||
|
||||
Every key name written in this plan is logical. The physical form is
|
||||
`${KEY_PREFIX}${logical}`. A migration script that reaches past the adapter and
|
||||
issues `ZRANGE leaderboard:city:dl` reads an **empty namespace**, reports zero
|
||||
members, and "succeeds" having done nothing.
|
||||
|
||||
### C4 — The export needs a primitive that does not exist
|
||||
|
||||
The adapter exposes ten commands — `getJson`, `putJson`, `del`, `zAdd`, `zScore`,
|
||||
`zRangeWithScores`, `zRank`, `zRevRank`, `zRemRangeByRank`, plus `getUpstash`.
|
||||
There is **no** `scan` and no `keys`. The earlier draft made a full export the
|
||||
precondition for any write while restricting `upstash.js` to "namespace comment
|
||||
only" — a contradiction.
|
||||
|
||||
Add `scanKeys(h, pattern)` as a real deliverable: prefix-aware, stripping the
|
||||
prefix on return, mirrored in `tests/fake-upstash-redis.js` (which documents
|
||||
itself as covering "nine commands" and must grow to match).
|
||||
|
||||
### Why the key names do not change
|
||||
|
||||
```
|
||||
leaderboard:vietnam sorted set (score)
|
||||
leaderboard:city:{regionCode} sorted set (score)
|
||||
distance:vietnam sorted set (distance)
|
||||
distance:city:{regionCode} sorted set (distance)
|
||||
```
|
||||
|
||||
Renaming `:city:` to `:region:` would read better and strand every existing key.
|
||||
`getCityLeaderboardKey` becomes `getRegionLeaderboardKey`, still emitting
|
||||
`leaderboard:city:<lowercased code>`, with a comment recording why the segment
|
||||
says `city`. `VN` special-cases to the two legacy country keys.
|
||||
|
||||
| Node | Score key (logical) | Distance key (logical) |
|
||||
|---|---|---|
|
||||
| `VN` | `leaderboard:vietnam` | `distance:vietnam` |
|
||||
| `TPHCM` | `leaderboard:city:tphcm` | `distance:city:tphcm` |
|
||||
| `TPHCM-Q7` | `leaderboard:city:tphcm-q7` | `distance:city:tphcm-q7` |
|
||||
| `LD` | `leaderboard:city:ld` | `distance:city:ld` |
|
||||
| `DL` | `leaderboard:city:dl` | `distance:city:dl` |
|
||||
|
||||
67 nodes × 2 = 134 sorted sets at full occupancy, each capped at 200 entries.
|
||||
|
||||
### Fan-out
|
||||
|
||||
```js
|
||||
export async function submitScore(username, score, regionCode) {
|
||||
const chain = ancestorsOf(regionCode); // ['TPHCM-Q7','TPHCM','VN']
|
||||
// read every current total, add, write back, trim — as today, per level
|
||||
}
|
||||
```
|
||||
|
||||
**C6 — validate in the library.** `getCityLeaderboardKey` currently
|
||||
string-concatenates a lowercased caller value (`leaderboard.js:19-21`) and
|
||||
`/api/leaderboard` passes `searchParams.get('city')` straight through
|
||||
(`leaderboard/route.js:7-12`). Route-level validation is not enough: the
|
||||
migration script and every future caller bypass routes. Validate `regionCode`
|
||||
against `REGIONS` **inside** `submitScore`, `submitDistanceRecord`, and
|
||||
`getLeaderboard`, and throw on an unknown code rather than creating a key for a
|
||||
typo. Clamp `limit` to `[1, 200]` (today `parseInt(...) || 100` accepts `-1`,
|
||||
which reaches `zRangeWithScores(h, key, 0, -2)`).
|
||||
|
||||
Return shape becomes a `levels` array plus named aliases:
|
||||
|
||||
```js
|
||||
{
|
||||
success: true,
|
||||
levels: [
|
||||
{ code: 'TPHCM-Q7', name: 'District 7', score: 12, rank: 4 },
|
||||
{ code: 'TPHCM', name: 'Ho Chi Minh', score: 88, rank: 11 },
|
||||
{ code: 'VN', name: 'Vietnam', score: 402, rank: 37 },
|
||||
],
|
||||
district: { ... }, province: { ... }, global: { ... },
|
||||
}
|
||||
```
|
||||
|
||||
`global` keeps its current meaning so `GameClient`'s existing handling survives
|
||||
until Phase 5.
|
||||
|
||||
For distances, generate the entry id `username:distance:timestamp` **once** and
|
||||
write the same id to all three levels, so a record is identifiable across them.
|
||||
|
||||
### H1 — The guess handler is not idempotent, and the earlier claim was false
|
||||
|
||||
The earlier draft said "the three writes already run under `Promise.all`; a
|
||||
rejection surfaces as a 500 and the guess is not silently half-recorded". Both
|
||||
halves are wrong. `Promise.all` does not roll back — resolved writes are
|
||||
committed. And `/api/guess` runs `submitScore` (`:65`), then
|
||||
`submitDistanceRecord` (`:68`), and deletes the session only at `:83`. A failure
|
||||
in the second leaves the score written *and* the session replayable for up to 30
|
||||
minutes, so a retry double-credits all three levels.
|
||||
|
||||
Fan-out widens this from 2 keys to 6. Phase 4 owns the fix (consume the session
|
||||
before the writes); this phase owns deleting the false claim and adding the
|
||||
matching test.
|
||||
|
||||
**Not fixed here, recorded:** `submitScore` reads with `zScore` then writes with
|
||||
`zAdd` rather than `ZINCRBY`, so concurrent guesses by one player can lose an
|
||||
increment. Pre-existing. Fixing it means adding `zIncrBy` to the adapter — a
|
||||
clean, separable follow-up, as is pipelining the now-21 REST round trips per
|
||||
guess.
|
||||
|
||||
### H2 — Migration: ordering, replacement, and a recovery story that destroyed data
|
||||
|
||||
Three corrections to the earlier draft.
|
||||
|
||||
**Ordering is now pinned: deploy the fan-out first, then migrate.** The earlier
|
||||
draft left it undefined. Migrating first opens a window where Da Lat guesses
|
||||
credit `:dl` and `leaderboard:vietnam` but not `:ld`, permanently shorting the
|
||||
new province — and the script was documented as one-off, so nothing re-runs.
|
||||
Deploying first is safe because a pre-migration `:ld` only accumulates a subset
|
||||
of `:dl`'s deltas, which the copy then overwrites.
|
||||
|
||||
**Copy is not replace.** Writing each source member into the destination leaves
|
||||
any destination-only member behind with a stale score — reachable because the
|
||||
200-cap (`leaderboard.js:16,115-116`) can trim a player out of `:dl` while they
|
||||
persist in `:ld`. The script `DEL`s the destination inside the same run before
|
||||
copying.
|
||||
|
||||
**The reconciliation suggestion is deleted.** The earlier draft offered "a
|
||||
reconciliation script can rebuild a province from its children later". That
|
||||
directly contradicts this plan's own Key Decision and Goal 3: province sets hold
|
||||
legacy history with *no* corresponding district entries, so rebuilding from
|
||||
children **deletes every point earned before this change**. If drift is ever
|
||||
reconciled, the formula is `province = legacy baseline snapshot + sum(children)`,
|
||||
and `migrate-leaderboards.mjs` must capture that baseline snapshot as a committed
|
||||
artefact.
|
||||
|
||||
`scripts/migrate-leaderboards.mjs`:
|
||||
|
||||
1. Print the resolved `KEY_PREFIX`. Export every `leaderboard:*` and `distance:*`
|
||||
key via `scanKeys`, with members and scores, to a timestamped JSON file.
|
||||
**Abort if the export contains zero keys** — an empty export is a wrong-prefix
|
||||
symptom, not a clean slate.
|
||||
2. Write the legacy baseline snapshot for `HN`, `DN`, `TPHCM` alongside it.
|
||||
3. `DEL` destination, then copy `:dl` → `:ld` and `:dh` → `:la` with **absolute
|
||||
scores**, score and distance both.
|
||||
4. Report per-key member counts before and after. Abort if either source is empty.
|
||||
5. `--dry-run` is the default; `--apply` is explicit; `--confirm-prefix=<value>`
|
||||
is required so a wrong-namespace run is impossible.
|
||||
|
||||
Only `LD` and `LA` are seeded. `HN`, `DN`, and `TPHCM` keep their history where
|
||||
it is.
|
||||
|
||||
## Related Code Files
|
||||
|
||||
- Modify: `src/lib/leaderboard.js` — fan-out, `VN` mapping, library-level validation, return shape
|
||||
- Modify: `src/lib/upstash.js` — add `scanKeys`; update the namespace comment
|
||||
- Modify: `tests/fake-upstash-redis.js` — mirror `scanKeys`
|
||||
- Create: `scripts/migrate-leaderboards.mjs`
|
||||
- Modify: `tests/leaderboard.test.js`
|
||||
|
||||
## Implementation Steps
|
||||
|
||||
1. Add `scanKeys(h, pattern)` to the adapter and the fake.
|
||||
2. Replace the key helpers with region-aware versions; special-case `VN`.
|
||||
Comment why the key segment still reads `city`.
|
||||
3. Rewrite `submitScore` to resolve `ancestorsOf(regionCode)` and apply the
|
||||
existing read-add-write-trim per level. Validate the code against `REGIONS`
|
||||
first.
|
||||
4. Same for `submitDistanceRecord`, generating the entry id once.
|
||||
5. Extend `getLeaderboard` to any validated code; clamp `limit` to `[1, 200]`.
|
||||
6. Delete the false `Promise.all` claim from the risk notes; add the
|
||||
replay/double-credit test.
|
||||
7. Write `scripts/migrate-leaderboards.mjs` per H2.
|
||||
8. Extend `tests/leaderboard.test.js`.
|
||||
9. `npm test`, then `npm run test:integration` (`npm run redis:up` first) — this
|
||||
phase touches persistence, so the fake alone is not enough.
|
||||
|
||||
## Validation
|
||||
|
||||
- A guess at `TPHCM-Q7` worth 3 points raises `leaderboard:city:tphcm-q7`,
|
||||
`:tphcm`, and `leaderboard:vietnam` by exactly 3 each.
|
||||
- A guess at `DL` raises `:dl`, `:ld`, and `leaderboard:vietnam`.
|
||||
- A province-level guess on an unassigned panorama raises two levels, not three.
|
||||
- `getLeaderboard('VN')` and `getLeaderboard(null)` return the same rows.
|
||||
- Unknown region code throws from the **library**, not just the route; no key created.
|
||||
- `limit=-1` and `limit=99999` both clamp.
|
||||
- Migration with a wrong `--confirm-prefix` refuses to run.
|
||||
- Migration against an empty namespace aborts instead of reporting success.
|
||||
- Dry-run writes nothing and reports the intended copies.
|
||||
- Applied twice, scores are identical; a destination-only orphan is removed.
|
||||
- Every key present before the migration is present after with identical members
|
||||
and scores — diff the export against a fresh export.
|
||||
- A replayed guess (session still alive) does not double-credit.
|
||||
|
||||
## Risk Assessment
|
||||
|
||||
**The migration runs against the wrong namespace.** *Signal:* the export is empty
|
||||
or the printed prefix is unexpected. *Response:* the zero-key abort and
|
||||
`--confirm-prefix` make this impossible to do silently. This was the most likely
|
||||
way to lose data and now fails closed.
|
||||
|
||||
**The migration corrupts live leaderboards.** *Signal:* post-run export differs
|
||||
outside the four seeded keys. *Response:* the export is the rollback; restore
|
||||
from it. Never `--apply` without a successful non-empty export in the same run.
|
||||
|
||||
**Someone reconciles a province from its children.** *Signal:* a proposal to
|
||||
"rebuild `:tphcm` from its 22 districts". *Response:* that deletes all pre-change
|
||||
history. Only `legacy baseline + sum(children)` is valid, and the baseline is the
|
||||
artefact step 2 commits.
|
||||
|
||||
**Fan-out partially fails.** *Signal:* a 500 from `/api/guess` with some levels
|
||||
written. *Response:* Phase 4 consumes the session before writing, so a retry
|
||||
cannot double-credit. Residual single-level drift is accepted; leaf totals are
|
||||
the finer-grained record.
|
||||
|
||||
**Trimming interacts with three levels.** A player visible on their district board
|
||||
may be absent from the country board once it exceeds 200. Correct behaviour — it
|
||||
already happens between city and global today. Recorded so it is not mistaken for
|
||||
a fan-out bug.
|
||||
|
||||
## Success Criteria
|
||||
|
||||
- [ ] One guess writes all ancestor levels, score and distance both
|
||||
- [ ] `VN` maps to `leaderboard:vietnam` / `distance:vietnam`
|
||||
- [ ] `scanKeys` exists in both the adapter and the fake
|
||||
- [ ] Region codes and `limit` are validated inside the library
|
||||
- [ ] `leaderboard:city:ld` equals `:dl`, and `:la` equals `:dh`
|
||||
- [ ] Every pre-existing key is unchanged after the migration
|
||||
- [ ] The migration prints its prefix, aborts on an empty export, requires `--confirm-prefix`, and `DEL`s before copying
|
||||
- [ ] The legacy baseline snapshot for HN/DN/TPHCM is committed
|
||||
- [ ] Migration ordering is documented as deploy-then-migrate
|
||||
- [ ] `npm test` and `npm run test:integration` pass
|
||||
@@ -0,0 +1,214 @@
|
||||
---
|
||||
phase: 4
|
||||
title: "API surface"
|
||||
status: todo
|
||||
priority: P1
|
||||
effort: "0.75d"
|
||||
dependencies: [2, 3]
|
||||
---
|
||||
|
||||
# Phase 4: API surface
|
||||
|
||||
## Overview
|
||||
|
||||
Carry the region tree through the route handlers. The session becomes the place
|
||||
where "what the player picked" and "where the panorama actually is" are both
|
||||
recorded — the second is what scoring fans out from, and **neither the client nor
|
||||
any response may see it before the guess**.
|
||||
|
||||
No `/api/regions` route: the tree and its counts are two small generated files
|
||||
the client imports directly (H5).
|
||||
|
||||
## Requirements
|
||||
|
||||
**Functional**
|
||||
- `/api/new-game` accepts any playable region code and stores both the picked
|
||||
region and the resolved leaf.
|
||||
- `/api/guess` fans out from the session's resolved leaf, never from client input.
|
||||
- `/api/leaderboard` serves any validated region code.
|
||||
- `/api/debug/city-coverage` serves any region with a boundary.
|
||||
- The session is consumed before the leaderboard writes, so a retry cannot
|
||||
double-credit (H1).
|
||||
|
||||
**Non-functional**
|
||||
- `?city=` keeps working everywhere it works today. `/game?location=HN` and
|
||||
`?location=DL` must not break.
|
||||
- `session.regionCode` joins `exactLocation` on the never-serialized list.
|
||||
|
||||
## Architecture
|
||||
|
||||
### Session shape
|
||||
|
||||
```js
|
||||
{
|
||||
sessionId,
|
||||
pickedRegion: 'TPHCM', // what the player chose — safe to echo
|
||||
regionCode: 'TPHCM-Q7', // SECRET: where the panorama is
|
||||
exactLocation: { lat, lng },// SECRET
|
||||
imageId,
|
||||
scored: false,
|
||||
createdAt,
|
||||
}
|
||||
```
|
||||
|
||||
`cityCode` is gone. Sessions live 30 minutes, so a deploy strands at most half an
|
||||
hour of in-flight games: `/api/guess` reads `session.regionCode ?? session.cityCode`
|
||||
so an old session still scores, at province level, instead of throwing. Remove
|
||||
that fallback in a later release, and cover it with a test using a hand-crafted
|
||||
old-shape session so it is not silently dropped.
|
||||
|
||||
### C6 — Two secrets, not one
|
||||
|
||||
The earlier draft guarded only `exactLocation`. `session.regionCode` names the
|
||||
district the panorama is in; leaking it collapses a `VN` game from ~330,000 km²
|
||||
to one district — reliably inside the 1 km band, often inside 200 m.
|
||||
|
||||
Two concrete leak paths, both closed here:
|
||||
|
||||
1. **`POST /api/new-game`** (the session-lookup path) returns
|
||||
`{ sessionId, cityCode, createdAt }` today (`new-game/route.js:111-119`). The
|
||||
mechanical rename `cityCode` → `regionCode` publishes the answer. It returns
|
||||
**`pickedRegion` only**.
|
||||
2. **The GET response's `region` block.** It carries `pickedRegion`'s code, name,
|
||||
and ancestor path — never the resolved leaf. For a `VN` game the path is
|
||||
`['Vietnam']`, not `['Vietnam','Ho Chi Minh','District 7']`.
|
||||
|
||||
Both get an explicit test: no response body before the guess may contain the
|
||||
value of `session.regionCode`.
|
||||
|
||||
### `/api/new-game`
|
||||
|
||||
```
|
||||
GET /api/new-game?region=TPHCM-Q7[&sessionId=]
|
||||
GET /api/new-game?city=TPHCM # still accepted
|
||||
```
|
||||
|
||||
Resolve through the tree, reject an unknown or non-playable code with 400, draw
|
||||
via `fetchCityPanorama(code)` — which after Phase 2 returns the winning
|
||||
candidate's `regionCode` — and store both values.
|
||||
|
||||
### C2 — the draw path
|
||||
|
||||
`/api/new-game` does not call `pickRandomPano`; `src/lib/mapillary.js` does, and
|
||||
Phase 2 is where it starts propagating the leaf. This phase only consumes it. If
|
||||
`imageResult.data.regionCode` is absent, that is a Phase 2 regression — fail loudly
|
||||
rather than defaulting to the picked region, which would silently empty every
|
||||
district board.
|
||||
|
||||
### `/api/guess` — H1
|
||||
|
||||
Order changes. Today: `submitScore` (`:65`) → `submitDistanceRecord` (`:68`) →
|
||||
`deleteGameSession` (`:83`). A failure in the second leaves the score written and
|
||||
the session replayable.
|
||||
|
||||
New order: mark the session consumed (or delete it) **first**, then write. A
|
||||
replay finds no live session and is rejected. Losing a guess to a mid-write
|
||||
failure is strictly better than double-crediting six keys.
|
||||
|
||||
The response's `gameResult` grows from two ranks to Phase 3's `levels` array,
|
||||
keeping `globalRank` as an alias until Phase 5.
|
||||
|
||||
### `/api/leaderboard`
|
||||
|
||||
```
|
||||
GET /api/leaderboard?region=TPHCM-Q7&type=score&limit=100
|
||||
GET /api/leaderboard?city=TPHCM # still accepted
|
||||
GET /api/leaderboard # country
|
||||
```
|
||||
|
||||
Validate against the tree and 400 on an unknown code — today an unknown value
|
||||
silently returns an empty board, indistinguishable from a real empty one. Phase 3
|
||||
also validates inside the library, since the route is not the only caller.
|
||||
|
||||
### H5 — no `/api/regions`
|
||||
|
||||
The earlier draft added a route so the client could get per-leaf counts. Its own
|
||||
justification conceded the tree is safe to bundle, and Phase 1's reason for
|
||||
withholding counts (two writers on one file) dissolved once Phase 2 emits a
|
||||
separate `src/data/regions/counts.js`.
|
||||
|
||||
So: the client imports `src/lib/regions.js`, which imports the two generated
|
||||
region files and nothing else. No route, no fetch, no loading state, no cache —
|
||||
and the home page keeps rendering its picker on first paint, as it does today
|
||||
from `cities` (`page.js:14`).
|
||||
|
||||
### `/api/debug/city-coverage`
|
||||
|
||||
Accept `?region=`, keep `?city=`. A leaf serves its own boundary and panoramas; a
|
||||
province serves the union and all of them. Viewport filtering, even sampling, and
|
||||
boundary-once behaviour unchanged. Rename the directory to `region-coverage` and
|
||||
update the single caller (`src/app/debug/coverage/page.js:59`).
|
||||
|
||||
## Related Code Files
|
||||
|
||||
- Modify: `src/app/api/new-game/route.js`
|
||||
- Modify: `src/app/api/guess/route.js`
|
||||
- Modify: `src/app/api/leaderboard/route.js`
|
||||
- Modify: `src/app/api/debug/city-coverage/route.js` → `.../debug/region-coverage/route.js`
|
||||
- Modify: `src/lib/session.js` — JSDoc only; the store is shape-agnostic
|
||||
- Modify: `tests/session.test.js` — fixtures use `regionCode` / `pickedRegion`
|
||||
|
||||
## Implementation Steps
|
||||
|
||||
1. Add a shared `resolvePlayableRegion(searchParams)` helper: read `region`, fall
|
||||
back to `city`, uppercase, validate against the tree and `playableRegions()`,
|
||||
return the node or a 400-shaped error. Use it in all four routes.
|
||||
2. Update `/api/new-game` GET: draw, store `pickedRegion` + `regionCode`, return
|
||||
the `region` block built from `pickedRegion` only.
|
||||
3. Update `/api/new-game` POST: return `pickedRegion`, never `regionCode`.
|
||||
4. Update `/api/guess`: consume the session first, then fan out from
|
||||
`session.regionCode ?? session.cityCode`; widen `gameResult` with `levels`.
|
||||
5. Update `/api/leaderboard`: validated region, 400 on unknown.
|
||||
6. Rename the coverage route directory; update its one caller.
|
||||
7. Update `tests/session.test.js` fixtures; add the old-shape-session test.
|
||||
8. `npm test`, then `npm run build:check`.
|
||||
|
||||
## Validation
|
||||
|
||||
- `GET /api/new-game?region=TPHCM-Q7` stores `regionCode: 'TPHCM-Q7'`.
|
||||
- `GET /api/new-game?region=VN` succeeds; the stored `regionCode` is a **leaf**,
|
||||
and the response body contains it **nowhere**.
|
||||
- `POST /api/new-game` with a `VN` session returns no leaf code and no
|
||||
`exactLocation`.
|
||||
- `GET /api/new-game?city=HN` behaves as before.
|
||||
- `GET /api/new-game?region=NOPE` and `?region=<sub-threshold leaf>` return 400,
|
||||
not 500, naming the region.
|
||||
- A guess against a `VN` session credits three server-chosen levels.
|
||||
- Posting a mismatched region in the guess body changes nothing about which keys
|
||||
move — the anti-cheat property the design rests on.
|
||||
- A replayed guess after a mid-write failure does not double-credit.
|
||||
- An old-shape session (`cityCode`, no `regionCode`) still scores at province level.
|
||||
- `GET /api/leaderboard?region=VN` matches `GET /api/leaderboard`.
|
||||
- No route imports `src/lib/pano-index.js` into anything a client component reaches.
|
||||
|
||||
## Risk Assessment
|
||||
|
||||
**A pre-guess response leaks the leaf.** *Signal:* any response body containing
|
||||
`session.regionCode`. *Response:* the explicit test above runs against both
|
||||
`/api/new-game` verbs. This is the highest-value test in the phase.
|
||||
|
||||
**In-flight sessions break across deploy.** *Signal:* `/api/guess` 500s on
|
||||
sessions created before the deploy. *Response:* the `?? session.cityCode`
|
||||
fallback, covered by a test so it survives refactors.
|
||||
|
||||
**Consuming the session first loses a guess on a write failure.** *Signal:* a
|
||||
player reports a scored round that did not count. *Response:* accepted trade —
|
||||
one lost guess beats six double-credited keys. The 500 is visible to the player.
|
||||
|
||||
**Renaming the coverage route breaks the debug page.** *Signal:* 404 on
|
||||
`/debug/coverage`. *Response:* one caller at `page.js:59`; update in the same
|
||||
commit. Note that `src/app/debug/coverage/page.js` could not be read during
|
||||
planning (a context hook denies paths matching `coverage`), so confirm its
|
||||
contents at implementation time rather than trusting this description.
|
||||
|
||||
## Success Criteria
|
||||
|
||||
- [ ] All four routes accept region codes at any level
|
||||
- [ ] `?city=` still works everywhere it works today
|
||||
- [ ] The session stores both the picked region and the resolved leaf
|
||||
- [ ] No pre-guess response exposes `regionCode` or `exactLocation`
|
||||
- [ ] Scoring reads the region from the session, never from the request
|
||||
- [ ] The session is consumed before the leaderboard writes
|
||||
- [ ] Unknown or non-playable codes return 400 with a useful message
|
||||
- [ ] No `/api/regions` route exists; the client imports the generated tree
|
||||
- [ ] `npm test` and `npm run build:check` pass
|
||||
@@ -0,0 +1,216 @@
|
||||
---
|
||||
phase: 5
|
||||
title: "UI region navigation"
|
||||
status: todo
|
||||
priority: P1
|
||||
effort: "1.5-2d"
|
||||
dependencies: [4]
|
||||
---
|
||||
|
||||
# Phase 5: UI region navigation
|
||||
|
||||
## Overview
|
||||
|
||||
A flat list of five cards and five leaderboard tabs does not survive 67 nodes.
|
||||
Both need to become navigable, and the leaderboard modal has to stop fetching
|
||||
everything at once — at 67 nodes that is 134 requests on one click.
|
||||
|
||||
## Dependency change — apply manually
|
||||
|
||||
`src/components/ui/` holds nine primitives: `alert`, `badge`, `button`, `card`,
|
||||
`dialog`, `input`, `label`, `skeleton`, `tabs`. There is no accordion and no
|
||||
select, and `package.json` carries only `@radix-ui/react-dialog`, `react-label`,
|
||||
`react-slot`, `react-tabs`.
|
||||
|
||||
**Add to `package.json`:**
|
||||
|
||||
```
|
||||
@radix-ui/react-accordion
|
||||
@radix-ui/react-select
|
||||
```
|
||||
|
||||
Per `docs/development.md`, configuration changes are flagged for manual
|
||||
processing — this phase cannot start until they are installed. The alternative
|
||||
(hand-rolling an accessible accordion and a grouped listbox with roving focus and
|
||||
`aria-expanded`) was rejected: the realistic outcome is `div`-based toggles with
|
||||
broken keyboard support, verified only by eye.
|
||||
|
||||
Generate the shadcn wrappers into `src/components/ui/accordion.jsx` and
|
||||
`select.jsx` so they match the existing vocabulary.
|
||||
|
||||
## Requirements
|
||||
|
||||
**Functional**
|
||||
- Pick any node to play: Vietnam, a province, or a leaf.
|
||||
- Leaderboards browsable at every level.
|
||||
- A result shows all three credited levels.
|
||||
- The game header shows what the player picked; the result reveals where the
|
||||
panorama actually was.
|
||||
- Lam Dong and Long An show their partial-coverage label.
|
||||
- Sub-threshold leaves are visible but not playable; `thin` leaves are labelled.
|
||||
|
||||
**Non-functional**
|
||||
- The leaderboard modal fetches only the node being viewed — 2 requests, not 134.
|
||||
- No horizontal scrolling on mobile. `TabsList` currently uses
|
||||
`repeat(cities.length + 1, ...)` (`page.js:246`), unusable at 67 columns.
|
||||
- The region tree is imported, not fetched (Phase 4 removed `/api/regions`).
|
||||
|
||||
## Architecture
|
||||
|
||||
### Region picker (home page)
|
||||
|
||||
A two-level accordion, one row per province:
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────────┐
|
||||
│ Play anywhere in Vietnam 424,691 › │ ← country CTA, always first
|
||||
├────────────────────────────────────────────┤
|
||||
│ Ha Noi 30 districts ⌄ │
|
||||
│ Ho Chi Minh 22 districts ⌄ │
|
||||
│ Da Nang 7 districts ⌄ │
|
||||
│ Lam Dong partial · 1 town ⌄ │
|
||||
│ Long An partial · 1 town ⌄ │
|
||||
└────────────────────────────────────────────┘
|
||||
expanded ⌄
|
||||
├─ Play anywhere in Ha Noi ›
|
||||
├─ Ba Dinh 4,102 ›
|
||||
├─ Hoan Kiem thin 1,880 ›
|
||||
├─ My Duc no coverage ← disabled
|
||||
└─ …
|
||||
```
|
||||
|
||||
The province row plays the whole province; only the chevron expands. Counts and
|
||||
playability come from `src/data/regions/counts.js` via `src/lib/regions.js` — a
|
||||
static import, no network.
|
||||
|
||||
Links become `/game?region=<code>`; `?location=` keeps working.
|
||||
|
||||
**Vietnam scores zero.** The user accepted this: `calculateScore` returns 0 above
|
||||
1 km and the covered provinces span ~1,100 km. It ships as an exploration mode.
|
||||
Do not add a per-level band scale, and do not quietly demote the CTA — but the
|
||||
label should set expectations rather than promise points.
|
||||
|
||||
### Leaderboard modal
|
||||
|
||||
Tabs stop being one-per-node. Two controls:
|
||||
|
||||
- A **level** row: `Vietnam` · `Province` · `District`
|
||||
- A **region select** (Radix Select) listing nodes at that level, grouped by
|
||||
parent for districts
|
||||
|
||||
`score`/`distance` stays as the existing vertical toggle. Fetch on selection
|
||||
change, keyed `${region}-${type}` into the existing `leaderboards` state. The
|
||||
`fetchIdRef` guard (`page.js:32,50,75,83`) carries over unchanged — it is exactly
|
||||
the guard a per-selection fetch needs.
|
||||
|
||||
`fetchAllLeaderboards` (`page.js:49,91`) is deleted. Eager fetching was only ever
|
||||
viable at five cities.
|
||||
|
||||
Clear the cache when the modal **opens**, keeping it only for within-session tab
|
||||
switching, so a player who just scored does not see a stale board.
|
||||
|
||||
### Game screen
|
||||
|
||||
`GameClient` reads `cityNames[location]` in five places (`:388, 412, 557, 576,
|
||||
594`) plus `CITIES[location]?.bbox` (`:463`) and `cityCenters` (`:102`). Replace
|
||||
with `regionPath(code)` from `src/lib/regions.js` — one helper, called from all
|
||||
three render sites, not string-built at each.
|
||||
|
||||
- Header while guessing: what the player picked — `Ho Chi Minh`, or
|
||||
`Vietnam · anywhere`.
|
||||
- Result modal: where it actually was — `Vietnam › Ho Chi Minh › District 7`.
|
||||
This is the interesting reveal for province and country games, and it is only
|
||||
safe **after** the guess (Phase 4 keeps it out of every pre-guess response).
|
||||
- Rank rows: one per credited level from `levels`, replacing the hardcoded
|
||||
global/city pair (`:45-50, 189-192, 559, 566, 574-583`).
|
||||
|
||||
The map fits the picked region's bbox, so a district game opens zoomed to the
|
||||
district — a real gameplay improvement that falls out of the tree.
|
||||
|
||||
### Debug coverage page
|
||||
|
||||
Its `cities` dropdown becomes the same level + region select pair, and its fetch
|
||||
moves to `/api/debug/region-coverage?region=`.
|
||||
|
||||
**Caveat:** `src/app/debug/coverage/page.js` could not be read during planning or
|
||||
by any red-team reviewer — a context hook denies paths matching `coverage`. Only
|
||||
two touch points are grep-confirmed: the `cities` import (`:10`) and the fetch
|
||||
URL (`:59`). Open the file first and adjust this section to what is actually there.
|
||||
|
||||
## Related Code Files
|
||||
|
||||
- Modify: `package.json` — two Radix packages (**manual**)
|
||||
- Create: `src/components/ui/accordion.jsx`, `src/components/ui/select.jsx`
|
||||
- Modify: `src/app/page.js` — region picker, lazy leaderboard modal
|
||||
- Create: `src/app/components/RegionPicker.js`
|
||||
- Create: `src/app/components/RegionSelect.js` — shared by the modal and debug page
|
||||
- Modify: `src/app/components/GameClient.js` — region path, per-level ranks, bbox
|
||||
- Modify: `src/app/debug/coverage/page.js` — region select, `?region=` fetch
|
||||
|
||||
## Implementation Steps
|
||||
|
||||
1. Confirm the two Radix packages are installed; generate the shadcn wrappers.
|
||||
2. Read `src/app/debug/coverage/page.js` and reconcile this phase's description.
|
||||
3. Build `RegionSelect` first — level row plus grouped select — since the modal
|
||||
and the debug page both consume it.
|
||||
4. Build `RegionPicker`: province row plays, chevron expands, counts from the
|
||||
generated file, disabled state for sub-threshold leaves, `thin` label.
|
||||
5. Replace the home page's card grid; delete `fetchAllLeaderboards`; add
|
||||
`fetchLeaderboard(region, type)` keyed into the existing cache; clear on open.
|
||||
6. Rewrite the modal's `Tabs` block around `RegionSelect`.
|
||||
7. Update `GameClient`: accept `?region=` with `?location=` fallback, render the
|
||||
path via `regionPath`, render `levels` as rank rows, use the picked bbox.
|
||||
8. Update the debug coverage page.
|
||||
9. Check every screen at 375 px. The old `TabsList` grid must be gone.
|
||||
10. `npm run lint`, `npm test`, `npm run build:check`.
|
||||
|
||||
## Validation
|
||||
|
||||
- Vietnam, each province, and a sample of leaves are reachable in at most two
|
||||
clicks.
|
||||
- Opening the leaderboard modal issues 2 requests, not 134.
|
||||
- Switching region or type issues 2 more; revisiting a pair within the session
|
||||
issues none; reopening the modal refetches.
|
||||
- The home page renders its picker on first paint with no network request for the
|
||||
tree.
|
||||
- A game started at `VN` shows the full three-level path **in the result**, and
|
||||
nothing identifying the district before the guess.
|
||||
- A game started at a leaf opens the map zoomed to that leaf.
|
||||
- A sub-threshold leaf renders disabled and cannot be clicked through to `/game`.
|
||||
- `thin` leaves carry a label.
|
||||
- Lam Dong and Long An show their partial-coverage label.
|
||||
- `/game?location=HN` and `/game?location=DL` both load.
|
||||
- No horizontal scroll at 375 px on the home page, the modal, or the game screen.
|
||||
- Keyboard: the accordion and select are operable without a mouse.
|
||||
|
||||
## Risk Assessment
|
||||
|
||||
**The accordion buries districts.** *Signal:* reaching a district takes more than
|
||||
two clicks, or the province row's play action reads ambiguously against its
|
||||
expand control. *Response:* split into an explicit `Play` pill plus a district
|
||||
count button, matching the existing card's `Play →` affordance.
|
||||
|
||||
**Stale leaderboards after a guess.** *Signal:* a player returns to the home page
|
||||
and sees their old total. *Response:* clear the cache on modal open — correctness
|
||||
beats one saved request.
|
||||
|
||||
**The debug page description is wrong.** It was written from two grep hits
|
||||
because no agent in this workflow could read the file. *Signal:* step 2 finds
|
||||
something else. *Response:* adjust this phase, do not force the file to match it.
|
||||
|
||||
**Manual UI verification is the user's.** Per the repo's guidelines the agent runs
|
||||
`npm test` and reports; the user drives the browser. *Response:* finish with an
|
||||
explicit list of screens and breakpoints to check.
|
||||
|
||||
## Success Criteria
|
||||
|
||||
- [ ] The two Radix packages are installed and wrapped
|
||||
- [ ] Every node is reachable and playable from the home page
|
||||
- [ ] Leaderboards browse by level and region, fetching only what is shown
|
||||
- [ ] The result screen shows all three credited levels
|
||||
- [ ] The result reveals the actual district; nothing before the guess does
|
||||
- [ ] Sub-threshold leaves are visible but disabled; `thin` leaves labelled
|
||||
- [ ] Partial-coverage provinces are labelled
|
||||
- [ ] `?location=` URLs still work, including `DL`
|
||||
- [ ] No horizontal scroll at 375 px; accordion and select are keyboard-operable
|
||||
- [ ] `npm run lint`, `npm test`, and `npm run build:check` pass
|
||||
@@ -0,0 +1,171 @@
|
||||
---
|
||||
phase: 6
|
||||
title: "Docs and verification"
|
||||
status: todo
|
||||
priority: P2
|
||||
effort: "0.5d"
|
||||
dependencies: [5]
|
||||
---
|
||||
|
||||
# Phase 6: Docs and verification
|
||||
|
||||
## Overview
|
||||
|
||||
Reconcile the documentation with what the code now does, write the coverage note,
|
||||
sweep for leftover vocabulary, and run the full gate.
|
||||
|
||||
The docs in `/docs/` are the project's stated authority — `CLAUDE.md` points at
|
||||
them first — and every one currently describes a five-city flat model.
|
||||
`docs/features.md` is worse than stale: it documents a "Dart-throw Strategy" and
|
||||
per-city delta tuning that `src/lib/pano-index.js` replaced entirely.
|
||||
|
||||
## Requirements
|
||||
|
||||
- The coverage note is written and classifies absent coverage into its three causes.
|
||||
- Every `/docs/` claim about cities, leaderboards, or the pipeline matches the code.
|
||||
- No stale `city`-as-a-model vocabulary outside the Redis key names, which stay
|
||||
`city` on purpose.
|
||||
- The full gate passes: lint, unit tests, integration tests, build check.
|
||||
|
||||
## The coverage note
|
||||
|
||||
Canonical location: `docs/project-overview.md`. This is the deliverable the user
|
||||
asked for, and its value is the *classification* — a note that only says
|
||||
"coverage is partial" teaches maintainers to ignore real gaps.
|
||||
|
||||
> ## Coverage
|
||||
>
|
||||
> Five provinces — Ha Noi, Ho Chi Minh, Da Nang, Lam Dong, Long An — split into
|
||||
> 61 districts and towns. Coverage is deliberately partial and will grow in
|
||||
> future releases. **None are planned at present.**
|
||||
>
|
||||
> Absent coverage has three distinct causes. Only one is a defect:
|
||||
>
|
||||
> **Not yet added** — any province outside the five. Roadmap, not a bug. Adding
|
||||
> one is an entry in `scripts/build-region-boundaries.mjs`, a boundary build, a
|
||||
> panorama index build, and a district assignment run. No application code
|
||||
> changes.
|
||||
>
|
||||
> **No street imagery** — a district inside a covered province where Mapillary
|
||||
> holds no panoramas, typically rural. The tree lists it; `playableRegions()`
|
||||
> excludes it from play. Expected, and it may resolve on its own: re-running the
|
||||
> panorama index build picks up new Mapillary coverage.
|
||||
>
|
||||
> **Missing from the boundary** — a district whose OpenStreetMap lookup did not
|
||||
> resolve, so it never entered the province union and its panoramas were clipped
|
||||
> away. Cu Chi in Ho Chi Minh City is the known case (`"missingParts": 1` in
|
||||
> `src/data/boundaries/tphcm.json`). Fixable: resolve the query, rebuild the
|
||||
> boundary, re-run that province's panorama index. It costs Mapillary tile
|
||||
> requests against a 50,000/day cap, which is why it is not done automatically.
|
||||
>
|
||||
> Before treating an empty district as a bug, check `missingParts` in its
|
||||
> province's boundary file — that distinguishes the third case from the first two.
|
||||
|
||||
Naming Cu Chi explicitly is deliberate: an unnamed "known case" becomes a mystery
|
||||
in six months. Cross-check the `missingParts` value at write time — Phase 1 may
|
||||
have added unresolved leaves beyond Cu Chi.
|
||||
|
||||
Add a one-line pointer in `scripts/build-region-boundaries.mjs`'s `REGIONS`
|
||||
config so a reader arriving from the code finds the note.
|
||||
|
||||
## Docs impact
|
||||
|
||||
| File | Change |
|
||||
|---|---|
|
||||
| `docs/project-overview.md` | "5 major cities" → the tree. The coverage note. Replace "Triple-Leaderboard System" with the rollup model. Note the pre-2025 basis |
|
||||
| `docs/features.md` | **Rewrite Location Coverage and Street View System** — the dart-throw and per-city delta content is obsolete. Add the fan-out: one guess credits leaf, province, country. Record that `VN` is an exploration mode that scores 0 |
|
||||
| `docs/game-flow.md` | Selection is country/province/district; the round reveals the resolving district |
|
||||
| `docs/project-structure.md` | `src/data/regions/`, renamed and new scripts, `src/lib/regions.js`, `scripts/lib/`, the renamed coverage route |
|
||||
| `docs/development.md` | Build sequence: boundaries → panorama index → district assignment. The migration, its dry-run default, and deploy-then-migrate ordering. The two new Radix dependencies |
|
||||
| `CLAUDE.md` | Quick Reference only if a command name changed |
|
||||
|
||||
Link to scripts rather than restating their flags — they carry header comments,
|
||||
and duplicating them guarantees drift.
|
||||
|
||||
## Stale-vocabulary sweep
|
||||
|
||||
Corrected against actual grep results; the earlier draft's expectations were
|
||||
wrong in two rows.
|
||||
|
||||
| Pattern | Expected outcome |
|
||||
|---|---|
|
||||
| `build-city-boundaries` | Zero hits. Real reference set is 5 sites: `scripts/build-city-boundaries.mjs:9,197`, `scripts/build-pano-index.mjs:12,160`, `src/data/boundaries/index.js:1`. **Not in `docs/`** — the earlier claim was false |
|
||||
| `city-coverage` | Zero hits outside git history. One caller: `src/app/debug/coverage/page.js:59` |
|
||||
| `cityCode` | Only in `src/lib/upstash.js`'s namespace comment and the `/api/guess` legacy-session fallback |
|
||||
| `CITIES` | Only as the derived back-compat export in `src/lib/game.js`, or gone if Phase 5 removed the last consumer |
|
||||
| `cityNames`, `cityCenters`, `cities` | Consumers migrated; delete any export with no caller |
|
||||
| `cityBboxes` | **Already dead** — `src/lib/game.js:64` is the only site. Delete it |
|
||||
| `indexedCities` | **Already dead** — `src/lib/pano-index.js:58` is the only site. Delete it, or keep it if Phase 1's test now uses it |
|
||||
| `5 cities`, `five cities`, `major cities` | Zero hits in `docs/`. Only genuine hit today is `docs/project-overview.md:3` |
|
||||
| `Dart-throw`, `delta` | Zero hits — obsolete strategy still documented in `docs/features.md` |
|
||||
| `leaderboard:city:` | Present and **correct** — do not rename |
|
||||
| `/api/regions` | Zero hits — the route was removed during red-team |
|
||||
|
||||
Deleting the back-compat exports is the point of the sweep. They exist so phases
|
||||
1-4 could land without breaking the UI; once Phase 5 is in, leaving them is dead
|
||||
code that invites new callers back onto the flat model.
|
||||
|
||||
## Related Code Files
|
||||
|
||||
- Modify: `docs/project-overview.md`, `docs/features.md`, `docs/game-flow.md`,
|
||||
`docs/project-structure.md`, `docs/development.md`
|
||||
- Modify: `CLAUDE.md` (only if a command name changed)
|
||||
- Modify: `scripts/build-region-boundaries.mjs` — pointer comment
|
||||
- Modify: any file surfaced by the sweep
|
||||
|
||||
## Implementation Steps
|
||||
|
||||
1. Read each doc before editing, then reconcile against the **shipped code** —
|
||||
not against this plan, which is a record of intent and may have been amended
|
||||
during execution.
|
||||
2. Write the coverage note; verify its `missingParts` claim against the actual
|
||||
boundary files.
|
||||
3. Run the sweep table top to bottom.
|
||||
4. Delete back-compat and dead exports.
|
||||
5. Whole-plan consistency check: re-read `plan.md` and phases 1-5, reconcile
|
||||
anything execution changed.
|
||||
6. Full gate: `npm run lint`, `npm test`, `npm run test:integration`
|
||||
(`npm run redis:up` first), `npm run build:check`.
|
||||
7. Verify each of `plan.md`'s success criteria against live state, with evidence
|
||||
— a command, a key, or a screen. Not against the phase checkboxes.
|
||||
8. `npm run redis:down`.
|
||||
|
||||
## Validation
|
||||
|
||||
- The coverage note names all three causes and the current `missingParts` reality.
|
||||
- Every sweep pattern lands on its expected outcome.
|
||||
- Every `/docs/` statement about coverage, scoring, or the pipeline is true of the
|
||||
shipped code — in particular `docs/features.md` no longer describes dart-throw.
|
||||
- All four gate commands exit 0.
|
||||
- Every `plan.md` success criterion verified against live state, with evidence.
|
||||
- No background processes left running.
|
||||
|
||||
## Risk Assessment
|
||||
|
||||
**Docs get updated from the plan rather than the code.** The plan states intent
|
||||
written before implementation; anything the phases amended would be copied in as
|
||||
a false claim. *Signal:* a doc statement no grep or command can confirm.
|
||||
*Response:* step 1 makes code the source; every claim needs a citable file or
|
||||
command.
|
||||
|
||||
**Back-compat exports get left in "just in case".** *Signal:* `cityNames` still
|
||||
exported with zero callers. *Response:* delete. Git history is the safety net.
|
||||
|
||||
**The coverage note drifts.** It is prose, and `docs/features.md` proves prose
|
||||
drifts here. *Signal:* a future maintainer adds a province and the note still says
|
||||
five. *Response:* the note leans on `missingParts` and the generated tree for its
|
||||
specifics; keep counts out of the prose where the data already carries them.
|
||||
|
||||
**The integration container stays up.** *Signal:* a Redis container still running.
|
||||
*Response:* `npm run redis:down` is step 8, not an afterthought.
|
||||
|
||||
## Success Criteria
|
||||
|
||||
- [ ] The coverage note is written, classifies all three causes, and names Cu Chi
|
||||
- [ ] All five `/docs/` files describe the region tree accurately
|
||||
- [ ] `docs/features.md`'s obsolete dart-throw content is gone
|
||||
- [ ] Every sweep pattern resolves to its expected outcome
|
||||
- [ ] Dead and back-compat exports deleted
|
||||
- [ ] `npm run lint`, `npm test`, `npm run test:integration`, `npm run build:check` all pass
|
||||
- [ ] All `plan.md` success criteria verified against live state
|
||||
- [ ] No background processes left running
|
||||
@@ -0,0 +1,291 @@
|
||||
---
|
||||
title: "Nested Region Tree"
|
||||
description: "Replace the flat five-city model with a country > province > district tree whose scores roll upward, preserving all existing leaderboard points."
|
||||
status: pending
|
||||
priority: P1
|
||||
effort: "4-6d"
|
||||
tags: [data-model, leaderboard, region-tree]
|
||||
created: 2026-08-30
|
||||
blockedBy: []
|
||||
blocks: []
|
||||
---
|
||||
|
||||
# Nested Region Tree
|
||||
|
||||
## Overview
|
||||
|
||||
Today the game knows five flat "cities" (`HN`, `DN`, `TPHCM`, `DL`, `DH`), each
|
||||
with one boundary polygon, one panorama index, and one pair of leaderboards,
|
||||
plus a single country-wide pair keyed `leaderboard:vietnam` / `distance:vietnam`.
|
||||
|
||||
This plan replaces that with a three-level tree:
|
||||
|
||||
```
|
||||
VN (Vietnam)
|
||||
├── HN Ha Noi → 30 districts
|
||||
├── DN Da Nang → 7 districts
|
||||
├── TPHCM Ho Chi Minh → 22 districts
|
||||
├── LD Lam Dong → 1 town (DL Da Lat) — partial coverage
|
||||
└── LA Long An → 1 town (DH Duc Hoa) — partial coverage
|
||||
```
|
||||
|
||||
A guess resolves to the **leaf** the panorama sits in, then credits that leaf,
|
||||
its province, and `VN`. Every node is playable: choosing a province draws a
|
||||
panorama from anywhere inside it; choosing `VN` draws from anywhere covered.
|
||||
|
||||
**Administrative basis.** The repo deliberately uses **pre-2025-merger** extents
|
||||
(`scripts/build-region-boundaries.mjs` header explains why: post-merger Ho Chi Minh
|
||||
City spans 36,566 km² and reaches Vung Tau, which is not a guessable area against
|
||||
scoring bands that top out at 1 km). This plan keeps that basis, so districts are
|
||||
the pre-2025 quận/huyện/thị xã, and Duc Hoa's parent is **Long An** — not the
|
||||
post-2025 Tay Ninh it was merged into.
|
||||
|
||||
## Goals
|
||||
|
||||
| # | Goal | Priority |
|
||||
|---|------|----------|
|
||||
| 1 | A single generated region tree replaces the hand-maintained `CITIES` map and the duplicated bbox/center data | P1 |
|
||||
| 2 | Guessing in a district credits the district, its province, and Vietnam | P1 |
|
||||
| 3 | Every existing leaderboard point survives the change, untouched | P1 |
|
||||
| 4 | Any node — country, province, or leaf — is selectable and playable | P1 |
|
||||
| 5 | District panorama assignment reuses the panoramas already on disk; no province is re-fetched | P1 |
|
||||
| 6 | Adding a province or district later is a data edit plus a build run, not a code change | P2 |
|
||||
| 7 | Absent coverage is self-describing: a maintainer can classify any empty node without asking | P2 |
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- Adding provinces or districts beyond the five provinces already covered. The
|
||||
tree makes future additions cheap; none are planned now.
|
||||
- Resolving Cu Chi. It is already absent from the shipped TPHCM boundary
|
||||
(`"missingParts": 1`) and therefore from its panorama index. Fixing it costs
|
||||
Mapillary tile requests; the user has accepted the gap. See *Coverage note*.
|
||||
- Changing the 0-5 scoring bands, the distance formula, or the 200-entry cap.
|
||||
- Bonus multipliers. Lam Dong and Long An carry a *partial coverage* label only;
|
||||
they score exactly like any other province.
|
||||
- Post-2025 administrative boundaries.
|
||||
- Fixing the pre-existing read-then-write race in `submitScore`, or adding
|
||||
request pipelining to the Upstash adapter (see Risks).
|
||||
- Rate limiting. None exists today; adding it is unrequested scope.
|
||||
|
||||
## Key Decisions
|
||||
|
||||
| Decision | Choice | Why |
|
||||
|---|---|---|
|
||||
| Aggregation | Write fan-out (leaf + province + country on every guess) | Preserves pre-existing parent totals for free; derive-on-read would orphan them |
|
||||
| Leaf codes for Da Lat / Duc Hoa | Keep `DL` / `DH` verbatim | Their leaderboard keys already exist; renaming would strand the history |
|
||||
| Redis key prefix | Keep `leaderboard:city:` / `distance:city:` | Renaming to `:region:` would strand every existing key. Naming purity loses to data preservation |
|
||||
| Redis key names in this plan | All **logical**; physical form is `${KEY_PREFIX}${logical}`, default `vngeoguessr:` | The adapter applies the prefix invisibly (`upstash.js:34,40-42`). A migration that bypasses it silently reads an empty namespace |
|
||||
| `VN` key mapping | Special-cases to the legacy `leaderboard:vietnam` / `distance:vietnam` | Keeps the existing global leaderboard as the country node |
|
||||
| Legacy province points | Stay on the province node | Old HN/DN/TPHCM points predate districts and are unattributable; province totals stay continuous, district totals start at zero |
|
||||
| New province nodes | `LD` and `LA` backfilled by copying `DL` / `DH` sorted sets once | Their only child already holds the full history; without a backfill the province would disagree with its child |
|
||||
| District panoramas | Partition the **existing** province indexes by point-in-polygon | A per-district tile fetch would multiply the 2,800-tile budget against a 50,000/day cap for data we already hold |
|
||||
| Province geometry | Derived as the union of its children — except a single-child province, which byte-copies its child's polygon | Removes parent/child drift. Re-simplifying a single child at a looser tolerance would strand its own panoramas in the gap |
|
||||
| Country geometry | None; bbox and center derived from children | The real Vietnam outline is huge and irrelevant — coverage is five provinces |
|
||||
| Node centers | Generated centroid, overridable per node; the five existing centers are seeded as overrides | `game.js:9-12` documents why these are deliberately *not* polygon centroids |
|
||||
| `VN` random draw | Pick a province uniformly, then a panorama within it | Uniform over all 424,691 panoramas would make "Vietnam" 97% Ha Noi + Ho Chi Minh |
|
||||
| Province random draw | Unchanged: uniform over the province's panoramas | Preserves today's behaviour for existing entry points |
|
||||
| **`VN` scores zero and ships anyway** | Accepted trade-off, not a defect | `calculateScore` returns 0 above 1 km and the five provinces span ~1,100 km. `VN` is an exploration mode. **Do not "fix" this by rescaling the bands** — the user decided it explicitly |
|
||||
| Panorama counts for the UI | A second generated file, `src/data/regions/counts.js` | Keeps one writer per generated file, keeps the tree client-safe, and removes the need for a `/api/regions` route |
|
||||
| Phase 5 primitives | Add `@radix-ui/react-accordion` and `@radix-ui/react-select` | Neither exists in `src/components/ui/`. Hand-rolling both would ship broken a11y. **`package.json` change — apply manually** |
|
||||
| Boundary file layout | `src/data/boundaries/<province>/<code>.json` | 66 files in one flat directory is unreadable, and every leaf belongs to exactly one province |
|
||||
| Nominatim query strategy | Qualified form, then bare form, with the hit's centroid validated against the parent's pre-2025 bbox | Discovered in execution: OSM has applied the 2025 merger, so pre-2025 districts survive only as `boundary/historic` relations whose rendered parent is the *current* province. A qualified query matches nothing; a bare one can match a same-named unit in another province. See Phase 1 |
|
||||
| Panorama file layout | Stays flat — one file per province | Five files, one per province forever. A directory per file would be noise; the boundary case is 31 files under `hn/` |
|
||||
|
||||
## Architecture
|
||||
|
||||
### Region tree
|
||||
|
||||
Two generated files, one writer each, both small and client-safe:
|
||||
|
||||
- `src/data/regions/index.js` — written by the boundary build. Node code, name,
|
||||
parent, level, center, bbox, `partialCoverage`.
|
||||
- `src/data/regions/counts.js` — written by the district-assignment build.
|
||||
Per-node panorama count, distinct-cell count, and playability.
|
||||
|
||||
```js
|
||||
// src/data/regions/index.js
|
||||
export const REGIONS = {
|
||||
VN: { code: 'VN', name: 'Vietnam', parent: null, level: 'country', center: [...], bbox: [...] },
|
||||
TPHCM: { code: 'TPHCM', name: 'Ho Chi Minh', parent: 'VN', level: 'province', center: [...], bbox: [...] },
|
||||
'TPHCM-Q7': { code: 'TPHCM-Q7', name: 'District 7', parent: 'TPHCM', level: 'district', center: [...], bbox: [...] },
|
||||
LD: { code: 'LD', name: 'Lam Dong', parent: 'VN', level: 'province', partialCoverage: 'one town covered', ... },
|
||||
DL: { code: 'DL', name: 'Da Lat', parent: 'LD', level: 'district', ... },
|
||||
LA: { code: 'LA', name: 'Long An', parent: 'VN', level: 'province', partialCoverage: 'one town covered', ... },
|
||||
DH: { code: 'DH', name: 'Duc Hoa', parent: 'LA', level: 'district', ... },
|
||||
};
|
||||
```
|
||||
|
||||
`src/lib/regions.js` — hand-written traversal over those two files and **nothing
|
||||
else**. It must never import `src/data/panos/` or `src/lib/pano-index.js`: client
|
||||
components import it, and the panorama data is 25 MB of exact answers. A test
|
||||
asserts that import boundary.
|
||||
|
||||
**Codes.** Provinces keep their current codes. Districts are
|
||||
`<PARENT>-<SLUG>` (`HN-BADINH`, `TPHCM-Q7`, `DN-HAICHAU`). `DL` and `DH` are the
|
||||
two exceptions, keeping their bare historical codes.
|
||||
|
||||
Hyphenated codes are **not** legal JS identifiers or bare object keys. Both
|
||||
barrel writers derive both from the filename today
|
||||
(`build-city-boundaries.mjs:190-203`, `build-pano-index.mjs:273-287`) and must be
|
||||
fixed before the first leaf is written.
|
||||
|
||||
### Panorama partition
|
||||
|
||||
`src/data/panos/<province>.json` gains a header `districts` array and a `d`
|
||||
field per entry — an **index** into that array:
|
||||
|
||||
```json
|
||||
"districts": ["TPHCM-Q1","TPHCM-Q3", ...],
|
||||
"districtCounts": {"TPHCM-Q1": 3211, ...},
|
||||
"districtCells": {"TPHCM-Q1": 274, ...},
|
||||
"unassigned": 412,
|
||||
"panos": [ {"id":"...","lat":10.77,"lng":106.70,"d":0} ]
|
||||
```
|
||||
|
||||
An entry with no `d` fell in a gap between simplified district outlines. It is
|
||||
still playable at province level and credits province + country only.
|
||||
|
||||
`districtCells` counts distinct 0.01° (~1.1 km) cells, because raw counts
|
||||
overstate distinct *places* by roughly 30× — the index is thinned at 33 m, so a
|
||||
single street corridor yields dozens of entries. Da Nang's 1,521 panoramas
|
||||
occupy 39 cells in total; Da Lat's 475 occupy 8.
|
||||
|
||||
### Score fan-out
|
||||
|
||||
```
|
||||
POST /api/guess
|
||||
session.regionCode (leaf resolved from the winning draw — server-side only)
|
||||
→ ancestorsOf(regionCode) = ['TPHCM-Q7', 'TPHCM', 'VN']
|
||||
→ submitScore / submitDistanceRecord write all three
|
||||
```
|
||||
|
||||
`session.regionCode` is a **secret**, alongside `exactLocation`: it names the
|
||||
district the panorama is in. Neither is ever serialized to the client before the
|
||||
guess. Responses carry `pickedRegion` only.
|
||||
|
||||
### Data flow
|
||||
|
||||
```
|
||||
build-region-boundaries.mjs (Nominatim, up to 2 queries per leaf)
|
||||
→ src/data/boundaries/<province>/<code>.json (leaves + province outline)
|
||||
→ src/data/boundaries/index.js (barrel, sanitised identifiers)
|
||||
→ src/data/regions/index.js (tree: names, parents, centers, bboxes)
|
||||
--regenerate (rebuild provinces/barrel/tree from disk,
|
||||
no network — for reshaping outputs)
|
||||
|
||||
assign-pano-districts.mjs (no network)
|
||||
→ re-clips src/data/panos/<province>.json against the current outline [done in Phase 1]
|
||||
→ rewrites it with districts[] + d
|
||||
→ src/data/regions/counts.js (counts, cells, playability)
|
||||
|
||||
migrate-leaderboards.mjs (after deploy; --dry-run default)
|
||||
→ copies leaderboard:city:dl → :ld, distance:city:dl → :ld
|
||||
→ copies leaderboard:city:dh → :la, distance:city:dh → :la
|
||||
```
|
||||
|
||||
## Coverage note
|
||||
|
||||
Canonical text lives in `docs/project-overview.md` (Phase 6 writes it). Its
|
||||
substance, recorded here because it is a durable decision:
|
||||
|
||||
Coverage is deliberately partial and will grow in future releases; **none are
|
||||
planned at present**. Absent coverage has three distinct causes, and only one is
|
||||
a defect:
|
||||
|
||||
1. **Not yet added** — any province outside the five. Roadmap. Adding one is an
|
||||
entry in `scripts/build-region-boundaries.mjs` plus three build runs, no
|
||||
application code change.
|
||||
2. **No street imagery** — a district where Mapillary holds no panoramas,
|
||||
typically rural. Listed in the tree, excluded from play. May self-resolve on
|
||||
a future index rebuild.
|
||||
3. **Missing from the boundary** — a district whose OSM lookup did not resolve,
|
||||
so it never entered the province union and its panoramas were clipped away.
|
||||
Cu Chi is the known case (`"missingParts": 1` in
|
||||
`src/data/boundaries/tphcm.json`). Fixable, but costs Mapillary tile requests
|
||||
against a 50,000/day cap, which is why it is not automatic.
|
||||
|
||||
`missingParts` in a province's boundary file distinguishes the third from the
|
||||
first two.
|
||||
|
||||
## Phases
|
||||
|
||||
| # | Phase | Status | Depends on |
|
||||
|---|-------|--------|-----------|
|
||||
| 1 | [Phase 1: Region tree model and boundaries](./phase-01-region-tree-model-and-boundaries.md) | Completed | — |
|
||||
| 2 | [Phase 2: Panorama district partition](./phase-02-panorama-district-partition.md) | Pending | 1 |
|
||||
| 3 | [Phase 3: Leaderboard fan-out and migration](./phase-03-leaderboard-fan-out-and-migration.md) | Pending | 1 |
|
||||
| 4 | [Phase 4: API surface](./phase-04-api-surface.md) | Pending | 2, 3 |
|
||||
| 5 | [Phase 5: UI region navigation](./phase-05-ui-region-navigation.md) | Pending | 4 |
|
||||
| 6 | [Phase 6: Docs and verification](./phase-06-docs-and-verification.md) | Pending | 5 |
|
||||
|
||||
## Success Criteria
|
||||
|
||||
- [ ] `src/data/regions/index.js` holds 1 country, 5 provinces, and 61 leaves, all generated
|
||||
- [ ] Both generated barrels parse — a test `import()`s them
|
||||
- [ ] Every leaf that Nominatim resolved has a boundary polygon; unresolved leaves are recorded in `missingParts`, not fatal
|
||||
- [ ] Every panorama in the five province indexes carries a district assignment, or is counted in a reported unassigned tally under 2%
|
||||
- [ ] `src/lib/regions.js`'s import graph never reaches `src/data/panos/` — asserted by test
|
||||
- [ ] A guess in `TPHCM-Q7` increases the `TPHCM-Q7`, `TPHCM`, and `VN` score totals by the same amount
|
||||
- [ ] The leaf is resolved from the **winning** panorama draw, server-side, and never appears in any pre-guess response
|
||||
- [ ] `leaderboard:vietnam` and `distance:vietnam` remain the country node's keys — pre-change totals unchanged and still growing
|
||||
- [ ] `leaderboard:city:hn` / `:dn` / `:tphcm` / `:dl` / `:dh` totals are unchanged immediately after deploy
|
||||
- [ ] `leaderboard:city:ld` equals `leaderboard:city:dl` and `leaderboard:city:la` equals `leaderboard:city:dh` immediately after migration
|
||||
- [ ] The migration aborts if its export contains zero keys, and prints the resolved `KEY_PREFIX`
|
||||
- [ ] `/game?location=HN` and `/game?location=DL` still work
|
||||
- [ ] A leaf below the playability threshold is listed in the tree but not offered as playable
|
||||
- [ ] 200 draws at `VN` hit at least four distinct provinces
|
||||
- [ ] `npm test`, `npm run test:integration`, and `npm run build:check` pass
|
||||
- [ ] No province's panorama index is re-fetched from Mapillary
|
||||
|
||||
## Red Team Review
|
||||
|
||||
### Session — 2026-08-30
|
||||
**Reviewers:** 4 (Security Adversary, Failure Mode Analyst, Assumption Destroyer, Scope & Complexity Critic), Full verification tier
|
||||
**Findings:** 38 raw → 15 after deduplication (15 accepted, 3 rejected)
|
||||
**Severity breakdown:** 6 Critical, 7 High, 2 Medium
|
||||
|
||||
| # | Finding | Severity | Disposition | Applied To |
|
||||
|---|---------|----------|-------------|------------|
|
||||
| C1 | `writeBarrel` emits invalid JS for hyphenated leaf codes | Critical | Accept | Phase 1 |
|
||||
| C2 | `src/lib/mapillary.js` in no phase; discards the resolved leaf | Critical | Accept | Phase 2, 4 |
|
||||
| C3 | Phase 1 cannot pass its own `npm test`; `CODES` derives from `CITIES` | Critical | Accept | Phase 1 |
|
||||
| C4 | Migration export unbuildable: no scan primitive, `KEY_PREFIX` unaddressed | Critical | Accept | Phase 3 |
|
||||
| C5 | `regions.js` + client import ships 25 MB of coordinates to the browser | Critical | Accept | Phase 1, 5 |
|
||||
| C6 | `POST /api/new-game` leaks the resolved leaf; `getLeaderboard` takes unvalidated input into key names | Critical | Accept | Phase 4 |
|
||||
| H1 | `/api/guess` non-idempotent; fan-out widens double-credit; "not half-recorded" claim false | High | Accept | Phase 3, 4 |
|
||||
| H2 | Migration ordering undefined; copy≠replace; reconciliation would delete legacy points | High | Accept | Phase 3 |
|
||||
| H3 | Both size budgets arithmetically wrong and self-contradictory | High | Accept | Phase 1, 2 |
|
||||
| H4 | Generated centroids overwrite documented hand-picked centers | High | Accept | Phase 1 |
|
||||
| H5 | `/api/regions` works around Phase 1's own decision; `plan.md` contradicted Phase 1 | High | Accept | Phase 1, 2, 4, 5 |
|
||||
| H6 | Phase 5 accordion + grouped select absent under "no new dependencies" | High | Accept | Phase 5 |
|
||||
| H7 | HN province union breaks panorama containment tests, no repair step | High | Accept | Phase 1, 2 |
|
||||
| M1 | `build-pano-index.mjs` default list resurrects `dl.json`/`dh.json`; barrel double-counts | Medium | Accept | Phase 2 |
|
||||
| M2 | `count > 0` too weak a playability gate; clustering measured | Medium | Accept | Phase 2 |
|
||||
| — | Rate limiting on `/api/guess` | High | Reject | Pre-existing gap, unrequested scope |
|
||||
| — | Redis round-trip count as blocking | Medium | Reject | Separable perf follow-up, same class as the declared out-of-scope `zIncrBy` race |
|
||||
| — | Cu Chi as a defect | Critical | Reject | User accepted the gap; recorded in the coverage note instead |
|
||||
|
||||
**User decisions taken during adjudication:**
|
||||
1. `VN` ships playable despite scoring zero — exploration mode, bands stay frozen.
|
||||
2. Add `@radix-ui/react-accordion` and `@radix-ui/react-select` rather than hand-rolling or degrading to native controls.
|
||||
3. Missing coverage (Cu Chi included) is acceptable; the coverage note carries it.
|
||||
|
||||
### Whole-Plan Consistency Sweep
|
||||
- Files reread: `plan.md`, `phase-01` … `phase-06`
|
||||
- Decision deltas checked: 18
|
||||
- Reconciled stale references: 11 — `/api/regions` removed from Phase 4/5 and `plan.md` Architecture; `panos:` count field moved out of the tree into `counts.js`; `partialCoverage` widened from boolean to reason string; Goal 5 reworded from "zero new Mapillary requests" to "no province re-fetched"; Phase 1 Validation/Risk contradiction on `tests/pano-index.test.js` resolved; pano file rename moved from Phase 2 to Phase 1; size budgets restated in both phases; `session.regionCode` marked secret in Architecture and Phase 4; migration ordering pinned in `plan.md` Data flow and Phase 3; centroid overrides added to Key Decisions and Phase 1
|
||||
- Unresolved contradictions: 0
|
||||
|
||||
## Open Questions
|
||||
|
||||
None. Seven decisions were resolved with the user:
|
||||
|
||||
1. **Bonus cases** — Lam Dong and Long An are a *partial-coverage label only*.
|
||||
2. **Play level** — any node is playable, not leaf-only.
|
||||
3. **New province leaderboards** — backfilled from their single child's totals.
|
||||
4. **Legacy province points** — left on the parent, no synthetic child node.
|
||||
5. **Duc Hoa's parent** — Long An, not Tay Ninh, per the pre-2025 basis.
|
||||
6. **`VN` scoring zero** — accepted; ships as an exploration mode.
|
||||
7. **Phase 5 primitives** — add the two Radix packages.
|
||||
|
||||
<!-- slug: nested-region-tree -->
|
||||
@@ -0,0 +1,97 @@
|
||||
// Re-clip each province's panorama index against its current boundary, and
|
||||
// rewrite the header to match.
|
||||
//
|
||||
// node scripts/assign-pano-districts.mjs # every province
|
||||
// node scripts/assign-pano-districts.mjs HN TPHCM # only these
|
||||
//
|
||||
// Network-free by design. The panoramas are already on disk; re-fetching them
|
||||
// per district would multiply a ~2,800-tile build against Mapillary's
|
||||
// 50,000/day cap for data we already hold.
|
||||
//
|
||||
// Why re-clipping is needed at all: the boundaries are now built per district
|
||||
// and unioned upward, at a tighter simplification tolerance than the old
|
||||
// whole-city outlines. A province's edge therefore moves by a few metres, and
|
||||
// panoramas clipped against the old outline no longer agree with the new one.
|
||||
// Leaving them would break the containment guarantee the index rests on --
|
||||
// every entry is meant to be inside the region it is served for.
|
||||
//
|
||||
// District assignment (a `d` field per panorama, and the per-leaf counts the UI
|
||||
// needs) lands in this same script in the next phase.
|
||||
|
||||
import { readdirSync, readFileSync, writeFileSync, statSync } from 'node:fs';
|
||||
import * as turf from '@turf/turf';
|
||||
|
||||
const BOUNDARY_DIR = 'src/data/boundaries';
|
||||
const PANO_DIR = 'src/data/panos';
|
||||
|
||||
/** A province's boundary feature. */
|
||||
function loadBoundary(code) {
|
||||
const slug = code.toLowerCase();
|
||||
return JSON.parse(readFileSync(`${BOUNDARY_DIR}/${slug}/${slug}.json`, 'utf8'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewrite one province's index against its current boundary.
|
||||
* @param {string} code Province code.
|
||||
* @returns {{kept: number, dropped: number}} Outcome.
|
||||
*/
|
||||
function reclip(code) {
|
||||
const path = `${PANO_DIR}/${code.toLowerCase()}.json`;
|
||||
const index = JSON.parse(readFileSync(path, 'utf8'));
|
||||
const boundary = loadBoundary(code);
|
||||
const [west, south, east, north] = boundary.properties.bbox;
|
||||
|
||||
const kept = index.panos.filter((p) => {
|
||||
// Cheap rejection first: point-in-polygon against a detailed outline is the
|
||||
// expensive part, and most strays fall outside the box.
|
||||
if (p.lat < south || p.lat > north || p.lng < west || p.lng > east) return false;
|
||||
return turf.booleanPointInPolygon(turf.point([p.lng, p.lat]), boundary);
|
||||
});
|
||||
|
||||
const dropped = index.panos.length - kept.length;
|
||||
|
||||
const header = {
|
||||
code,
|
||||
name: boundary.properties.name,
|
||||
center: boundary.properties.center,
|
||||
bbox: boundary.properties.bbox,
|
||||
count: kept.length,
|
||||
source: index.source,
|
||||
grid: index.grid,
|
||||
generatedAt: index.generatedAt,
|
||||
reclippedAt: new Date().toISOString(),
|
||||
};
|
||||
|
||||
// Written by hand rather than through JSON.stringify's indent option: one
|
||||
// panorama per line keeps a six-figure list diffable and editable, where a
|
||||
// fully indented file would run to millions of lines.
|
||||
const body =
|
||||
Object.entries(header)
|
||||
.map(([key, value]) => ` ${JSON.stringify(key)}: ${JSON.stringify(value)},`)
|
||||
.join('\n') +
|
||||
'\n "panos": [\n' +
|
||||
kept.map((p) => ` {"id":${JSON.stringify(p.id)},"lat":${p.lat},"lng":${p.lng}}`).join(',\n') +
|
||||
'\n ]\n';
|
||||
|
||||
writeFileSync(path, `{\n${body}}\n`);
|
||||
return { kept: kept.length, dropped };
|
||||
}
|
||||
|
||||
const requested = process.argv.slice(2).filter((arg) => !arg.startsWith('--'));
|
||||
const available = readdirSync(PANO_DIR)
|
||||
.filter((name) => name.endsWith('.json'))
|
||||
.map((name) => name.replace(/\.json$/, '').toUpperCase());
|
||||
const codes = requested.length ? requested : available;
|
||||
|
||||
let totalDropped = 0;
|
||||
for (const code of codes) {
|
||||
const { kept, dropped } = reclip(code);
|
||||
totalDropped += dropped;
|
||||
const mb = (statSync(`${PANO_DIR}/${code.toLowerCase()}.json`).size / 1e6).toFixed(1);
|
||||
console.log(
|
||||
`${code.padEnd(6)} ${String(kept).padStart(7)} kept` +
|
||||
(dropped ? `, ${dropped} dropped outside the new outline` : '') +
|
||||
` (${mb} MB)`
|
||||
);
|
||||
}
|
||||
console.log(totalDropped ? `\n${totalDropped} panoramas dropped in total` : '\nno panoramas dropped');
|
||||
@@ -1,218 +0,0 @@
|
||||
// Build the play-area polygon for each city and write it as GeoJSON.
|
||||
//
|
||||
// Vietnam merged its provinces in mid-2025, so the current administrative
|
||||
// boundary for Ho Chi Minh City covers 36,566 km2 and reaches Vung Tau. That is
|
||||
// not a guessable area: the scoring bands top out at 1km. These are the
|
||||
// pre-merger extents instead, rebuilt by unioning the old district-level units,
|
||||
// which OpenStreetMap still serves as boundary/historic relations.
|
||||
//
|
||||
// node scripts/build-city-boundaries.mjs
|
||||
//
|
||||
// Output lands in src/data/boundaries/<code>.geojson and is meant to be edited
|
||||
// by hand afterwards if a city needs trimming. Re-running overwrites it.
|
||||
|
||||
import { mkdirSync, readdirSync, writeFileSync } from 'node:fs';
|
||||
import * as turf from '@turf/turf';
|
||||
|
||||
const NOMINATIM = 'https://nominatim.openstreetmap.org/search';
|
||||
// Nominatim asks for a real identifier and at most one request per second.
|
||||
const USER_AGENT = 'vngeoguessr/1.0 (https://github.com/tiennm99/vngeoguessr)';
|
||||
const REQUEST_GAP_MS = 1100;
|
||||
// The unioned outlines carry tens of thousands of points, most of them detail
|
||||
// no one will ever see at city zoom. This keeps the files hand-editable.
|
||||
const SIMPLIFY_TOLERANCE = 0.0005;
|
||||
|
||||
const OUT_DIR = 'src/data/boundaries';
|
||||
|
||||
// One entry per city. `parts` are unioned; a single-element list is fine.
|
||||
const CITIES = {
|
||||
HN: {
|
||||
name: 'Ha Noi',
|
||||
// Ha Noi was not merged in 2025, so its own boundary is still the right one.
|
||||
parts: ['Thành phố Hà Nội, Việt Nam'],
|
||||
},
|
||||
TPHCM: {
|
||||
name: 'Ho Chi Minh',
|
||||
parts: [
|
||||
'Quận 1, Thành phố Hồ Chí Minh, Việt Nam',
|
||||
'Quận 3, Thành phố Hồ Chí Minh, Việt Nam',
|
||||
'Quận 4, Thành phố Hồ Chí Minh, Việt Nam',
|
||||
'Quận 5, Thành phố Hồ Chí Minh, Việt Nam',
|
||||
'Quận 6, Thành phố Hồ Chí Minh, Việt Nam',
|
||||
'Quận 7, Thành phố Hồ Chí Minh, Việt Nam',
|
||||
'Quận 8, Thành phố Hồ Chí Minh, Việt Nam',
|
||||
'Quận 10, Thành phố Hồ Chí Minh, Việt Nam',
|
||||
'Quận 11, Thành phố Hồ Chí Minh, Việt Nam',
|
||||
'Quận 12, Thành phố Hồ Chí Minh, Việt Nam',
|
||||
'Quận Bình Tân, Thành phố Hồ Chí Minh, Việt Nam',
|
||||
'Quận Bình Thạnh, Thành phố Hồ Chí Minh, Việt Nam',
|
||||
'Quận Gò Vấp, Thành phố Hồ Chí Minh, Việt Nam',
|
||||
'Quận Phú Nhuận, Thành phố Hồ Chí Minh, Việt Nam',
|
||||
'Quận Tân Bình, Thành phố Hồ Chí Minh, Việt Nam',
|
||||
'Quận Tân Phú, Thành phố Hồ Chí Minh, Việt Nam',
|
||||
'Thành phố Thủ Đức, Việt Nam',
|
||||
'Huyện Bình Chánh, Việt Nam',
|
||||
'Huyện Cần Giờ, Việt Nam',
|
||||
'Huyện Củ Chi, Việt Nam',
|
||||
'Huyện Hóc Môn, Việt Nam',
|
||||
'Huyện Nhà Bè, Việt Nam',
|
||||
],
|
||||
},
|
||||
DN: {
|
||||
name: 'Da Nang',
|
||||
// Hoang Sa is administratively part of Da Nang but is a disputed offshore
|
||||
// island group with no street imagery, so it is deliberately left out.
|
||||
parts: [
|
||||
'Quận Hải Châu, Đà Nẵng, Việt Nam',
|
||||
'Quận Thanh Khê, Đà Nẵng, Việt Nam',
|
||||
'Quận Sơn Trà, Đà Nẵng, Việt Nam',
|
||||
'Quận Ngũ Hành Sơn, Đà Nẵng, Việt Nam',
|
||||
'Quận Liên Chiểu, Đà Nẵng, Việt Nam',
|
||||
'Quận Cẩm Lệ, Đà Nẵng, Việt Nam',
|
||||
'Huyện Hòa Vang, Việt Nam',
|
||||
],
|
||||
},
|
||||
DL: {
|
||||
name: 'Da Lat',
|
||||
parts: ['Thành phố Đà Lạt, Việt Nam'],
|
||||
},
|
||||
DH: {
|
||||
name: 'Duc Hoa',
|
||||
parts: ['Đức Hòa, Việt Nam'],
|
||||
},
|
||||
};
|
||||
|
||||
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||
|
||||
/**
|
||||
* Look up one administrative area and return it as a turf feature.
|
||||
* @param {string} query Nominatim free-text query.
|
||||
* @returns {Promise<Object|null>} Feature, or null when nothing usable matched.
|
||||
*/
|
||||
async function lookupArea(query) {
|
||||
const url = `${NOMINATIM}?q=${encodeURIComponent(query)}&format=json&polygon_geojson=1&limit=8`;
|
||||
const response = await fetch(url, {
|
||||
headers: { 'User-Agent': USER_AGENT, 'Accept-Language': 'en' },
|
||||
});
|
||||
if (!response.ok) throw new Error(`Nominatim ${response.status} for "${query}"`);
|
||||
|
||||
// A cemetery or a road of the same name can outrank the district, so scan the
|
||||
// candidates for the first one that is actually an administrative area.
|
||||
for (const hit of await response.json()) {
|
||||
if (!hit?.geojson) continue;
|
||||
if (!['Polygon', 'MultiPolygon'].includes(hit.geojson.type)) continue;
|
||||
if (hit.class !== 'boundary') continue;
|
||||
return turf.feature(hit.geojson, { osm: `${hit.osm_type}/${hit.osm_id}` });
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build and write one city's play-area polygon.
|
||||
* @param {string} code City code.
|
||||
* @param {Object} config Entry from CITIES.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function buildCity(code, config) {
|
||||
console.log(`\n${code} — ${config.name}`);
|
||||
let merged = null;
|
||||
let missing = 0;
|
||||
|
||||
for (const query of config.parts) {
|
||||
const label = query.split(',')[0];
|
||||
let feature = null;
|
||||
try {
|
||||
feature = await lookupArea(query);
|
||||
} catch (error) {
|
||||
console.error(` ${label}: ${error.message}`);
|
||||
}
|
||||
|
||||
if (!feature) {
|
||||
missing++;
|
||||
console.warn(` ${label}: no boundary found — SKIPPED`);
|
||||
} else {
|
||||
const areaKm2 = turf.area(feature) / 1e6;
|
||||
console.log(` ${label}: ${areaKm2.toFixed(0)} km2 (${feature.properties.osm})`);
|
||||
merged = merged ? turf.union(turf.featureCollection([merged, feature])) : feature;
|
||||
}
|
||||
await sleep(REQUEST_GAP_MS);
|
||||
}
|
||||
|
||||
if (!merged) throw new Error(`${code}: no parts resolved, nothing to write`);
|
||||
if (missing > 0) {
|
||||
console.warn(` ${missing} part(s) missing — the polygon is incomplete`);
|
||||
}
|
||||
|
||||
const simplified = turf.simplify(merged, { tolerance: SIMPLIFY_TOLERANCE, highQuality: true });
|
||||
const bbox = turf.bbox(simplified);
|
||||
const centroid = turf.centerOfMass(simplified).geometry.coordinates;
|
||||
|
||||
const out = {
|
||||
type: 'Feature',
|
||||
properties: {
|
||||
code,
|
||||
name: config.name,
|
||||
// Recorded so a future reader knows why this is not the current
|
||||
// administrative boundary.
|
||||
basis: 'pre-2025-merger extent, unioned from OSM boundary/historic relations',
|
||||
parts: config.parts.length,
|
||||
missingParts: missing,
|
||||
areaKm2: Number((turf.area(simplified) / 1e6).toFixed(1)),
|
||||
bbox: bbox.map((n) => Number(n.toFixed(5))),
|
||||
center: [Number(centroid[1].toFixed(5)), Number(centroid[0].toFixed(5))],
|
||||
generatedAt: new Date().toISOString(),
|
||||
},
|
||||
geometry: simplified.geometry,
|
||||
};
|
||||
|
||||
mkdirSync(OUT_DIR, { recursive: true });
|
||||
const path = `${OUT_DIR}/${code.toLowerCase()}.json`;
|
||||
writeFileSync(path, JSON.stringify(out, null, 1) + '\n');
|
||||
console.log(
|
||||
` -> ${path} ${out.properties.areaKm2} km2, ` +
|
||||
`${JSON.stringify(simplified.geometry.coordinates).length} bytes of geometry`
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewrite the barrel module that imports every boundary that exists.
|
||||
*
|
||||
* Same reason as the panorama indexes: static imports let the data be bundled
|
||||
* with the server build, but a city that has not been generated yet would then
|
||||
* be a hard build error. Generating the list from disk keeps a partial build
|
||||
* usable.
|
||||
* @returns {string[]} City codes now in the barrel.
|
||||
*/
|
||||
function writeBarrel() {
|
||||
const entries = readdirSync(OUT_DIR)
|
||||
.filter((name) => name.endsWith('.json') && name !== 'index.js')
|
||||
.sort()
|
||||
.map((name) => ({
|
||||
code: name.replace(/\.json$/, '').toUpperCase(),
|
||||
ident: name.replace(/\.json$/, ''),
|
||||
file: name,
|
||||
}));
|
||||
|
||||
const body =
|
||||
'// Generated by scripts/build-city-boundaries.mjs. Do not edit by hand.\n' +
|
||||
'//\n' +
|
||||
'// Lists only the boundaries that have actually been built, so a partial\n' +
|
||||
'// build still compiles.\n\n' +
|
||||
entries.map((e) => `import ${e.ident} from './${e.file}';`).join('\n') +
|
||||
'\n\nexport const CITY_BOUNDARIES = {\n' +
|
||||
entries.map((e) => ` ${e.code}: ${e.ident},`).join('\n') +
|
||||
'\n};\n';
|
||||
|
||||
writeFileSync(`${OUT_DIR}/index.js`, body);
|
||||
return entries.map((e) => e.code);
|
||||
}
|
||||
|
||||
const only = process.argv.slice(2);
|
||||
for (const [code, config] of Object.entries(CITIES)) {
|
||||
if (only.length && !only.includes(code)) continue;
|
||||
await buildCity(code, config);
|
||||
// Rewritten after each city so an interrupted run leaves a consistent barrel.
|
||||
writeBarrel();
|
||||
}
|
||||
console.log(`\nbarrel: ${writeBarrel().join(', ')}`);
|
||||
console.log('done');
|
||||
@@ -9,7 +9,7 @@
|
||||
// node scripts/build-pano-index.mjs TPHCM DN # only these
|
||||
//
|
||||
// Needs MAPILLARY_ACCESS_TOKEN in .env, and the boundaries built first by
|
||||
// scripts/build-city-boundaries.mjs. Output is src/data/panos/<code>.json,
|
||||
// scripts/build-region-boundaries.mjs. Output is src/data/panos/<code>.json,
|
||||
// which is plain JSON and safe to prune by hand.
|
||||
|
||||
import { mkdirSync, readdirSync, readFileSync, statSync, writeFileSync, existsSync } from 'node:fs';
|
||||
@@ -155,9 +155,11 @@ function thinToGrid(points) {
|
||||
}
|
||||
|
||||
async function buildCity(code, token) {
|
||||
const boundaryPath = `${BOUNDARY_DIR}/${code.toLowerCase()}.json`;
|
||||
const slug = code.toLowerCase();
|
||||
// Boundaries are grouped by province: src/data/boundaries/<province>/<code>.json
|
||||
const boundaryPath = `${BOUNDARY_DIR}/${slug}/${slug}.json`;
|
||||
if (!existsSync(boundaryPath)) {
|
||||
throw new Error(`${code}: missing ${boundaryPath} — run build-city-boundaries.mjs first`);
|
||||
throw new Error(`${code}: missing ${boundaryPath} — run build-region-boundaries.mjs first`);
|
||||
}
|
||||
const boundary = JSON.parse(readFileSync(boundaryPath, 'utf8'));
|
||||
const bbox = boundary.properties.bbox ?? turf.bbox(boundary);
|
||||
@@ -272,18 +274,21 @@ function writeBarrel() {
|
||||
.sort();
|
||||
const entries = files.map((name) => ({
|
||||
code: name.replace(/\.json$/, '').toUpperCase(),
|
||||
ident: name.replace(/\.json$/, ''),
|
||||
// Sanitised because region codes contain hyphens, which are legal in a
|
||||
// filename but not in a bare identifier or an unquoted object key. Emitting
|
||||
// them raw makes the generated barrel a syntax error.
|
||||
ident: name.replace(/\.json$/, '').replace(/[^a-z0-9]/gi, '_'),
|
||||
file: name,
|
||||
}));
|
||||
|
||||
const body =
|
||||
'// Generated by scripts/build-pano-index.mjs. Do not edit by hand.\n' +
|
||||
'//\n' +
|
||||
'// Lists only the city indexes that have actually been built, so a partial\n' +
|
||||
'// build still compiles.\n\n' +
|
||||
'// Lists only the province indexes that have actually been built, so a\n' +
|
||||
'// partial build still compiles.\n\n' +
|
||||
entries.map((e) => `import ${e.ident} from './${e.file}';`).join('\n') +
|
||||
'\n\nexport const PANO_INDEXES = {\n' +
|
||||
entries.map((e) => ` ${e.code}: ${e.ident},`).join('\n') +
|
||||
entries.map((e) => ` ${JSON.stringify(e.code)}: ${e.ident},`).join('\n') +
|
||||
'\n};\n';
|
||||
|
||||
writeFileSync(`${OUT_DIR}/index.js`, body);
|
||||
@@ -294,7 +299,7 @@ let tilesRequested = 0;
|
||||
|
||||
const token = loadToken();
|
||||
const requested = process.argv.slice(2);
|
||||
const codes = requested.length ? requested : ['HN', 'TPHCM', 'DN', 'DL', 'DH'];
|
||||
const codes = requested.length ? requested : ['HN', 'TPHCM', 'DN', 'LD', 'LA'];
|
||||
for (const code of codes) {
|
||||
await buildCity(code, token);
|
||||
// Rewrite after every city, so an interrupted run still leaves a consistent
|
||||
|
||||
@@ -0,0 +1,519 @@
|
||||
// Build the play-area polygon for every region and write it as GeoJSON, then
|
||||
// generate the region tree the rest of the app navigates by.
|
||||
//
|
||||
// Vietnam merged its provinces in mid-2025, so the current administrative
|
||||
// boundary for Ho Chi Minh City covers 36,566 km2 and reaches Vung Tau. That is
|
||||
// not a guessable area: the scoring bands top out at 1km. These are the
|
||||
// pre-merger extents instead, which is also why Duc Hoa sits under Long An
|
||||
// rather than the Tay Ninh it was merged into.
|
||||
//
|
||||
// node scripts/build-region-boundaries.mjs # everything
|
||||
// node scripts/build-region-boundaries.mjs HN DN # only these provinces
|
||||
//
|
||||
// Output:
|
||||
// src/data/boundaries/<code>.json one per resolved leaf, plus one per province
|
||||
// src/data/boundaries/index.js barrel
|
||||
// src/data/regions/index.js the tree: names, parents, centers, bboxes
|
||||
//
|
||||
// Coverage is deliberately partial and grows by adding entries below. A leaf
|
||||
// Nominatim cannot resolve is recorded as unresolved rather than failing the
|
||||
// build -- see the Coverage section of docs/project-overview.md for why absent
|
||||
// coverage is not automatically a defect.
|
||||
|
||||
import { mkdirSync, readdirSync, readFileSync, writeFileSync, existsSync } from 'node:fs';
|
||||
import { dirname } from 'node:path';
|
||||
import * as turf from '@turf/turf';
|
||||
|
||||
const NOMINATIM = 'https://nominatim.openstreetmap.org/search';
|
||||
// Nominatim asks for a real identifier and at most one request per second.
|
||||
const USER_AGENT = 'vngeoguessr/1.0 (https://github.com/tiennm99/vngeoguessr)';
|
||||
const REQUEST_GAP_MS = 1100;
|
||||
|
||||
// Leaves are simplified tightly: at district scale a loose tolerance opens gaps
|
||||
// along shared borders, and scripts/assign-pano-districts.mjs has to decide
|
||||
// which side of those borders every panorama falls on. Province outlines are
|
||||
// only ever drawn at city zoom, so they stay coarse.
|
||||
const LEAF_TOLERANCE = 0.0001; // ~11m
|
||||
const PROVINCE_TOLERANCE = 0.0005; // ~55m
|
||||
|
||||
const OUT_DIR = 'src/data/boundaries';
|
||||
const TREE_DIR = 'src/data/regions';
|
||||
|
||||
// The region tree. One entry per node.
|
||||
//
|
||||
// level 'country' | 'province' | 'district'
|
||||
// query Nominatim free-text lookup. Leaves only; a province is the union
|
||||
// of its children and the country has no polygon at all.
|
||||
// center Optional override. The computed centre of mass of an irregular
|
||||
// outline can land somewhere no one associates with the place, so
|
||||
// the five original entry points keep their hand-picked centres.
|
||||
const REGIONS = {
|
||||
VN: { name: 'Vietnam', level: 'country' },
|
||||
|
||||
// -- Ha Noi ---------------------------------------------------------------
|
||||
// Not merged in 2025, but split here into its 30 pre-2025 units.
|
||||
HN: { name: 'Ha Noi', level: 'province', parent: 'VN', center: [21.0285, 105.8542], legacyBbox: [105.28896, 20.56452, 106.02004, 21.38542] },
|
||||
'HN-BADINH': { name: 'Ba Dinh', level: 'district', parent: 'HN', query: 'Quận Ba Đình, Hà Nội, Việt Nam' },
|
||||
'HN-HOANKIEM': { name: 'Hoan Kiem', level: 'district', parent: 'HN', query: 'Quận Hoàn Kiếm, Hà Nội, Việt Nam' },
|
||||
'HN-TAYHO': { name: 'Tay Ho', level: 'district', parent: 'HN', query: 'Quận Tây Hồ, Hà Nội, Việt Nam' },
|
||||
'HN-LONGBIEN': { name: 'Long Bien', level: 'district', parent: 'HN', query: 'Quận Long Biên, Hà Nội, Việt Nam' },
|
||||
'HN-CAUGIAY': { name: 'Cau Giay', level: 'district', parent: 'HN', query: 'Quận Cầu Giấy, Hà Nội, Việt Nam' },
|
||||
'HN-DONGDA': { name: 'Dong Da', level: 'district', parent: 'HN', query: 'Quận Đống Đa, Hà Nội, Việt Nam' },
|
||||
'HN-HAIBATRUNG': { name: 'Hai Ba Trung', level: 'district', parent: 'HN', query: 'Quận Hai Bà Trưng, Hà Nội, Việt Nam' },
|
||||
'HN-HOANGMAI': { name: 'Hoang Mai', level: 'district', parent: 'HN', query: 'Quận Hoàng Mai, Hà Nội, Việt Nam' },
|
||||
'HN-THANHXUAN': { name: 'Thanh Xuan', level: 'district', parent: 'HN', query: 'Quận Thanh Xuân, Hà Nội, Việt Nam' },
|
||||
'HN-BACTULIEM': { name: 'Bac Tu Liem', level: 'district', parent: 'HN', query: 'Quận Bắc Từ Liêm, Hà Nội, Việt Nam' },
|
||||
'HN-NAMTULIEM': { name: 'Nam Tu Liem', level: 'district', parent: 'HN', query: 'Quận Nam Từ Liêm, Hà Nội, Việt Nam' },
|
||||
'HN-HADONG': { name: 'Ha Dong', level: 'district', parent: 'HN', query: 'Quận Hà Đông, Hà Nội, Việt Nam' },
|
||||
'HN-SONTAY': { name: 'Son Tay', level: 'district', parent: 'HN', query: 'Thị xã Sơn Tây, Hà Nội, Việt Nam' },
|
||||
'HN-BAVI': { name: 'Ba Vi', level: 'district', parent: 'HN', query: 'Huyện Ba Vì, Hà Nội, Việt Nam' },
|
||||
'HN-CHUONGMY': { name: 'Chuong My', level: 'district', parent: 'HN', query: 'Huyện Chương Mỹ, Hà Nội, Việt Nam' },
|
||||
'HN-DANPHUONG': { name: 'Dan Phuong', level: 'district', parent: 'HN', query: 'Huyện Đan Phượng, Hà Nội, Việt Nam' },
|
||||
'HN-DONGANH': { name: 'Dong Anh', level: 'district', parent: 'HN', query: 'Huyện Đông Anh, Hà Nội, Việt Nam' },
|
||||
'HN-GIALAM': { name: 'Gia Lam', level: 'district', parent: 'HN', query: 'Huyện Gia Lâm, Hà Nội, Việt Nam' },
|
||||
'HN-HOAIDUC': { name: 'Hoai Duc', level: 'district', parent: 'HN', query: 'Huyện Hoài Đức, Hà Nội, Việt Nam' },
|
||||
'HN-MELINH': { name: 'Me Linh', level: 'district', parent: 'HN', query: 'Huyện Mê Linh, Hà Nội, Việt Nam' },
|
||||
'HN-MYDUC': { name: 'My Duc', level: 'district', parent: 'HN', query: 'Huyện Mỹ Đức, Hà Nội, Việt Nam' },
|
||||
'HN-PHUXUYEN': { name: 'Phu Xuyen', level: 'district', parent: 'HN', query: 'Huyện Phú Xuyên, Hà Nội, Việt Nam' },
|
||||
'HN-PHUCTHO': { name: 'Phuc Tho', level: 'district', parent: 'HN', query: 'Huyện Phúc Thọ, Hà Nội, Việt Nam' },
|
||||
'HN-QUOCOAI': { name: 'Quoc Oai', level: 'district', parent: 'HN', query: 'Huyện Quốc Oai, Hà Nội, Việt Nam' },
|
||||
'HN-SOCSON': { name: 'Soc Son', level: 'district', parent: 'HN', query: 'Huyện Sóc Sơn, Hà Nội, Việt Nam' },
|
||||
'HN-THACHTHAT': { name: 'Thach That', level: 'district', parent: 'HN', query: 'Huyện Thạch Thất, Hà Nội, Việt Nam' },
|
||||
'HN-THANHOAI': { name: 'Thanh Oai', level: 'district', parent: 'HN', query: 'Huyện Thanh Oai, Hà Nội, Việt Nam' },
|
||||
'HN-THANHTRI': { name: 'Thanh Tri', level: 'district', parent: 'HN', query: 'Huyện Thanh Trì, Hà Nội, Việt Nam' },
|
||||
'HN-THUONGTIN': { name: 'Thuong Tin', level: 'district', parent: 'HN', query: 'Huyện Thường Tín, Hà Nội, Việt Nam' },
|
||||
'HN-UNGHOA': { name: 'Ung Hoa', level: 'district', parent: 'HN', query: 'Huyện Ứng Hòa, Hà Nội, Việt Nam' },
|
||||
|
||||
// -- Ho Chi Minh ----------------------------------------------------------
|
||||
// Districts 2 and 9 are absent on purpose: both merged into Thu Duc in 2021.
|
||||
TPHCM: { name: 'Ho Chi Minh', level: 'province', parent: 'VN', center: [10.8231, 106.6297], legacyBbox: [106.46356, 10.35828, 107.02758, 10.92934] },
|
||||
'TPHCM-Q1': { name: 'District 1', level: 'district', parent: 'TPHCM', query: 'Quận 1, Thành phố Hồ Chí Minh, Việt Nam' },
|
||||
'TPHCM-Q3': { name: 'District 3', level: 'district', parent: 'TPHCM', query: 'Quận 3, Thành phố Hồ Chí Minh, Việt Nam' },
|
||||
'TPHCM-Q4': { name: 'District 4', level: 'district', parent: 'TPHCM', query: 'Quận 4, Thành phố Hồ Chí Minh, Việt Nam' },
|
||||
'TPHCM-Q5': { name: 'District 5', level: 'district', parent: 'TPHCM', query: 'Quận 5, Thành phố Hồ Chí Minh, Việt Nam' },
|
||||
'TPHCM-Q6': { name: 'District 6', level: 'district', parent: 'TPHCM', query: 'Quận 6, Thành phố Hồ Chí Minh, Việt Nam' },
|
||||
'TPHCM-Q7': { name: 'District 7', level: 'district', parent: 'TPHCM', query: 'Quận 7, Thành phố Hồ Chí Minh, Việt Nam' },
|
||||
'TPHCM-Q8': { name: 'District 8', level: 'district', parent: 'TPHCM', query: 'Quận 8, Thành phố Hồ Chí Minh, Việt Nam' },
|
||||
'TPHCM-Q10': { name: 'District 10', level: 'district', parent: 'TPHCM', query: 'Quận 10, Thành phố Hồ Chí Minh, Việt Nam' },
|
||||
'TPHCM-Q11': { name: 'District 11', level: 'district', parent: 'TPHCM', query: 'Quận 11, Thành phố Hồ Chí Minh, Việt Nam' },
|
||||
'TPHCM-Q12': { name: 'District 12', level: 'district', parent: 'TPHCM', query: 'Quận 12, Thành phố Hồ Chí Minh, Việt Nam' },
|
||||
'TPHCM-BINHTAN': { name: 'Binh Tan', level: 'district', parent: 'TPHCM', query: 'Quận Bình Tân, Thành phố Hồ Chí Minh, Việt Nam' },
|
||||
'TPHCM-BINHTHANH': { name: 'Binh Thanh', level: 'district', parent: 'TPHCM', query: 'Quận Bình Thạnh, Thành phố Hồ Chí Minh, Việt Nam' },
|
||||
'TPHCM-GOVAP': { name: 'Go Vap', level: 'district', parent: 'TPHCM', query: 'Quận Gò Vấp, Thành phố Hồ Chí Minh, Việt Nam' },
|
||||
'TPHCM-PHUNHUAN': { name: 'Phu Nhuan', level: 'district', parent: 'TPHCM', query: 'Quận Phú Nhuận, Thành phố Hồ Chí Minh, Việt Nam' },
|
||||
'TPHCM-TANBINH': { name: 'Tan Binh', level: 'district', parent: 'TPHCM', query: 'Quận Tân Bình, Thành phố Hồ Chí Minh, Việt Nam' },
|
||||
'TPHCM-TANPHU': { name: 'Tan Phu', level: 'district', parent: 'TPHCM', query: 'Quận Tân Phú, Thành phố Hồ Chí Minh, Việt Nam' },
|
||||
'TPHCM-THUDUC': { name: 'Thu Duc', level: 'district', parent: 'TPHCM', query: 'Thành phố Thủ Đức, Việt Nam' },
|
||||
'TPHCM-BINHCHANH': { name: 'Binh Chanh', level: 'district', parent: 'TPHCM', query: 'Huyện Bình Chánh, Thành phố Hồ Chí Minh, Việt Nam' },
|
||||
'TPHCM-CANGIO': { name: 'Can Gio', level: 'district', parent: 'TPHCM', query: 'Huyện Cần Giờ, Thành phố Hồ Chí Minh, Việt Nam' },
|
||||
// Cu Chi did not resolve for the pre-tree build either, which is why the old
|
||||
// tphcm.json carried "missingParts": 1 and no panorama in the index sits in
|
||||
// Cu Chi. Kept here so the gap stays visible rather than silently dropped.
|
||||
'TPHCM-CUCHI': { name: 'Cu Chi', level: 'district', parent: 'TPHCM', query: 'Huyện Củ Chi, Thành phố Hồ Chí Minh, Việt Nam' },
|
||||
'TPHCM-HOCMON': { name: 'Hoc Mon', level: 'district', parent: 'TPHCM', query: 'Huyện Hóc Môn, Thành phố Hồ Chí Minh, Việt Nam' },
|
||||
'TPHCM-NHABE': { name: 'Nha Be', level: 'district', parent: 'TPHCM', query: 'Huyện Nhà Bè, Thành phố Hồ Chí Minh, Việt Nam' },
|
||||
|
||||
// -- Da Nang --------------------------------------------------------------
|
||||
// Hoang Sa is administratively part of Da Nang but is a disputed offshore
|
||||
// island group with no street imagery, so it is deliberately left out.
|
||||
DN: { name: 'Da Nang', level: 'province', parent: 'VN', center: [16.0544, 108.2022], legacyBbox: [107.81854, 15.91799, 108.33864, 16.2255] },
|
||||
'DN-HAICHAU': { name: 'Hai Chau', level: 'district', parent: 'DN', query: 'Quận Hải Châu, Đà Nẵng, Việt Nam' },
|
||||
'DN-THANHKHE': { name: 'Thanh Khe', level: 'district', parent: 'DN', query: 'Quận Thanh Khê, Đà Nẵng, Việt Nam' },
|
||||
'DN-SONTRA': { name: 'Son Tra', level: 'district', parent: 'DN', query: 'Quận Sơn Trà, Đà Nẵng, Việt Nam' },
|
||||
'DN-NGUHANHSON': { name: 'Ngu Hanh Son', level: 'district', parent: 'DN', query: 'Quận Ngũ Hành Sơn, Đà Nẵng, Việt Nam' },
|
||||
'DN-LIENCHIEU': { name: 'Lien Chieu', level: 'district', parent: 'DN', query: 'Quận Liên Chiểu, Đà Nẵng, Việt Nam' },
|
||||
'DN-CAMLE': { name: 'Cam Le', level: 'district', parent: 'DN', query: 'Quận Cẩm Lệ, Đà Nẵng, Việt Nam' },
|
||||
'DN-HOAVANG': { name: 'Hoa Vang', level: 'district', parent: 'DN', query: 'Huyện Hòa Vang, Đà Nẵng, Việt Nam' },
|
||||
|
||||
// -- Lam Dong / Long An ---------------------------------------------------
|
||||
// One town each. The leaf codes stay bare because their leaderboard keys
|
||||
// already exist under those names.
|
||||
LD: { name: 'Lam Dong', level: 'province', parent: 'VN', partialCoverage: 'one town covered', legacyBbox: [108.31521, 11.80798, 108.5944, 12.00855] },
|
||||
DL: { name: 'Da Lat', level: 'district', parent: 'LD', query: 'Thành phố Đà Lạt, Việt Nam', center: [11.9404, 108.4583] },
|
||||
|
||||
LA: { name: 'Long An', level: 'province', parent: 'VN', partialCoverage: 'one town covered', legacyBbox: [106.27082, 10.7409, 106.53287, 11.02578] },
|
||||
DH: { name: 'Duc Hoa', level: 'district', parent: 'LA', query: 'Đức Hòa, Việt Nam', center: [10.8888, 106.3825] },
|
||||
};
|
||||
|
||||
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||
|
||||
/**
|
||||
* Province directory a region's polygon lives in.
|
||||
*
|
||||
* Boundaries are grouped by province: 61 leaves plus 5 provinces in one flat
|
||||
* directory is unreadable, and every leaf belongs to exactly one province.
|
||||
* @param {string} code Region code.
|
||||
* @returns {string} Directory name, lowercased province code.
|
||||
*/
|
||||
function folderFor(code) {
|
||||
const config = REGIONS[code];
|
||||
// The country has no polygon of its own, so it has no home directory either.
|
||||
if (config.level === 'country') return null;
|
||||
const province = config.level === 'province' ? code : config.parent;
|
||||
return province.toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Path a region's polygon lives under, relative to OUT_DIR.
|
||||
* @param {string} code Region code.
|
||||
* @returns {string|null} Path, or null for a region with no polygon.
|
||||
*/
|
||||
function fileFor(code) {
|
||||
const folder = folderFor(code);
|
||||
return folder ? `${folder}/${code.toLowerCase()}.json` : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* A JS identifier safe to use in a generated import.
|
||||
*
|
||||
* Region codes are hyphenated (TPHCM-Q7), and a hyphen is legal in a filename
|
||||
* but not in a bare identifier or an unquoted object key. Without this the
|
||||
* generated barrel is a syntax error and nothing in the app compiles.
|
||||
* @param {string} file Filename, with or without extension.
|
||||
* @returns {string} Identifier.
|
||||
*/
|
||||
function identFor(file) {
|
||||
return file.replace(/\.json$/, '').replace(/[^a-z0-9]/gi, '_');
|
||||
}
|
||||
|
||||
/** Direct children of a node, in declaration order. */
|
||||
function childrenOf(code) {
|
||||
return Object.keys(REGIONS).filter((key) => REGIONS[key].parent === code);
|
||||
}
|
||||
|
||||
/**
|
||||
* The unqualified form of a query: "Huyện Ba Vì, Hà Nội, Việt Nam" becomes
|
||||
* "Huyện Ba Vì, Việt Nam".
|
||||
*
|
||||
* The 2025 restructure abolished the district level, and OSM has followed it:
|
||||
* the pre-2025 units now exist as boundary/historic relations whose rendered
|
||||
* parent is the CURRENT province, not the historic one. Qualifying such a query
|
||||
* with the historic city name therefore matches nothing, while the bare form
|
||||
* finds the relation. Quan (urban districts) mostly still match qualified, so
|
||||
* both forms are tried.
|
||||
* @param {string} query Fully qualified query.
|
||||
* @returns {string} Bare form.
|
||||
*/
|
||||
function bareForm(query) {
|
||||
const head = query.split(',')[0].trim();
|
||||
return `${head}, Việt Nam`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Look up one administrative area and return it as a turf feature.
|
||||
*
|
||||
* `box` is the parent's pre-2025 extent. It is the guard against the bare query
|
||||
* form matching a same-named unit elsewhere in the country: Nominatim happily
|
||||
* returns "Ba Vì District, Phú Thọ Province" for a Ha Noi lookup, and only the
|
||||
* geometry tells you whether it is the right Ba Vì.
|
||||
* @param {string} query Nominatim free-text query.
|
||||
* @param {number[]} box Parent bbox [west, south, east, north].
|
||||
* @returns {Promise<Object|null>} Feature, or null when nothing usable matched.
|
||||
*/
|
||||
async function lookupArea(query, box) {
|
||||
const url = `${NOMINATIM}?q=${encodeURIComponent(query)}&format=json&polygon_geojson=1&limit=10`;
|
||||
const response = await fetch(url, {
|
||||
headers: { 'User-Agent': USER_AGENT, 'Accept-Language': 'en' },
|
||||
});
|
||||
if (!response.ok) throw new Error(`Nominatim ${response.status} for "${query}"`);
|
||||
|
||||
// A cemetery, a bus stop or a hospital of the same name can outrank the
|
||||
// district, so scan the candidates for one that is actually a boundary. Both
|
||||
// 'administrative' and 'historic' count: the pre-2025 units are historic now.
|
||||
for (const hit of await response.json()) {
|
||||
if (!hit?.geojson) continue;
|
||||
if (!['Polygon', 'MultiPolygon'].includes(hit.geojson.type)) continue;
|
||||
if (hit.class !== 'boundary') continue;
|
||||
if (box) {
|
||||
const [lat, lon] = [Number(hit.lat), Number(hit.lon)];
|
||||
const inside = lon >= box[0] && lon <= box[2] && lat >= box[1] && lat <= box[3];
|
||||
if (!inside) continue;
|
||||
}
|
||||
return turf.feature(hit.geojson, { osm: `${hit.osm_type}/${hit.osm_id}` });
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write one region's polygon to disk.
|
||||
* @param {string} code Region code.
|
||||
* @param {Object} geometry GeoJSON geometry.
|
||||
* @param {Object} extra Extra properties to record.
|
||||
* @returns {Object} The written feature.
|
||||
*/
|
||||
function writeBoundary(code, geometry, extra) {
|
||||
const config = REGIONS[code];
|
||||
const feature = turf.feature(geometry);
|
||||
const bbox = turf.bbox(feature).map((n) => Number(n.toFixed(5)));
|
||||
const centroid = turf.centerOfMass(feature).geometry.coordinates;
|
||||
|
||||
const out = {
|
||||
type: 'Feature',
|
||||
properties: {
|
||||
code,
|
||||
name: config.name,
|
||||
level: config.level,
|
||||
parent: config.parent ?? null,
|
||||
// Recorded so a future reader knows why this is not the current
|
||||
// administrative boundary.
|
||||
basis: 'pre-2025-merger extent, from OSM boundary/historic relations',
|
||||
areaKm2: Number((turf.area(feature) / 1e6).toFixed(1)),
|
||||
bbox,
|
||||
center: config.center ?? [Number(centroid[1].toFixed(5)), Number(centroid[0].toFixed(5))],
|
||||
generatedAt: new Date().toISOString(),
|
||||
...extra,
|
||||
},
|
||||
geometry,
|
||||
};
|
||||
|
||||
const path = `${OUT_DIR}/${fileFor(code)}`;
|
||||
mkdirSync(dirname(path), { recursive: true });
|
||||
writeFileSync(path, JSON.stringify(out, null, 1) + '\n');
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve one leaf and write its polygon.
|
||||
* @param {string} code Leaf code.
|
||||
* @returns {Promise<boolean>} True when it resolved.
|
||||
*/
|
||||
async function buildLeaf(code) {
|
||||
const config = REGIONS[code];
|
||||
const box = REGIONS[config.parent]?.legacyBbox;
|
||||
|
||||
// Qualified first, then bare. See bareForm() for why one form is not enough.
|
||||
const forms = [config.query];
|
||||
const bare = bareForm(config.query);
|
||||
if (bare !== config.query) forms.push(bare);
|
||||
|
||||
let feature = null;
|
||||
for (const form of forms) {
|
||||
try {
|
||||
feature = await lookupArea(form, box);
|
||||
} catch (error) {
|
||||
console.error(` ${code}: ${error.message}`);
|
||||
}
|
||||
await sleep(REQUEST_GAP_MS);
|
||||
if (feature) break;
|
||||
}
|
||||
|
||||
if (!feature) {
|
||||
// Not fatal. An unresolved leaf is recorded in the tree and excluded from
|
||||
// play; see the Coverage note in docs/project-overview.md.
|
||||
console.warn(` ${code} (${config.name}): no boundary found — UNRESOLVED`);
|
||||
return false;
|
||||
}
|
||||
|
||||
const simplified = turf.simplify(feature, { tolerance: LEAF_TOLERANCE, highQuality: true });
|
||||
const out = writeBoundary(code, simplified.geometry, { osm: feature.properties.osm });
|
||||
console.log(` ${code} (${config.name}): ${out.properties.areaKm2} km2`);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Union a province's resolved children and write the result.
|
||||
*
|
||||
* A province with a single child copies that child's geometry verbatim rather
|
||||
* than re-simplifying it at the coarser province tolerance: the two would then
|
||||
* disagree along the border, and every panorama in the gap would be assigned to
|
||||
* the province but to no district.
|
||||
* @param {string} code Province code.
|
||||
* @returns {number} Children that could not be included.
|
||||
*/
|
||||
function buildProvince(code) {
|
||||
const children = childrenOf(code);
|
||||
const resolved = children.filter((child) => existsSync(`${OUT_DIR}/${fileFor(child)}`));
|
||||
const missing = children.length - resolved.length;
|
||||
|
||||
if (resolved.length === 0) {
|
||||
console.warn(` ${code}: no children resolved, nothing to write`);
|
||||
return missing;
|
||||
}
|
||||
|
||||
const features = resolved.map((child) =>
|
||||
JSON.parse(readFileSync(`${OUT_DIR}/${fileFor(child)}`, 'utf8'))
|
||||
);
|
||||
|
||||
let geometry;
|
||||
if (features.length === 1) {
|
||||
geometry = features[0].geometry;
|
||||
} else {
|
||||
let merged = features[0];
|
||||
for (const feature of features.slice(1)) {
|
||||
merged = turf.union(turf.featureCollection([merged, feature]));
|
||||
}
|
||||
geometry = turf.simplify(merged, {
|
||||
tolerance: PROVINCE_TOLERANCE,
|
||||
highQuality: true,
|
||||
}).geometry;
|
||||
}
|
||||
|
||||
const out = writeBoundary(code, geometry, {
|
||||
parts: children.length,
|
||||
missingParts: missing,
|
||||
});
|
||||
console.log(
|
||||
` ${code}: ${out.properties.areaKm2} km2 from ${resolved.length}/${children.length} children` +
|
||||
(missing ? ` (${missing} unresolved)` : '')
|
||||
);
|
||||
return missing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewrite the barrel that imports every boundary on disk.
|
||||
*
|
||||
* Generated from the directory rather than a declared list so a partial build
|
||||
* still compiles. Identifiers are sanitised and keys quoted because region
|
||||
* codes contain hyphens.
|
||||
* @returns {string[]} Region codes now in the barrel.
|
||||
*/
|
||||
function writeBoundaryBarrel() {
|
||||
// Grouped by province, so walk one level of subdirectories.
|
||||
const entries = readdirSync(OUT_DIR, { withFileTypes: true })
|
||||
.filter((entry) => entry.isDirectory())
|
||||
.flatMap((dir) =>
|
||||
readdirSync(`${OUT_DIR}/${dir.name}`)
|
||||
.filter((name) => name.endsWith('.json'))
|
||||
.map((name) => `${dir.name}/${name}`)
|
||||
)
|
||||
.sort()
|
||||
.map((file) => ({
|
||||
code: file.split('/').pop().replace(/\.json$/, '').toUpperCase(),
|
||||
ident: identFor(file.split('/').pop()),
|
||||
file,
|
||||
}));
|
||||
|
||||
const body =
|
||||
'// Generated by scripts/build-region-boundaries.mjs. Do not edit by hand.\n' +
|
||||
'//\n' +
|
||||
'// Lists only the boundaries that have actually been built, so a partial\n' +
|
||||
'// build still compiles.\n\n' +
|
||||
entries.map((e) => `import ${e.ident} from './${e.file}';`).join('\n') +
|
||||
'\n\nexport const REGION_BOUNDARIES = {\n' +
|
||||
entries.map((e) => ` ${JSON.stringify(e.code)}: ${e.ident},`).join('\n') +
|
||||
'\n};\n';
|
||||
|
||||
writeFileSync(`${OUT_DIR}/index.js`, body);
|
||||
return entries.map((e) => e.code);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the region tree.
|
||||
*
|
||||
* Panorama counts deliberately live in a separate generated file written by
|
||||
* scripts/assign-pano-districts.mjs, so each generated file has exactly one
|
||||
* writer. src/lib/regions.js joins the two.
|
||||
* @param {Object} resolvedState Per-code resolution info.
|
||||
*/
|
||||
function writeTree(resolvedState) {
|
||||
const nodes = Object.entries(REGIONS).map(([code, config]) => {
|
||||
const state = resolvedState[code] ?? {};
|
||||
const node = {
|
||||
code,
|
||||
name: config.name,
|
||||
parent: config.parent ?? null,
|
||||
level: config.level,
|
||||
children: childrenOf(code),
|
||||
};
|
||||
if (state.center) node.center = state.center;
|
||||
if (state.bbox) node.bbox = state.bbox;
|
||||
if (config.partialCoverage) node.partialCoverage = config.partialCoverage;
|
||||
if (state.coverage) node.coverage = state.coverage;
|
||||
if (state.missingParts) node.missingParts = state.missingParts;
|
||||
return node;
|
||||
});
|
||||
|
||||
const body =
|
||||
'// Generated by scripts/build-region-boundaries.mjs. Do not edit by hand.\n' +
|
||||
'//\n' +
|
||||
'// The region tree: country > province > district. Safe to import from a\n' +
|
||||
'// client component -- it holds names and extents, never panorama data.\n' +
|
||||
'//\n' +
|
||||
'// A node marked coverage: \'unresolved\' has no boundary. That is recorded,\n' +
|
||||
'// not a build failure; see the Coverage note in docs/project-overview.md.\n\n' +
|
||||
'export const REGIONS = {\n' +
|
||||
nodes
|
||||
.map((node) => ` ${JSON.stringify(node.code)}: ${JSON.stringify(node)},`)
|
||||
.join('\n') +
|
||||
'\n};\n';
|
||||
|
||||
mkdirSync(TREE_DIR, { recursive: true });
|
||||
writeFileSync(`${TREE_DIR}/index.js`, body);
|
||||
}
|
||||
|
||||
// -- run ---------------------------------------------------------------------
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
// --regenerate rebuilds provinces, the barrel and the tree from the leaf
|
||||
// polygons already on disk, without touching Nominatim. Use it after moving
|
||||
// files or changing how the outputs are shaped; a full refetch is 60+ requests
|
||||
// to a courtesy-rate-limited public service and should be reserved for actually
|
||||
// refreshing the source data.
|
||||
const regenerate = args.includes('--regenerate');
|
||||
const only = args.filter((arg) => !arg.startsWith('--'));
|
||||
|
||||
const provinces = Object.keys(REGIONS).filter((code) => REGIONS[code].level === 'province');
|
||||
const targets = only.length ? provinces.filter((code) => only.includes(code)) : provinces;
|
||||
|
||||
const resolvedState = {};
|
||||
let unresolved = 0;
|
||||
|
||||
for (const province of targets) {
|
||||
console.log(`\n${province} — ${REGIONS[province].name}`);
|
||||
for (const leaf of childrenOf(province)) {
|
||||
const ok = regenerate
|
||||
? existsSync(`${OUT_DIR}/${fileFor(leaf)}`)
|
||||
: await buildLeaf(leaf);
|
||||
if (!ok) {
|
||||
resolvedState[leaf] = { coverage: 'unresolved' };
|
||||
unresolved++;
|
||||
}
|
||||
}
|
||||
const missing = buildProvince(province);
|
||||
if (missing) resolvedState[province] = { missingParts: missing };
|
||||
// Rewritten after each province so an interrupted run leaves a consistent barrel.
|
||||
writeBoundaryBarrel();
|
||||
}
|
||||
|
||||
// Read back what landed on disk, so the tree records the real extents rather
|
||||
// than what the build hoped for.
|
||||
for (const code of Object.keys(REGIONS)) {
|
||||
const relative = fileFor(code);
|
||||
if (!relative) continue;
|
||||
const path = `${OUT_DIR}/${relative}`;
|
||||
if (!existsSync(path)) continue;
|
||||
const { properties } = JSON.parse(readFileSync(path, 'utf8'));
|
||||
resolvedState[code] = {
|
||||
...resolvedState[code],
|
||||
center: properties.center,
|
||||
bbox: properties.bbox,
|
||||
};
|
||||
}
|
||||
|
||||
// The country has no polygon of its own: the real Vietnam outline is vastly
|
||||
// larger than the covered area and would be misleading on a map. Its extent is
|
||||
// the envelope of the provinces that actually resolved.
|
||||
const provinceBoxes = provinces.map((code) => resolvedState[code]?.bbox).filter(Boolean);
|
||||
if (provinceBoxes.length) {
|
||||
const bbox = [
|
||||
Math.min(...provinceBoxes.map((b) => b[0])),
|
||||
Math.min(...provinceBoxes.map((b) => b[1])),
|
||||
Math.max(...provinceBoxes.map((b) => b[2])),
|
||||
Math.max(...provinceBoxes.map((b) => b[3])),
|
||||
].map((n) => Number(n.toFixed(5)));
|
||||
resolvedState.VN = {
|
||||
bbox,
|
||||
center: [
|
||||
Number(((bbox[1] + bbox[3]) / 2).toFixed(5)),
|
||||
Number(((bbox[0] + bbox[2]) / 2).toFixed(5)),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const codes = writeBoundaryBarrel();
|
||||
writeTree(resolvedState);
|
||||
|
||||
console.log(`\nbarrel: ${codes.length} boundaries`);
|
||||
console.log(`tree: ${Object.keys(REGIONS).length} nodes -> ${TREE_DIR}/index.js`);
|
||||
if (unresolved) console.log(`unresolved leaves: ${unresolved} (recorded, not fatal)`);
|
||||
console.log('done');
|
||||
@@ -1,5 +1,5 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import { CITY_BOUNDARIES } from '../../../../data/boundaries/index.js';
|
||||
import { REGION_BOUNDARIES } from '../../../../data/boundaries/index.js';
|
||||
import { PANO_INDEXES } from '../../../../data/panos/index.js';
|
||||
|
||||
// Serves a city's outline and its panorama locations for the coverage debug
|
||||
@@ -31,7 +31,7 @@ export async function GET(request) {
|
||||
const { searchParams } = new URL(request.url);
|
||||
const code = (searchParams.get('city') || '').toUpperCase();
|
||||
|
||||
const boundary = CITY_BOUNDARIES[code];
|
||||
const boundary = REGION_BOUNDARIES[code];
|
||||
const index = PANO_INDEXES[code];
|
||||
if (!boundary || !index) {
|
||||
return NextResponse.json(
|
||||
|
||||
@@ -1,457 +0,0 @@
|
||||
{
|
||||
"type": "Feature",
|
||||
"properties": {
|
||||
"code": "DH",
|
||||
"name": "Duc Hoa",
|
||||
"basis": "pre-2025-merger extent, unioned from OSM boundary/historic relations",
|
||||
"parts": 1,
|
||||
"missingParts": 0,
|
||||
"areaKm2": 427.4,
|
||||
"bbox": [
|
||||
106.27082,
|
||||
10.7409,
|
||||
106.53287,
|
||||
11.02578
|
||||
],
|
||||
"center": [
|
||||
10.88904,
|
||||
106.40896
|
||||
],
|
||||
"generatedAt": "2026-08-30T04:03:11.323Z"
|
||||
},
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [
|
||||
[
|
||||
[
|
||||
106.2708229,
|
||||
11.0143718
|
||||
],
|
||||
[
|
||||
106.2716241,
|
||||
11.0123102
|
||||
],
|
||||
[
|
||||
106.2828627,
|
||||
11.00478
|
||||
],
|
||||
[
|
||||
106.2868812,
|
||||
10.9935494
|
||||
],
|
||||
[
|
||||
106.2863675,
|
||||
10.9908216
|
||||
],
|
||||
[
|
||||
106.2831164,
|
||||
10.9860504
|
||||
],
|
||||
[
|
||||
106.2737989,
|
||||
10.9818653
|
||||
],
|
||||
[
|
||||
106.2723237,
|
||||
10.9789268
|
||||
],
|
||||
[
|
||||
106.272364,
|
||||
10.9766213
|
||||
],
|
||||
[
|
||||
106.277373,
|
||||
10.97434
|
||||
],
|
||||
[
|
||||
106.288671,
|
||||
10.973062
|
||||
],
|
||||
[
|
||||
106.28977,
|
||||
10.971577
|
||||
],
|
||||
[
|
||||
106.283979,
|
||||
10.957844
|
||||
],
|
||||
[
|
||||
106.283757,
|
||||
10.955816
|
||||
],
|
||||
[
|
||||
106.285993,
|
||||
10.952844
|
||||
],
|
||||
[
|
||||
106.296165,
|
||||
10.944885
|
||||
],
|
||||
[
|
||||
106.299939,
|
||||
10.940861
|
||||
],
|
||||
[
|
||||
106.303055,
|
||||
10.919617
|
||||
],
|
||||
[
|
||||
106.302771,
|
||||
10.911197
|
||||
],
|
||||
[
|
||||
106.3035921,
|
||||
10.9087361
|
||||
],
|
||||
[
|
||||
106.3053432,
|
||||
10.9064512
|
||||
],
|
||||
[
|
||||
106.3103245,
|
||||
10.9044218
|
||||
],
|
||||
[
|
||||
106.328342,
|
||||
10.898802
|
||||
],
|
||||
[
|
||||
106.3355797,
|
||||
10.893552
|
||||
],
|
||||
[
|
||||
106.339096,
|
||||
10.889392
|
||||
],
|
||||
[
|
||||
106.340046,
|
||||
10.886711
|
||||
],
|
||||
[
|
||||
106.339786,
|
||||
10.883923
|
||||
],
|
||||
[
|
||||
106.334568,
|
||||
10.87386
|
||||
],
|
||||
[
|
||||
106.335809,
|
||||
10.868353
|
||||
],
|
||||
[
|
||||
106.342127,
|
||||
10.861411
|
||||
],
|
||||
[
|
||||
106.341553,
|
||||
10.856251
|
||||
],
|
||||
[
|
||||
106.338006,
|
||||
10.849102
|
||||
],
|
||||
[
|
||||
106.33848,
|
||||
10.845878
|
||||
],
|
||||
[
|
||||
106.34176,
|
||||
10.843466
|
||||
],
|
||||
[
|
||||
106.358607,
|
||||
10.841727
|
||||
],
|
||||
[
|
||||
106.358825,
|
||||
10.839626
|
||||
],
|
||||
[
|
||||
106.356896,
|
||||
10.836604
|
||||
],
|
||||
[
|
||||
106.356196,
|
||||
10.833671
|
||||
],
|
||||
[
|
||||
106.356687,
|
||||
10.829524
|
||||
],
|
||||
[
|
||||
106.360126,
|
||||
10.822964
|
||||
],
|
||||
[
|
||||
106.364083,
|
||||
10.820279
|
||||
],
|
||||
[
|
||||
106.367052,
|
||||
10.819551
|
||||
],
|
||||
[
|
||||
106.371178,
|
||||
10.820615
|
||||
],
|
||||
[
|
||||
106.378262,
|
||||
10.826691
|
||||
],
|
||||
[
|
||||
106.382717,
|
||||
10.826341
|
||||
],
|
||||
[
|
||||
106.386601,
|
||||
10.823693
|
||||
],
|
||||
[
|
||||
106.390408,
|
||||
10.818871
|
||||
],
|
||||
[
|
||||
106.391742,
|
||||
10.815266
|
||||
],
|
||||
[
|
||||
106.3904002,
|
||||
10.8038032
|
||||
],
|
||||
[
|
||||
106.3908291,
|
||||
10.8006586
|
||||
],
|
||||
[
|
||||
106.394357,
|
||||
10.795162
|
||||
],
|
||||
[
|
||||
106.398697,
|
||||
10.79206
|
||||
],
|
||||
[
|
||||
106.413741,
|
||||
10.787096
|
||||
],
|
||||
[
|
||||
106.415613,
|
||||
10.788397
|
||||
],
|
||||
[
|
||||
106.417142,
|
||||
10.793882
|
||||
],
|
||||
[
|
||||
106.418757,
|
||||
10.795691
|
||||
],
|
||||
[
|
||||
106.421691,
|
||||
10.796411
|
||||
],
|
||||
[
|
||||
106.426675,
|
||||
10.794485
|
||||
],
|
||||
[
|
||||
106.4287958,
|
||||
10.7917536
|
||||
],
|
||||
[
|
||||
106.4288278,
|
||||
10.7880422
|
||||
],
|
||||
[
|
||||
106.4272052,
|
||||
10.7844262
|
||||
],
|
||||
[
|
||||
106.4225716,
|
||||
10.7820609
|
||||
],
|
||||
[
|
||||
106.4233589,
|
||||
10.7781153
|
||||
],
|
||||
[
|
||||
106.4245686,
|
||||
10.7770535
|
||||
],
|
||||
[
|
||||
106.4242682,
|
||||
10.7753909
|
||||
],
|
||||
[
|
||||
106.4298767,
|
||||
10.7752538
|
||||
],
|
||||
[
|
||||
106.4290237,
|
||||
10.7732197
|
||||
],
|
||||
[
|
||||
106.4313573,
|
||||
10.7720498
|
||||
],
|
||||
[
|
||||
106.4314485,
|
||||
10.7726874
|
||||
],
|
||||
[
|
||||
106.4340824,
|
||||
10.7708482
|
||||
],
|
||||
[
|
||||
106.4386349,
|
||||
10.7640713
|
||||
],
|
||||
[
|
||||
106.4385885,
|
||||
10.7629011
|
||||
],
|
||||
[
|
||||
106.4395589,
|
||||
10.7627403
|
||||
],
|
||||
[
|
||||
106.4426401,
|
||||
10.7581245
|
||||
],
|
||||
[
|
||||
106.4488692,
|
||||
10.7629376
|
||||
],
|
||||
[
|
||||
106.4661748,
|
||||
10.740899
|
||||
],
|
||||
[
|
||||
106.4722331,
|
||||
10.7455255
|
||||
],
|
||||
[
|
||||
106.4635618,
|
||||
10.7565046
|
||||
],
|
||||
[
|
||||
106.4885044,
|
||||
10.7754906
|
||||
],
|
||||
[
|
||||
106.5045619,
|
||||
10.7889222
|
||||
],
|
||||
[
|
||||
106.5200679,
|
||||
10.8522195
|
||||
],
|
||||
[
|
||||
106.5328728,
|
||||
10.8999003
|
||||
],
|
||||
[
|
||||
106.5325466,
|
||||
10.9014489
|
||||
],
|
||||
[
|
||||
106.5315364,
|
||||
10.9026658
|
||||
],
|
||||
[
|
||||
106.4535377,
|
||||
10.9419195
|
||||
],
|
||||
[
|
||||
106.4118982,
|
||||
10.973542
|
||||
],
|
||||
[
|
||||
106.3731933,
|
||||
10.9696228
|
||||
],
|
||||
[
|
||||
106.3511251,
|
||||
10.9988937
|
||||
],
|
||||
[
|
||||
106.3455329,
|
||||
10.9947858
|
||||
],
|
||||
[
|
||||
106.3430053,
|
||||
11.0012811
|
||||
],
|
||||
[
|
||||
106.3378339,
|
||||
11.0052375
|
||||
],
|
||||
[
|
||||
106.327824,
|
||||
11.0098801
|
||||
],
|
||||
[
|
||||
106.3120618,
|
||||
11.0199771
|
||||
],
|
||||
[
|
||||
106.300806,
|
||||
11.0226745
|
||||
],
|
||||
[
|
||||
106.2976075,
|
||||
11.0240541
|
||||
],
|
||||
[
|
||||
106.296914,
|
||||
11.0257756
|
||||
],
|
||||
[
|
||||
106.2943806,
|
||||
11.0250353
|
||||
],
|
||||
[
|
||||
106.2924536,
|
||||
11.0255702
|
||||
],
|
||||
[
|
||||
106.2914359,
|
||||
11.0253081
|
||||
],
|
||||
[
|
||||
106.2910353,
|
||||
11.0234166
|
||||
],
|
||||
[
|
||||
106.2873844,
|
||||
11.0245535
|
||||
],
|
||||
[
|
||||
106.2880078,
|
||||
11.0219182
|
||||
],
|
||||
[
|
||||
106.2841263,
|
||||
11.0220866
|
||||
],
|
||||
[
|
||||
106.2790618,
|
||||
11.0158957
|
||||
],
|
||||
[
|
||||
106.2764274,
|
||||
11.0198205
|
||||
],
|
||||
[
|
||||
106.2738652,
|
||||
11.0216199
|
||||
],
|
||||
[
|
||||
106.2708229,
|
||||
11.0143718
|
||||
]
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,682 @@
|
||||
{
|
||||
"type": "Feature",
|
||||
"properties": {
|
||||
"code": "DN-CAMLE",
|
||||
"name": "Cam Le",
|
||||
"level": "district",
|
||||
"parent": "DN",
|
||||
"basis": "pre-2025-merger extent, from OSM boundary/historic relations",
|
||||
"areaKm2": 36.2,
|
||||
"bbox": [
|
||||
108.15769,
|
||||
15.97164,
|
||||
108.23951,
|
||||
16.06188
|
||||
],
|
||||
"center": [
|
||||
16.01409,
|
||||
108.19606
|
||||
],
|
||||
"generatedAt": "2026-08-30T07:35:16.948Z",
|
||||
"osm": "relation/19403875"
|
||||
},
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [
|
||||
[
|
||||
[
|
||||
108.157686,
|
||||
15.997032
|
||||
],
|
||||
[
|
||||
108.1580695,
|
||||
15.996341
|
||||
],
|
||||
[
|
||||
108.1579167,
|
||||
15.9952504
|
||||
],
|
||||
[
|
||||
108.1582968,
|
||||
15.9941207
|
||||
],
|
||||
[
|
||||
108.1592406,
|
||||
15.9947598
|
||||
],
|
||||
[
|
||||
108.1605107,
|
||||
15.9949596
|
||||
],
|
||||
[
|
||||
108.1641061,
|
||||
15.993685
|
||||
],
|
||||
[
|
||||
108.1673795,
|
||||
15.9934249
|
||||
],
|
||||
[
|
||||
108.1700778,
|
||||
15.99343
|
||||
],
|
||||
[
|
||||
108.1749143,
|
||||
15.9941036
|
||||
],
|
||||
[
|
||||
108.1770019,
|
||||
15.9942111
|
||||
],
|
||||
[
|
||||
108.1816591,
|
||||
15.9936886
|
||||
],
|
||||
[
|
||||
108.1844883,
|
||||
15.993856
|
||||
],
|
||||
[
|
||||
108.1851779,
|
||||
15.9941246
|
||||
],
|
||||
[
|
||||
108.1861649,
|
||||
15.9948884
|
||||
],
|
||||
[
|
||||
108.1894151,
|
||||
15.9989573
|
||||
],
|
||||
[
|
||||
108.1901864,
|
||||
15.9993424
|
||||
],
|
||||
[
|
||||
108.193801,
|
||||
16.0002668
|
||||
],
|
||||
[
|
||||
108.1984482,
|
||||
16.0023316
|
||||
],
|
||||
[
|
||||
108.2054682,
|
||||
16.0072359
|
||||
],
|
||||
[
|
||||
108.2070262,
|
||||
16.0085579
|
||||
],
|
||||
[
|
||||
108.2078788,
|
||||
16.0066374
|
||||
],
|
||||
[
|
||||
108.2078785,
|
||||
16.0060099
|
||||
],
|
||||
[
|
||||
108.2070491,
|
||||
16.0023802
|
||||
],
|
||||
[
|
||||
108.2081388,
|
||||
15.9800951
|
||||
],
|
||||
[
|
||||
108.2096927,
|
||||
15.980085
|
||||
],
|
||||
[
|
||||
108.2100402,
|
||||
15.9749052
|
||||
],
|
||||
[
|
||||
108.2089829,
|
||||
15.9748665
|
||||
],
|
||||
[
|
||||
108.2090044,
|
||||
15.9732732
|
||||
],
|
||||
[
|
||||
108.2099358,
|
||||
15.9733496
|
||||
],
|
||||
[
|
||||
108.2099825,
|
||||
15.9727559
|
||||
],
|
||||
[
|
||||
108.2110936,
|
||||
15.9729603
|
||||
],
|
||||
[
|
||||
108.2121627,
|
||||
15.9728995
|
||||
],
|
||||
[
|
||||
108.2150075,
|
||||
15.9717811
|
||||
],
|
||||
[
|
||||
108.2168795,
|
||||
15.9716436
|
||||
],
|
||||
[
|
||||
108.2181906,
|
||||
15.974046
|
||||
],
|
||||
[
|
||||
108.2219169,
|
||||
15.9784211
|
||||
],
|
||||
[
|
||||
108.2227105,
|
||||
15.9799469
|
||||
],
|
||||
[
|
||||
108.2226171,
|
||||
15.9820113
|
||||
],
|
||||
[
|
||||
108.2222751,
|
||||
15.9830619
|
||||
],
|
||||
[
|
||||
108.2208089,
|
||||
15.9849022
|
||||
],
|
||||
[
|
||||
108.2204101,
|
||||
15.9858521
|
||||
],
|
||||
[
|
||||
108.2205944,
|
||||
15.9872783
|
||||
],
|
||||
[
|
||||
108.2211222,
|
||||
15.9877031
|
||||
],
|
||||
[
|
||||
108.2223653,
|
||||
15.9879356
|
||||
],
|
||||
[
|
||||
108.2280323,
|
||||
15.9907624
|
||||
],
|
||||
[
|
||||
108.2294794,
|
||||
15.9925574
|
||||
],
|
||||
[
|
||||
108.2298996,
|
||||
15.9949807
|
||||
],
|
||||
[
|
||||
108.2295728,
|
||||
15.9972693
|
||||
],
|
||||
[
|
||||
108.2289687,
|
||||
15.9994678
|
||||
],
|
||||
[
|
||||
108.2289249,
|
||||
16.0004042
|
||||
],
|
||||
[
|
||||
108.2290955,
|
||||
16.001092
|
||||
],
|
||||
[
|
||||
108.2298395,
|
||||
16.0023703
|
||||
],
|
||||
[
|
||||
108.2313133,
|
||||
16.0041081
|
||||
],
|
||||
[
|
||||
108.2321854,
|
||||
16.005402
|
||||
],
|
||||
[
|
||||
108.2327005,
|
||||
16.0065582
|
||||
],
|
||||
[
|
||||
108.2324671,
|
||||
16.0090261
|
||||
],
|
||||
[
|
||||
108.233214,
|
||||
16.0117633
|
||||
],
|
||||
[
|
||||
108.2347079,
|
||||
16.014321
|
||||
],
|
||||
[
|
||||
108.2386601,
|
||||
16.018507
|
||||
],
|
||||
[
|
||||
108.2395077,
|
||||
16.0202503
|
||||
],
|
||||
[
|
||||
108.239365,
|
||||
16.0222607
|
||||
],
|
||||
[
|
||||
108.2383731,
|
||||
16.0242939
|
||||
],
|
||||
[
|
||||
108.2350589,
|
||||
16.0291604
|
||||
],
|
||||
[
|
||||
108.2336336,
|
||||
16.0285578
|
||||
],
|
||||
[
|
||||
108.2312369,
|
||||
16.028431
|
||||
],
|
||||
[
|
||||
108.22874,
|
||||
16.0279405
|
||||
],
|
||||
[
|
||||
108.2258911,
|
||||
16.0267248
|
||||
],
|
||||
[
|
||||
108.2236416,
|
||||
16.024976
|
||||
],
|
||||
[
|
||||
108.221327,
|
||||
16.0224955
|
||||
],
|
||||
[
|
||||
108.2196085,
|
||||
16.0237065
|
||||
],
|
||||
[
|
||||
108.2184832,
|
||||
16.0240425
|
||||
],
|
||||
[
|
||||
108.2179135,
|
||||
16.0247344
|
||||
],
|
||||
[
|
||||
108.2179489,
|
||||
16.0258407
|
||||
],
|
||||
[
|
||||
108.2166463,
|
||||
16.0276306
|
||||
],
|
||||
[
|
||||
108.2163878,
|
||||
16.0288392
|
||||
],
|
||||
[
|
||||
108.2158874,
|
||||
16.0300241
|
||||
],
|
||||
[
|
||||
108.2148958,
|
||||
16.0309759
|
||||
],
|
||||
[
|
||||
108.2143019,
|
||||
16.0358676
|
||||
],
|
||||
[
|
||||
108.2079731,
|
||||
16.0373217
|
||||
],
|
||||
[
|
||||
108.2081076,
|
||||
16.0378631
|
||||
],
|
||||
[
|
||||
108.2077334,
|
||||
16.0379451
|
||||
],
|
||||
[
|
||||
108.2074269,
|
||||
16.0376175
|
||||
],
|
||||
[
|
||||
108.2080739,
|
||||
16.0355945
|
||||
],
|
||||
[
|
||||
108.2081664,
|
||||
16.0343512
|
||||
],
|
||||
[
|
||||
108.2074426,
|
||||
16.0337633
|
||||
],
|
||||
[
|
||||
108.2080406,
|
||||
16.0314206
|
||||
],
|
||||
[
|
||||
108.2083166,
|
||||
16.0251799
|
||||
],
|
||||
[
|
||||
108.2070528,
|
||||
16.0246784
|
||||
],
|
||||
[
|
||||
108.2061226,
|
||||
16.0240098
|
||||
],
|
||||
[
|
||||
108.2018239,
|
||||
16.0234058
|
||||
],
|
||||
[
|
||||
108.2017312,
|
||||
16.0236192
|
||||
],
|
||||
[
|
||||
108.1998995,
|
||||
16.0227154
|
||||
],
|
||||
[
|
||||
108.1974565,
|
||||
16.0226915
|
||||
],
|
||||
[
|
||||
108.1964252,
|
||||
16.0216768
|
||||
],
|
||||
[
|
||||
108.1958417,
|
||||
16.0213354
|
||||
],
|
||||
[
|
||||
108.1950738,
|
||||
16.0213129
|
||||
],
|
||||
[
|
||||
108.1940516,
|
||||
16.021873
|
||||
],
|
||||
[
|
||||
108.1929884,
|
||||
16.0230896
|
||||
],
|
||||
[
|
||||
108.1925758,
|
||||
16.0230246
|
||||
],
|
||||
[
|
||||
108.1914988,
|
||||
16.0244334
|
||||
],
|
||||
[
|
||||
108.1910655,
|
||||
16.0253073
|
||||
],
|
||||
[
|
||||
108.1787682,
|
||||
16.0618775
|
||||
],
|
||||
[
|
||||
108.1732991,
|
||||
16.0582383
|
||||
],
|
||||
[
|
||||
108.1736498,
|
||||
16.0570739
|
||||
],
|
||||
[
|
||||
108.1720318,
|
||||
16.0564984
|
||||
],
|
||||
[
|
||||
108.1720769,
|
||||
16.0562996
|
||||
],
|
||||
[
|
||||
108.1718172,
|
||||
16.0562381
|
||||
],
|
||||
[
|
||||
108.1718749,
|
||||
16.0559939
|
||||
],
|
||||
[
|
||||
108.1710769,
|
||||
16.0561034
|
||||
],
|
||||
[
|
||||
108.1714903,
|
||||
16.0542372
|
||||
],
|
||||
[
|
||||
108.1704426,
|
||||
16.0539978
|
||||
],
|
||||
[
|
||||
108.1709019,
|
||||
16.0523288
|
||||
],
|
||||
[
|
||||
108.1703987,
|
||||
16.05216
|
||||
],
|
||||
[
|
||||
108.1706464,
|
||||
16.0513039
|
||||
],
|
||||
[
|
||||
108.1716295,
|
||||
16.0515797
|
||||
],
|
||||
[
|
||||
108.1718859,
|
||||
16.0507629
|
||||
],
|
||||
[
|
||||
108.1709438,
|
||||
16.0504497
|
||||
],
|
||||
[
|
||||
108.1714823,
|
||||
16.0488519
|
||||
],
|
||||
[
|
||||
108.1712228,
|
||||
16.0488532
|
||||
],
|
||||
[
|
||||
108.1712189,
|
||||
16.0477655
|
||||
],
|
||||
[
|
||||
108.1715844,
|
||||
16.0470859
|
||||
],
|
||||
[
|
||||
108.1713708,
|
||||
16.0468826
|
||||
],
|
||||
[
|
||||
108.1713304,
|
||||
16.0464785
|
||||
],
|
||||
[
|
||||
108.1716335,
|
||||
16.0461099
|
||||
],
|
||||
[
|
||||
108.1707909,
|
||||
16.0449551
|
||||
],
|
||||
[
|
||||
108.1709991,
|
||||
16.0447598
|
||||
],
|
||||
[
|
||||
108.1687379,
|
||||
16.0424687
|
||||
],
|
||||
[
|
||||
108.1688805,
|
||||
16.0423383
|
||||
],
|
||||
[
|
||||
108.1675512,
|
||||
16.0411658
|
||||
],
|
||||
[
|
||||
108.1664488,
|
||||
16.0405265
|
||||
],
|
||||
[
|
||||
108.1656978,
|
||||
16.0390932
|
||||
],
|
||||
[
|
||||
108.1629646,
|
||||
16.036683
|
||||
],
|
||||
[
|
||||
108.162353,
|
||||
16.0356725
|
||||
],
|
||||
[
|
||||
108.1622001,
|
||||
16.0349223
|
||||
],
|
||||
[
|
||||
108.1629063,
|
||||
16.0342209
|
||||
],
|
||||
[
|
||||
108.1633159,
|
||||
16.0334272
|
||||
],
|
||||
[
|
||||
108.1631319,
|
||||
16.0315283
|
||||
],
|
||||
[
|
||||
108.164008,
|
||||
16.0305657
|
||||
],
|
||||
[
|
||||
108.1645486,
|
||||
16.0288264
|
||||
],
|
||||
[
|
||||
108.1647027,
|
||||
16.0277351
|
||||
],
|
||||
[
|
||||
108.1635137,
|
||||
16.0268593
|
||||
],
|
||||
[
|
||||
108.1616503,
|
||||
16.0263379
|
||||
],
|
||||
[
|
||||
108.1601027,
|
||||
16.0240125
|
||||
],
|
||||
[
|
||||
108.1597358,
|
||||
16.0238978
|
||||
],
|
||||
[
|
||||
108.1596788,
|
||||
16.0236361
|
||||
],
|
||||
[
|
||||
108.1601628,
|
||||
16.0234374
|
||||
],
|
||||
[
|
||||
108.1624212,
|
||||
16.0238982
|
||||
],
|
||||
[
|
||||
108.163199,
|
||||
16.0236439
|
||||
],
|
||||
[
|
||||
108.165143,
|
||||
16.0219913
|
||||
],
|
||||
[
|
||||
108.165515,
|
||||
16.020364
|
||||
],
|
||||
[
|
||||
108.1658746,
|
||||
16.019757
|
||||
],
|
||||
[
|
||||
108.1678113,
|
||||
16.0180728
|
||||
],
|
||||
[
|
||||
108.1691095,
|
||||
16.0172065
|
||||
],
|
||||
[
|
||||
108.1706766,
|
||||
16.015775
|
||||
],
|
||||
[
|
||||
108.1683059,
|
||||
16.0139794
|
||||
],
|
||||
[
|
||||
108.1666059,
|
||||
16.0119336
|
||||
],
|
||||
[
|
||||
108.1605626,
|
||||
16.0093697
|
||||
],
|
||||
[
|
||||
108.1592242,
|
||||
16.0090454
|
||||
],
|
||||
[
|
||||
108.1587777,
|
||||
16.0033102
|
||||
],
|
||||
[
|
||||
108.1583485,
|
||||
16.0009098
|
||||
],
|
||||
[
|
||||
108.1577182,
|
||||
15.9989451
|
||||
],
|
||||
[
|
||||
108.157686,
|
||||
15.997032
|
||||
]
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,596 @@
|
||||
{
|
||||
"type": "Feature",
|
||||
"properties": {
|
||||
"code": "DN-HAICHAU",
|
||||
"name": "Hai Chau",
|
||||
"level": "district",
|
||||
"parent": "DN",
|
||||
"basis": "pre-2025-merger extent, from OSM boundary/historic relations",
|
||||
"areaKm2": 22.9,
|
||||
"bbox": [
|
||||
108.18883,
|
||||
16.02131,
|
||||
108.236,
|
||||
16.10365
|
||||
],
|
||||
"center": [
|
||||
16.05669,
|
||||
108.21143
|
||||
],
|
||||
"generatedAt": "2026-08-30T07:35:08.257Z",
|
||||
"osm": "relation/19403876"
|
||||
},
|
||||
"geometry": {
|
||||
"type": "MultiPolygon",
|
||||
"coordinates": [
|
||||
[
|
||||
[
|
||||
[
|
||||
108.1888335,
|
||||
16.0527778
|
||||
],
|
||||
[
|
||||
108.1960155,
|
||||
16.0364883
|
||||
],
|
||||
[
|
||||
108.1893537,
|
||||
16.0327948
|
||||
],
|
||||
[
|
||||
108.1913719,
|
||||
16.0265879
|
||||
],
|
||||
[
|
||||
108.1907008,
|
||||
16.0263747
|
||||
],
|
||||
[
|
||||
108.1912779,
|
||||
16.0248057
|
||||
],
|
||||
[
|
||||
108.1925758,
|
||||
16.0230246
|
||||
],
|
||||
[
|
||||
108.1929884,
|
||||
16.0230896
|
||||
],
|
||||
[
|
||||
108.1940516,
|
||||
16.021873
|
||||
],
|
||||
[
|
||||
108.1950738,
|
||||
16.0213129
|
||||
],
|
||||
[
|
||||
108.1958417,
|
||||
16.0213354
|
||||
],
|
||||
[
|
||||
108.1964252,
|
||||
16.0216768
|
||||
],
|
||||
[
|
||||
108.1974565,
|
||||
16.0226915
|
||||
],
|
||||
[
|
||||
108.1998995,
|
||||
16.0227154
|
||||
],
|
||||
[
|
||||
108.2017312,
|
||||
16.0236192
|
||||
],
|
||||
[
|
||||
108.2018239,
|
||||
16.0234058
|
||||
],
|
||||
[
|
||||
108.2061226,
|
||||
16.0240098
|
||||
],
|
||||
[
|
||||
108.2070528,
|
||||
16.0246784
|
||||
],
|
||||
[
|
||||
108.2083166,
|
||||
16.0251799
|
||||
],
|
||||
[
|
||||
108.2080406,
|
||||
16.0314206
|
||||
],
|
||||
[
|
||||
108.2074426,
|
||||
16.0337633
|
||||
],
|
||||
[
|
||||
108.2081664,
|
||||
16.0343512
|
||||
],
|
||||
[
|
||||
108.2080739,
|
||||
16.0355945
|
||||
],
|
||||
[
|
||||
108.2074269,
|
||||
16.0376175
|
||||
],
|
||||
[
|
||||
108.2077334,
|
||||
16.0379451
|
||||
],
|
||||
[
|
||||
108.2081076,
|
||||
16.0378631
|
||||
],
|
||||
[
|
||||
108.2079731,
|
||||
16.0373217
|
||||
],
|
||||
[
|
||||
108.2143019,
|
||||
16.0358676
|
||||
],
|
||||
[
|
||||
108.2148958,
|
||||
16.0309759
|
||||
],
|
||||
[
|
||||
108.2158874,
|
||||
16.0300241
|
||||
],
|
||||
[
|
||||
108.2163878,
|
||||
16.0288392
|
||||
],
|
||||
[
|
||||
108.2166463,
|
||||
16.0276306
|
||||
],
|
||||
[
|
||||
108.2179489,
|
||||
16.0258407
|
||||
],
|
||||
[
|
||||
108.2179135,
|
||||
16.0247344
|
||||
],
|
||||
[
|
||||
108.2184832,
|
||||
16.0240425
|
||||
],
|
||||
[
|
||||
108.2196085,
|
||||
16.0237065
|
||||
],
|
||||
[
|
||||
108.221327,
|
||||
16.0224955
|
||||
],
|
||||
[
|
||||
108.2236416,
|
||||
16.024976
|
||||
],
|
||||
[
|
||||
108.2258911,
|
||||
16.0267248
|
||||
],
|
||||
[
|
||||
108.22874,
|
||||
16.0279405
|
||||
],
|
||||
[
|
||||
108.2312369,
|
||||
16.028431
|
||||
],
|
||||
[
|
||||
108.2336336,
|
||||
16.0285578
|
||||
],
|
||||
[
|
||||
108.2350589,
|
||||
16.0291604
|
||||
],
|
||||
[
|
||||
108.2353237,
|
||||
16.0313059
|
||||
],
|
||||
[
|
||||
108.2357658,
|
||||
16.032605
|
||||
],
|
||||
[
|
||||
108.2359971,
|
||||
16.0340552
|
||||
],
|
||||
[
|
||||
108.2359977,
|
||||
16.0354354
|
||||
],
|
||||
[
|
||||
108.2345755,
|
||||
16.0422698
|
||||
],
|
||||
[
|
||||
108.2339033,
|
||||
16.0442982
|
||||
],
|
||||
[
|
||||
108.2323274,
|
||||
16.0473298
|
||||
],
|
||||
[
|
||||
108.2288383,
|
||||
16.0510559
|
||||
],
|
||||
[
|
||||
108.227316,
|
||||
16.0539997
|
||||
],
|
||||
[
|
||||
108.2270163,
|
||||
16.0573659
|
||||
],
|
||||
[
|
||||
108.2264075,
|
||||
16.0792643
|
||||
],
|
||||
[
|
||||
108.226028,
|
||||
16.0821677
|
||||
],
|
||||
[
|
||||
108.2252379,
|
||||
16.0859249
|
||||
],
|
||||
[
|
||||
108.2219899,
|
||||
16.0950595
|
||||
],
|
||||
[
|
||||
108.2206923,
|
||||
16.1036543
|
||||
],
|
||||
[
|
||||
108.2149991,
|
||||
16.0961727
|
||||
],
|
||||
[
|
||||
108.2126815,
|
||||
16.0974633
|
||||
],
|
||||
[
|
||||
108.2100685,
|
||||
16.0982392
|
||||
],
|
||||
[
|
||||
108.2071278,
|
||||
16.0984265
|
||||
],
|
||||
[
|
||||
108.2056103,
|
||||
16.0982773
|
||||
],
|
||||
[
|
||||
108.2041422,
|
||||
16.097898
|
||||
],
|
||||
[
|
||||
108.2042217,
|
||||
16.0977344
|
||||
],
|
||||
[
|
||||
108.2071317,
|
||||
16.098261
|
||||
],
|
||||
[
|
||||
108.2100283,
|
||||
16.0980412
|
||||
],
|
||||
[
|
||||
108.2125818,
|
||||
16.0972604
|
||||
],
|
||||
[
|
||||
108.2147603,
|
||||
16.0960807
|
||||
],
|
||||
[
|
||||
108.2140352,
|
||||
16.0945289
|
||||
],
|
||||
[
|
||||
108.2136667,
|
||||
16.0929725
|
||||
],
|
||||
[
|
||||
108.2133955,
|
||||
16.0925997
|
||||
],
|
||||
[
|
||||
108.2134178,
|
||||
16.0920781
|
||||
],
|
||||
[
|
||||
108.2131231,
|
||||
16.0915669
|
||||
],
|
||||
[
|
||||
108.2106184,
|
||||
16.089622
|
||||
],
|
||||
[
|
||||
108.2100524,
|
||||
16.0894136
|
||||
],
|
||||
[
|
||||
108.2072511,
|
||||
16.0889945
|
||||
],
|
||||
[
|
||||
108.2060396,
|
||||
16.0913602
|
||||
],
|
||||
[
|
||||
108.2047276,
|
||||
16.092864
|
||||
],
|
||||
[
|
||||
108.2030279,
|
||||
16.0938684
|
||||
],
|
||||
[
|
||||
108.2011491,
|
||||
16.0945157
|
||||
],
|
||||
[
|
||||
108.2010767,
|
||||
16.0944295
|
||||
],
|
||||
[
|
||||
108.2038013,
|
||||
16.0932455
|
||||
],
|
||||
[
|
||||
108.2052976,
|
||||
16.0920062
|
||||
],
|
||||
[
|
||||
108.2063845,
|
||||
16.0904322
|
||||
],
|
||||
[
|
||||
108.2070583,
|
||||
16.0888926
|
||||
],
|
||||
[
|
||||
108.2071238,
|
||||
16.0881534
|
||||
],
|
||||
[
|
||||
108.2072759,
|
||||
16.0881022
|
||||
],
|
||||
[
|
||||
108.2075553,
|
||||
16.0863245
|
||||
],
|
||||
[
|
||||
108.2071477,
|
||||
16.083312
|
||||
],
|
||||
[
|
||||
108.2063863,
|
||||
16.0811539
|
||||
],
|
||||
[
|
||||
108.2052142,
|
||||
16.0789681
|
||||
],
|
||||
[
|
||||
108.2081934,
|
||||
16.0761189
|
||||
],
|
||||
[
|
||||
108.2083277,
|
||||
16.0762674
|
||||
],
|
||||
[
|
||||
108.2093239,
|
||||
16.0754709
|
||||
],
|
||||
[
|
||||
108.2096262,
|
||||
16.0757492
|
||||
],
|
||||
[
|
||||
108.2099019,
|
||||
16.0755914
|
||||
],
|
||||
[
|
||||
108.2099497,
|
||||
16.0754293
|
||||
],
|
||||
[
|
||||
108.2096092,
|
||||
16.0749506
|
||||
],
|
||||
[
|
||||
108.2099004,
|
||||
16.0742854
|
||||
],
|
||||
[
|
||||
108.2122563,
|
||||
16.0761907
|
||||
],
|
||||
[
|
||||
108.2145593,
|
||||
16.0654636
|
||||
],
|
||||
[
|
||||
108.2129488,
|
||||
16.0568207
|
||||
],
|
||||
[
|
||||
108.2126515,
|
||||
16.0558177
|
||||
],
|
||||
[
|
||||
108.2120927,
|
||||
16.0548307
|
||||
],
|
||||
[
|
||||
108.2105123,
|
||||
16.0524394
|
||||
],
|
||||
[
|
||||
108.2100344,
|
||||
16.0528899
|
||||
],
|
||||
[
|
||||
108.2094872,
|
||||
16.0558198
|
||||
],
|
||||
[
|
||||
108.2086263,
|
||||
16.0564142
|
||||
],
|
||||
[
|
||||
108.2049575,
|
||||
16.0559447
|
||||
],
|
||||
[
|
||||
108.2049058,
|
||||
16.0564129
|
||||
],
|
||||
[
|
||||
108.2064034,
|
||||
16.0567075
|
||||
],
|
||||
[
|
||||
108.2046336,
|
||||
16.0604744
|
||||
],
|
||||
[
|
||||
108.2039387,
|
||||
16.0613375
|
||||
],
|
||||
[
|
||||
108.2021511,
|
||||
16.0646609
|
||||
],
|
||||
[
|
||||
108.2017609,
|
||||
16.0647558
|
||||
],
|
||||
[
|
||||
108.1930239,
|
||||
16.0643262
|
||||
],
|
||||
[
|
||||
108.1931372,
|
||||
16.0625717
|
||||
],
|
||||
[
|
||||
108.1928731,
|
||||
16.0615213
|
||||
],
|
||||
[
|
||||
108.1913247,
|
||||
16.0597045
|
||||
],
|
||||
[
|
||||
108.1904832,
|
||||
16.0589461
|
||||
],
|
||||
[
|
||||
108.1910606,
|
||||
16.0572813
|
||||
],
|
||||
[
|
||||
108.189535,
|
||||
16.0562297
|
||||
],
|
||||
[
|
||||
108.1906834,
|
||||
16.0535333
|
||||
],
|
||||
[
|
||||
108.1888335,
|
||||
16.0527778
|
||||
]
|
||||
]
|
||||
],
|
||||
[
|
||||
[
|
||||
[
|
||||
108.2063163,
|
||||
16.094756
|
||||
],
|
||||
[
|
||||
108.2066246,
|
||||
16.0941885
|
||||
],
|
||||
[
|
||||
108.2070675,
|
||||
16.0940929
|
||||
],
|
||||
[
|
||||
108.2068969,
|
||||
16.0951124
|
||||
],
|
||||
[
|
||||
108.2071855,
|
||||
16.0961768
|
||||
],
|
||||
[
|
||||
108.2079034,
|
||||
16.0965505
|
||||
],
|
||||
[
|
||||
108.2095642,
|
||||
16.0965754
|
||||
],
|
||||
[
|
||||
108.2097887,
|
||||
16.0968331
|
||||
],
|
||||
[
|
||||
108.2094464,
|
||||
16.0971447
|
||||
],
|
||||
[
|
||||
108.2082159,
|
||||
16.0973774
|
||||
],
|
||||
[
|
||||
108.2075942,
|
||||
16.0973076
|
||||
],
|
||||
[
|
||||
108.2073778,
|
||||
16.0977163
|
||||
],
|
||||
[
|
||||
108.2071676,
|
||||
16.0977423
|
||||
],
|
||||
[
|
||||
108.2066917,
|
||||
16.0966713
|
||||
],
|
||||
[
|
||||
108.2063163,
|
||||
16.094756
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,650 @@
|
||||
{
|
||||
"type": "Feature",
|
||||
"properties": {
|
||||
"code": "DN-NGUHANHSON",
|
||||
"name": "Ngu Hanh Son",
|
||||
"level": "district",
|
||||
"parent": "DN",
|
||||
"basis": "pre-2025-merger extent, from OSM boundary/historic relations",
|
||||
"areaKm2": 40.2,
|
||||
"bbox": [
|
||||
108.21683,
|
||||
15.95597,
|
||||
108.28957,
|
||||
16.05675
|
||||
],
|
||||
"center": [
|
||||
15.99755,
|
||||
108.25003
|
||||
],
|
||||
"generatedAt": "2026-08-30T07:35:13.283Z",
|
||||
"osm": "relation/19403879"
|
||||
},
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [
|
||||
[
|
||||
[
|
||||
108.2168322,
|
||||
15.9710357
|
||||
],
|
||||
[
|
||||
108.2169341,
|
||||
15.9700251
|
||||
],
|
||||
[
|
||||
108.2172839,
|
||||
15.9689689
|
||||
],
|
||||
[
|
||||
108.2190267,
|
||||
15.9653125
|
||||
],
|
||||
[
|
||||
108.2197139,
|
||||
15.9641968
|
||||
],
|
||||
[
|
||||
108.220803,
|
||||
15.9632557
|
||||
],
|
||||
[
|
||||
108.2234852,
|
||||
15.9621025
|
||||
],
|
||||
[
|
||||
108.2244541,
|
||||
15.9610835
|
||||
],
|
||||
[
|
||||
108.2247949,
|
||||
15.9602976
|
||||
],
|
||||
[
|
||||
108.2249384,
|
||||
15.959041
|
||||
],
|
||||
[
|
||||
108.2248356,
|
||||
15.9577657
|
||||
],
|
||||
[
|
||||
108.2244456,
|
||||
15.9561285
|
||||
],
|
||||
[
|
||||
108.22564,
|
||||
15.9559727
|
||||
],
|
||||
[
|
||||
108.2286776,
|
||||
15.9565365
|
||||
],
|
||||
[
|
||||
108.2306419,
|
||||
15.9573073
|
||||
],
|
||||
[
|
||||
108.2303953,
|
||||
15.9577239
|
||||
],
|
||||
[
|
||||
108.230972,
|
||||
15.9580127
|
||||
],
|
||||
[
|
||||
108.2310169,
|
||||
15.958551
|
||||
],
|
||||
[
|
||||
108.230012,
|
||||
15.9609043
|
||||
],
|
||||
[
|
||||
108.2313331,
|
||||
15.961986
|
||||
],
|
||||
[
|
||||
108.2312898,
|
||||
15.962398
|
||||
],
|
||||
[
|
||||
108.2347244,
|
||||
15.9634978
|
||||
],
|
||||
[
|
||||
108.2375417,
|
||||
15.9647029
|
||||
],
|
||||
[
|
||||
108.2388765,
|
||||
15.9649407
|
||||
],
|
||||
[
|
||||
108.2388228,
|
||||
15.9651212
|
||||
],
|
||||
[
|
||||
108.2393727,
|
||||
15.9653326
|
||||
],
|
||||
[
|
||||
108.2394907,
|
||||
15.9665176
|
||||
],
|
||||
[
|
||||
108.2408452,
|
||||
15.9675491
|
||||
],
|
||||
[
|
||||
108.2421555,
|
||||
15.968128
|
||||
],
|
||||
[
|
||||
108.2434625,
|
||||
15.9698496
|
||||
],
|
||||
[
|
||||
108.2440883,
|
||||
15.9704156
|
||||
],
|
||||
[
|
||||
108.2457992,
|
||||
15.9710123
|
||||
],
|
||||
[
|
||||
108.245936,
|
||||
15.9707183
|
||||
],
|
||||
[
|
||||
108.2463598,
|
||||
15.9707467
|
||||
],
|
||||
[
|
||||
108.2462427,
|
||||
15.9719639
|
||||
],
|
||||
[
|
||||
108.246489,
|
||||
15.9718595
|
||||
],
|
||||
[
|
||||
108.2465213,
|
||||
15.9719763
|
||||
],
|
||||
[
|
||||
108.2468923,
|
||||
15.9719872
|
||||
],
|
||||
[
|
||||
108.2469901,
|
||||
15.971675
|
||||
],
|
||||
[
|
||||
108.2472421,
|
||||
15.9715123
|
||||
],
|
||||
[
|
||||
108.2476875,
|
||||
15.9717988
|
||||
],
|
||||
[
|
||||
108.247835,
|
||||
15.971479
|
||||
],
|
||||
[
|
||||
108.2480577,
|
||||
15.9714764
|
||||
],
|
||||
[
|
||||
108.2501766,
|
||||
15.9720038
|
||||
],
|
||||
[
|
||||
108.2501773,
|
||||
15.9724131
|
||||
],
|
||||
[
|
||||
108.2517833,
|
||||
15.9726858
|
||||
],
|
||||
[
|
||||
108.2518369,
|
||||
15.972486
|
||||
],
|
||||
[
|
||||
108.2520233,
|
||||
15.972519
|
||||
],
|
||||
[
|
||||
108.2532236,
|
||||
15.9727935
|
||||
],
|
||||
[
|
||||
108.253229,
|
||||
15.9729475
|
||||
],
|
||||
[
|
||||
108.2540122,
|
||||
15.9729437
|
||||
],
|
||||
[
|
||||
108.2540256,
|
||||
15.9733318
|
||||
],
|
||||
[
|
||||
108.2547425,
|
||||
15.973324
|
||||
],
|
||||
[
|
||||
108.2547283,
|
||||
15.9725878
|
||||
],
|
||||
[
|
||||
108.2581455,
|
||||
15.971695
|
||||
],
|
||||
[
|
||||
108.2581481,
|
||||
15.9702314
|
||||
],
|
||||
[
|
||||
108.2593419,
|
||||
15.9702009
|
||||
],
|
||||
[
|
||||
108.2593314,
|
||||
15.9668138
|
||||
],
|
||||
[
|
||||
108.2598106,
|
||||
15.9660367
|
||||
],
|
||||
[
|
||||
108.262231,
|
||||
15.9650585
|
||||
],
|
||||
[
|
||||
108.263148,
|
||||
15.9650262
|
||||
],
|
||||
[
|
||||
108.2649153,
|
||||
15.9653597
|
||||
],
|
||||
[
|
||||
108.2673716,
|
||||
15.9663394
|
||||
],
|
||||
[
|
||||
108.2682118,
|
||||
15.966484
|
||||
],
|
||||
[
|
||||
108.2682144,
|
||||
15.9652809
|
||||
],
|
||||
[
|
||||
108.2680721,
|
||||
15.9648997
|
||||
],
|
||||
[
|
||||
108.2682031,
|
||||
15.9641491
|
||||
],
|
||||
[
|
||||
108.2687229,
|
||||
15.9633105
|
||||
],
|
||||
[
|
||||
108.2696286,
|
||||
15.9624737
|
||||
],
|
||||
[
|
||||
108.2722628,
|
||||
15.961901
|
||||
],
|
||||
[
|
||||
108.2724356,
|
||||
15.9621467
|
||||
],
|
||||
[
|
||||
108.272684,
|
||||
15.9620752
|
||||
],
|
||||
[
|
||||
108.2730415,
|
||||
15.9622717
|
||||
],
|
||||
[
|
||||
108.2778285,
|
||||
15.962001
|
||||
],
|
||||
[
|
||||
108.2787427,
|
||||
15.961512
|
||||
],
|
||||
[
|
||||
108.285911,
|
||||
15.9655679
|
||||
],
|
||||
[
|
||||
108.2895709,
|
||||
15.9674041
|
||||
],
|
||||
[
|
||||
108.284633,
|
||||
15.9756529
|
||||
],
|
||||
[
|
||||
108.2837425,
|
||||
15.9774528
|
||||
],
|
||||
[
|
||||
108.2830981,
|
||||
15.9783472
|
||||
],
|
||||
[
|
||||
108.2822756,
|
||||
15.9801388
|
||||
],
|
||||
[
|
||||
108.2811905,
|
||||
15.9816695
|
||||
],
|
||||
[
|
||||
108.280914,
|
||||
15.9824381
|
||||
],
|
||||
[
|
||||
108.2796893,
|
||||
15.9844818
|
||||
],
|
||||
[
|
||||
108.2784568,
|
||||
15.9870403
|
||||
],
|
||||
[
|
||||
108.2784535,
|
||||
15.9874035
|
||||
],
|
||||
[
|
||||
108.2766861,
|
||||
15.9901066
|
||||
],
|
||||
[
|
||||
108.2759961,
|
||||
15.9918999
|
||||
],
|
||||
[
|
||||
108.2735317,
|
||||
15.9970167
|
||||
],
|
||||
[
|
||||
108.2732622,
|
||||
15.9972705
|
||||
],
|
||||
[
|
||||
108.2722646,
|
||||
15.9995232
|
||||
],
|
||||
[
|
||||
108.269954,
|
||||
16.0038935
|
||||
],
|
||||
[
|
||||
108.2673952,
|
||||
16.0093355
|
||||
],
|
||||
[
|
||||
108.2653536,
|
||||
16.0141973
|
||||
],
|
||||
[
|
||||
108.2636942,
|
||||
16.0173469
|
||||
],
|
||||
[
|
||||
108.2627286,
|
||||
16.0197721
|
||||
],
|
||||
[
|
||||
108.2617413,
|
||||
16.0213589
|
||||
],
|
||||
[
|
||||
108.2607808,
|
||||
16.0233408
|
||||
],
|
||||
[
|
||||
108.2585768,
|
||||
16.0286559
|
||||
],
|
||||
[
|
||||
108.2581762,
|
||||
16.0291558
|
||||
],
|
||||
[
|
||||
108.2570964,
|
||||
16.0312988
|
||||
],
|
||||
[
|
||||
108.2568422,
|
||||
16.0321346
|
||||
],
|
||||
[
|
||||
108.2558082,
|
||||
16.0340215
|
||||
],
|
||||
[
|
||||
108.2555449,
|
||||
16.0351209
|
||||
],
|
||||
[
|
||||
108.2543508,
|
||||
16.0374413
|
||||
],
|
||||
[
|
||||
108.2538119,
|
||||
16.0390588
|
||||
],
|
||||
[
|
||||
108.2533321,
|
||||
16.0399823
|
||||
],
|
||||
[
|
||||
108.2529116,
|
||||
16.0416128
|
||||
],
|
||||
[
|
||||
108.2496942,
|
||||
16.0504905
|
||||
],
|
||||
[
|
||||
108.2479383,
|
||||
16.0567485
|
||||
],
|
||||
[
|
||||
108.2367957,
|
||||
16.0534755
|
||||
],
|
||||
[
|
||||
108.2356757,
|
||||
16.051786
|
||||
],
|
||||
[
|
||||
108.2345524,
|
||||
16.0511662
|
||||
],
|
||||
[
|
||||
108.2332849,
|
||||
16.0509853
|
||||
],
|
||||
[
|
||||
108.2318581,
|
||||
16.0514667
|
||||
],
|
||||
[
|
||||
108.2288383,
|
||||
16.0510559
|
||||
],
|
||||
[
|
||||
108.2323274,
|
||||
16.0473298
|
||||
],
|
||||
[
|
||||
108.2339033,
|
||||
16.0442982
|
||||
],
|
||||
[
|
||||
108.2345755,
|
||||
16.0422698
|
||||
],
|
||||
[
|
||||
108.2359977,
|
||||
16.0354354
|
||||
],
|
||||
[
|
||||
108.2359971,
|
||||
16.0340552
|
||||
],
|
||||
[
|
||||
108.2357658,
|
||||
16.032605
|
||||
],
|
||||
[
|
||||
108.2353237,
|
||||
16.0313059
|
||||
],
|
||||
[
|
||||
108.2350589,
|
||||
16.0291604
|
||||
],
|
||||
[
|
||||
108.2383731,
|
||||
16.0242939
|
||||
],
|
||||
[
|
||||
108.239365,
|
||||
16.0222607
|
||||
],
|
||||
[
|
||||
108.2395077,
|
||||
16.0202503
|
||||
],
|
||||
[
|
||||
108.2386601,
|
||||
16.018507
|
||||
],
|
||||
[
|
||||
108.2347079,
|
||||
16.014321
|
||||
],
|
||||
[
|
||||
108.233214,
|
||||
16.0117633
|
||||
],
|
||||
[
|
||||
108.2324671,
|
||||
16.0090261
|
||||
],
|
||||
[
|
||||
108.2327005,
|
||||
16.0065582
|
||||
],
|
||||
[
|
||||
108.2321854,
|
||||
16.005402
|
||||
],
|
||||
[
|
||||
108.2313133,
|
||||
16.0041081
|
||||
],
|
||||
[
|
||||
108.2298395,
|
||||
16.0023703
|
||||
],
|
||||
[
|
||||
108.2290955,
|
||||
16.001092
|
||||
],
|
||||
[
|
||||
108.2289249,
|
||||
16.0004042
|
||||
],
|
||||
[
|
||||
108.2289687,
|
||||
15.9994678
|
||||
],
|
||||
[
|
||||
108.2295728,
|
||||
15.9972693
|
||||
],
|
||||
[
|
||||
108.2298996,
|
||||
15.9949807
|
||||
],
|
||||
[
|
||||
108.2294794,
|
||||
15.9925574
|
||||
],
|
||||
[
|
||||
108.2280323,
|
||||
15.9907624
|
||||
],
|
||||
[
|
||||
108.2223653,
|
||||
15.9879356
|
||||
],
|
||||
[
|
||||
108.2211222,
|
||||
15.9877031
|
||||
],
|
||||
[
|
||||
108.2205944,
|
||||
15.9872783
|
||||
],
|
||||
[
|
||||
108.2204101,
|
||||
15.9858521
|
||||
],
|
||||
[
|
||||
108.2208089,
|
||||
15.9849022
|
||||
],
|
||||
[
|
||||
108.2222751,
|
||||
15.9830619
|
||||
],
|
||||
[
|
||||
108.2226171,
|
||||
15.9820113
|
||||
],
|
||||
[
|
||||
108.2227105,
|
||||
15.9799469
|
||||
],
|
||||
[
|
||||
108.2219169,
|
||||
15.9784211
|
||||
],
|
||||
[
|
||||
108.2188304,
|
||||
15.9748345
|
||||
],
|
||||
[
|
||||
108.2176304,
|
||||
15.9732102
|
||||
],
|
||||
[
|
||||
108.2168795,
|
||||
15.9716436
|
||||
],
|
||||
[
|
||||
108.2168322,
|
||||
15.9710357
|
||||
]
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,318 @@
|
||||
{
|
||||
"type": "Feature",
|
||||
"properties": {
|
||||
"code": "DN-THANHKHE",
|
||||
"name": "Thanh Khe",
|
||||
"level": "district",
|
||||
"parent": "DN",
|
||||
"basis": "pre-2025-merger extent, from OSM boundary/historic relations",
|
||||
"areaKm2": 10.6,
|
||||
"bbox": [
|
||||
108.16378,
|
||||
16.02637,
|
||||
108.21456,
|
||||
16.08084
|
||||
],
|
||||
"center": [
|
||||
16.06315,
|
||||
108.19039
|
||||
],
|
||||
"generatedAt": "2026-08-30T07:35:09.742Z",
|
||||
"osm": "relation/19403878"
|
||||
},
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [
|
||||
[
|
||||
[
|
||||
108.1637782,
|
||||
16.0767359
|
||||
],
|
||||
[
|
||||
108.167424,
|
||||
16.0744279
|
||||
],
|
||||
[
|
||||
108.1667039,
|
||||
16.0734
|
||||
],
|
||||
[
|
||||
108.17057,
|
||||
16.0708563
|
||||
],
|
||||
[
|
||||
108.1688963,
|
||||
16.0684767
|
||||
],
|
||||
[
|
||||
108.1704734,
|
||||
16.067447
|
||||
],
|
||||
[
|
||||
108.1708851,
|
||||
16.0640139
|
||||
],
|
||||
[
|
||||
108.1715476,
|
||||
16.060841
|
||||
],
|
||||
[
|
||||
108.1726561,
|
||||
16.0610581
|
||||
],
|
||||
[
|
||||
108.1746777,
|
||||
16.0623097
|
||||
],
|
||||
[
|
||||
108.1740384,
|
||||
16.0629824
|
||||
],
|
||||
[
|
||||
108.1750855,
|
||||
16.0644501
|
||||
],
|
||||
[
|
||||
108.1787682,
|
||||
16.0618775
|
||||
],
|
||||
[
|
||||
108.1907008,
|
||||
16.0263747
|
||||
],
|
||||
[
|
||||
108.1913719,
|
||||
16.0265879
|
||||
],
|
||||
[
|
||||
108.1893537,
|
||||
16.0327948
|
||||
],
|
||||
[
|
||||
108.1960155,
|
||||
16.0364883
|
||||
],
|
||||
[
|
||||
108.1888335,
|
||||
16.0527778
|
||||
],
|
||||
[
|
||||
108.1906834,
|
||||
16.0535333
|
||||
],
|
||||
[
|
||||
108.189535,
|
||||
16.0562297
|
||||
],
|
||||
[
|
||||
108.1910606,
|
||||
16.0572813
|
||||
],
|
||||
[
|
||||
108.1904832,
|
||||
16.0589461
|
||||
],
|
||||
[
|
||||
108.1913247,
|
||||
16.0597045
|
||||
],
|
||||
[
|
||||
108.1928731,
|
||||
16.0615213
|
||||
],
|
||||
[
|
||||
108.1931372,
|
||||
16.0625717
|
||||
],
|
||||
[
|
||||
108.1930239,
|
||||
16.0643262
|
||||
],
|
||||
[
|
||||
108.2020235,
|
||||
16.064716
|
||||
],
|
||||
[
|
||||
108.2023521,
|
||||
16.0644358
|
||||
],
|
||||
[
|
||||
108.2039387,
|
||||
16.0613375
|
||||
],
|
||||
[
|
||||
108.2046336,
|
||||
16.0604744
|
||||
],
|
||||
[
|
||||
108.2064034,
|
||||
16.0567075
|
||||
],
|
||||
[
|
||||
108.2049058,
|
||||
16.0564129
|
||||
],
|
||||
[
|
||||
108.2049575,
|
||||
16.0559447
|
||||
],
|
||||
[
|
||||
108.2086263,
|
||||
16.0564142
|
||||
],
|
||||
[
|
||||
108.2094872,
|
||||
16.0558198
|
||||
],
|
||||
[
|
||||
108.2100344,
|
||||
16.0528899
|
||||
],
|
||||
[
|
||||
108.2105123,
|
||||
16.0524394
|
||||
],
|
||||
[
|
||||
108.2127638,
|
||||
16.0561083
|
||||
],
|
||||
[
|
||||
108.2145593,
|
||||
16.0654636
|
||||
],
|
||||
[
|
||||
108.2122563,
|
||||
16.0761907
|
||||
],
|
||||
[
|
||||
108.2099004,
|
||||
16.0742854
|
||||
],
|
||||
[
|
||||
108.2096092,
|
||||
16.0749506
|
||||
],
|
||||
[
|
||||
108.2099497,
|
||||
16.0754293
|
||||
],
|
||||
[
|
||||
108.2099019,
|
||||
16.0755914
|
||||
],
|
||||
[
|
||||
108.2096262,
|
||||
16.0757492
|
||||
],
|
||||
[
|
||||
108.2093239,
|
||||
16.0754709
|
||||
],
|
||||
[
|
||||
108.2083277,
|
||||
16.0762674
|
||||
],
|
||||
[
|
||||
108.2081934,
|
||||
16.0761189
|
||||
],
|
||||
[
|
||||
108.2052142,
|
||||
16.0789681
|
||||
],
|
||||
[
|
||||
108.2036241,
|
||||
16.0766356
|
||||
],
|
||||
[
|
||||
108.2014068,
|
||||
16.074192
|
||||
],
|
||||
[
|
||||
108.1988197,
|
||||
16.0736534
|
||||
],
|
||||
[
|
||||
108.1946447,
|
||||
16.0731689
|
||||
],
|
||||
[
|
||||
108.1919899,
|
||||
16.0732907
|
||||
],
|
||||
[
|
||||
108.1872645,
|
||||
16.0740515
|
||||
],
|
||||
[
|
||||
108.1795891,
|
||||
16.0758997
|
||||
],
|
||||
[
|
||||
108.1790709,
|
||||
16.076148
|
||||
],
|
||||
[
|
||||
108.1787527,
|
||||
16.0765067
|
||||
],
|
||||
[
|
||||
108.1785685,
|
||||
16.0763609
|
||||
],
|
||||
[
|
||||
108.1782671,
|
||||
16.0764793
|
||||
],
|
||||
[
|
||||
108.178442,
|
||||
16.0766793
|
||||
],
|
||||
[
|
||||
108.1792614,
|
||||
16.0766718
|
||||
],
|
||||
[
|
||||
108.1783991,
|
||||
16.0768277
|
||||
],
|
||||
[
|
||||
108.1782225,
|
||||
16.0766207
|
||||
],
|
||||
[
|
||||
108.1779249,
|
||||
16.0766679
|
||||
],
|
||||
[
|
||||
108.1739816,
|
||||
16.0781674
|
||||
],
|
||||
[
|
||||
108.1681649,
|
||||
16.0808416
|
||||
],
|
||||
[
|
||||
108.1676454,
|
||||
16.0800972
|
||||
],
|
||||
[
|
||||
108.1667795,
|
||||
16.0806552
|
||||
],
|
||||
[
|
||||
108.1663906,
|
||||
16.0800979
|
||||
],
|
||||
[
|
||||
108.1662126,
|
||||
16.0802127
|
||||
],
|
||||
[
|
||||
108.1637782,
|
||||
16.0767359
|
||||
]
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -3,9 +3,9 @@
|
||||
"properties": {
|
||||
"code": "DN",
|
||||
"name": "Da Nang",
|
||||
"basis": "pre-2025-merger extent, unioned from OSM boundary/historic relations",
|
||||
"parts": 7,
|
||||
"missingParts": 0,
|
||||
"level": "province",
|
||||
"parent": "VN",
|
||||
"basis": "pre-2025-merger extent, from OSM boundary/historic relations",
|
||||
"areaKm2": 986.5,
|
||||
"bbox": [
|
||||
107.81854,
|
||||
@@ -14,10 +14,12 @@
|
||||
16.2255
|
||||
],
|
||||
"center": [
|
||||
16.07868,
|
||||
108.08095
|
||||
16.0544,
|
||||
108.2022
|
||||
],
|
||||
"generatedAt": "2026-08-30T04:03:06.884Z"
|
||||
"generatedAt": "2026-08-30T07:36:51.990Z",
|
||||
"parts": 7,
|
||||
"missingParts": 0
|
||||
},
|
||||
"geometry": {
|
||||
"type": "MultiPolygon",
|
||||
@@ -749,8 +751,8 @@
|
||||
15.9471613
|
||||
],
|
||||
[
|
||||
108.2218754,
|
||||
15.9503445
|
||||
108.2218092,
|
||||
15.9499712
|
||||
],
|
||||
[
|
||||
108.2244456,
|
||||
@@ -861,36 +863,32 @@
|
||||
16.0504905
|
||||
],
|
||||
[
|
||||
108.2477751,
|
||||
16.0577417
|
||||
108.2479383,
|
||||
16.0567485
|
||||
],
|
||||
[
|
||||
108.2468918,
|
||||
16.0675965
|
||||
108.2468928,
|
||||
16.0705626
|
||||
],
|
||||
[
|
||||
108.2473528,
|
||||
16.0763139
|
||||
],
|
||||
[
|
||||
108.2494787,
|
||||
16.0869294
|
||||
108.2498625,
|
||||
16.0880915
|
||||
],
|
||||
[
|
||||
108.2537013,
|
||||
16.0969023
|
||||
108.2547063,
|
||||
16.0983603
|
||||
],
|
||||
[
|
||||
108.2558006,
|
||||
16.0994467
|
||||
108.2570809,
|
||||
16.1004097
|
||||
],
|
||||
[
|
||||
108.2587984,
|
||||
16.1013331
|
||||
],
|
||||
[
|
||||
108.2618515,
|
||||
16.1020152
|
||||
108.2609105,
|
||||
16.1019828
|
||||
],
|
||||
[
|
||||
108.2644034,
|
||||
@@ -1213,16 +1211,12 @@
|
||||
16.12994
|
||||
],
|
||||
[
|
||||
108.2207336,
|
||||
16.1294114
|
||||
108.2206107,
|
||||
16.1286376
|
||||
],
|
||||
[
|
||||
108.2214092,
|
||||
16.1253608
|
||||
],
|
||||
[
|
||||
108.2205994,
|
||||
16.1241474
|
||||
108.2209887,
|
||||
16.1245237
|
||||
],
|
||||
[
|
||||
108.2188185,
|
||||
@@ -1385,8 +1379,8 @@
|
||||
16.1043222
|
||||
],
|
||||
[
|
||||
108.2393896,
|
||||
16.0988628
|
||||
108.2395084,
|
||||
16.0991055
|
||||
],
|
||||
[
|
||||
108.237476,
|
||||
@@ -1417,16 +1411,20 @@
|
||||
16.1010555
|
||||
],
|
||||
[
|
||||
108.2287139,
|
||||
16.1015858
|
||||
108.2287102,
|
||||
16.1015012
|
||||
],
|
||||
[
|
||||
108.2275621,
|
||||
16.1091433
|
||||
108.2276282,
|
||||
16.1088978
|
||||
],
|
||||
[
|
||||
108.2252836,
|
||||
16.1090575
|
||||
108.2256757,
|
||||
16.1091823
|
||||
],
|
||||
[
|
||||
108.2233137,
|
||||
16.1072045
|
||||
],
|
||||
[
|
||||
108.2149991,
|
||||
@@ -1477,8 +1475,8 @@
|
||||
16.0863245
|
||||
],
|
||||
[
|
||||
108.2056965,
|
||||
16.0797638
|
||||
108.2063863,
|
||||
16.0811539
|
||||
],
|
||||
[
|
||||
108.2014068,
|
||||
@@ -1489,32 +1487,44 @@
|
||||
16.0731689
|
||||
],
|
||||
[
|
||||
108.1842622,
|
||||
16.0746895
|
||||
108.1872645,
|
||||
16.0740515
|
||||
],
|
||||
[
|
||||
108.1739816,
|
||||
16.0781674
|
||||
108.1795891,
|
||||
16.0758997
|
||||
],
|
||||
[
|
||||
108.1641868,
|
||||
16.0830919
|
||||
108.1782671,
|
||||
16.0764793
|
||||
],
|
||||
[
|
||||
108.1514418,
|
||||
16.0916966
|
||||
108.1792614,
|
||||
16.0766718
|
||||
],
|
||||
[
|
||||
108.1423974,
|
||||
16.0998453
|
||||
108.1681649,
|
||||
16.0808416
|
||||
],
|
||||
[
|
||||
108.1315738,
|
||||
16.1130775
|
||||
108.1595417,
|
||||
16.0859521
|
||||
],
|
||||
[
|
||||
108.1314437,
|
||||
16.1164409
|
||||
108.1458038,
|
||||
16.0965209
|
||||
],
|
||||
[
|
||||
108.1389041,
|
||||
16.103517
|
||||
],
|
||||
[
|
||||
108.1313604,
|
||||
16.1135573
|
||||
],
|
||||
[
|
||||
108.1310892,
|
||||
16.1154024
|
||||
],
|
||||
[
|
||||
108.1320835,
|
||||
@@ -1545,12 +1555,12 @@
|
||||
16.1313246
|
||||
],
|
||||
[
|
||||
108.1232659,
|
||||
16.1349892
|
||||
108.1232448,
|
||||
16.1343148
|
||||
],
|
||||
[
|
||||
108.1245349,
|
||||
16.1400972
|
||||
108.1246011,
|
||||
16.1402605
|
||||
],
|
||||
[
|
||||
108.1262087,
|
||||
@@ -1569,16 +1579,20 @@
|
||||
16.1400461
|
||||
],
|
||||
[
|
||||
108.1385012,
|
||||
16.1418299
|
||||
108.1375723,
|
||||
16.1411024
|
||||
],
|
||||
[
|
||||
108.1395958,
|
||||
16.1437195
|
||||
],
|
||||
[
|
||||
108.1395994,
|
||||
16.1462941
|
||||
108.1393982,
|
||||
16.1471241
|
||||
],
|
||||
[
|
||||
108.1383141,
|
||||
16.1479677
|
||||
],
|
||||
[
|
||||
108.1369618,
|
||||
@@ -1825,20 +1839,20 @@
|
||||
16.1845569
|
||||
],
|
||||
[
|
||||
108.1191613,
|
||||
16.1811288
|
||||
108.1192894,
|
||||
16.1811608
|
||||
],
|
||||
[
|
||||
108.1160979,
|
||||
16.1828323
|
||||
],
|
||||
[
|
||||
108.1110885,
|
||||
16.1821333
|
||||
108.1109712,
|
||||
16.1821696
|
||||
],
|
||||
[
|
||||
108.1076149,
|
||||
16.1851716
|
||||
108.107908,
|
||||
16.1850078
|
||||
],
|
||||
[
|
||||
108.1051197,
|
||||
@@ -1849,24 +1863,20 @@
|
||||
16.1857654
|
||||
],
|
||||
[
|
||||
108.1007213,
|
||||
16.1866743
|
||||
],
|
||||
[
|
||||
108.0979521,
|
||||
16.1857461
|
||||
108.1005676,
|
||||
16.1866671
|
||||
],
|
||||
[
|
||||
108.0948213,
|
||||
16.1834853
|
||||
],
|
||||
[
|
||||
108.0937482,
|
||||
16.1808037
|
||||
108.0936416,
|
||||
16.1806804
|
||||
],
|
||||
[
|
||||
108.09246,
|
||||
16.1801517
|
||||
108.0921489,
|
||||
16.1801184
|
||||
],
|
||||
[
|
||||
108.0858047,
|
||||
@@ -1893,12 +1903,12 @@
|
||||
16.1840545
|
||||
],
|
||||
[
|
||||
108.0616507,
|
||||
16.1803585
|
||||
108.0610963,
|
||||
16.1798918
|
||||
],
|
||||
[
|
||||
108.0588251,
|
||||
16.1790417
|
||||
108.0584829,
|
||||
16.1790573
|
||||
],
|
||||
[
|
||||
108.036899,
|
||||
@@ -1985,8 +1995,8 @@
|
||||
16.1920087
|
||||
],
|
||||
[
|
||||
107.9559906,
|
||||
16.1890938
|
||||
107.9561164,
|
||||
16.189106
|
||||
],
|
||||
[
|
||||
107.9510164,
|
||||
@@ -2025,20 +2035,20 @@
|
||||
16.2081327
|
||||
],
|
||||
[
|
||||
107.9135245,
|
||||
16.2144119
|
||||
107.9145378,
|
||||
16.2134675
|
||||
],
|
||||
[
|
||||
107.9100849,
|
||||
16.2154467
|
||||
107.9100538,
|
||||
16.2154353
|
||||
],
|
||||
[
|
||||
107.9083372,
|
||||
16.214094
|
||||
],
|
||||
[
|
||||
107.9054296,
|
||||
16.2142213
|
||||
107.9055072,
|
||||
16.214273
|
||||
],
|
||||
[
|
||||
107.9035883,
|
||||
@@ -2053,8 +2063,8 @@
|
||||
16.2051553
|
||||
],
|
||||
[
|
||||
107.8900981,
|
||||
16.2063297
|
||||
107.8901597,
|
||||
16.2063701
|
||||
],
|
||||
[
|
||||
107.8898711,
|
||||
@@ -2065,8 +2075,8 @@
|
||||
16.1996755
|
||||
],
|
||||
[
|
||||
107.883012,
|
||||
16.2000092
|
||||
107.8831581,
|
||||
16.2000466
|
||||
],
|
||||
[
|
||||
107.8784466,
|
||||
@@ -2077,12 +2087,16 @@
|
||||
16.1971294
|
||||
],
|
||||
[
|
||||
107.8757023,
|
||||
16.1969082
|
||||
107.8759112,
|
||||
16.1969754
|
||||
],
|
||||
[
|
||||
107.874788,
|
||||
16.1958879
|
||||
107.8747093,
|
||||
16.195741
|
||||
],
|
||||
[
|
||||
107.8768641,
|
||||
16.1915966
|
||||
],
|
||||
[
|
||||
107.8772951,
|
||||
@@ -2244,6 +2258,384 @@
|
||||
107.8185394,
|
||||
16.0582918
|
||||
]
|
||||
],
|
||||
[
|
||||
[
|
||||
108.2244456,
|
||||
15.9561285
|
||||
],
|
||||
[
|
||||
108.22490230822314,
|
||||
15.95859325833631
|
||||
],
|
||||
[
|
||||
108.2248356,
|
||||
15.9577657
|
||||
],
|
||||
[
|
||||
108.2244456,
|
||||
15.9561285
|
||||
]
|
||||
],
|
||||
[
|
||||
[
|
||||
108.1577075,
|
||||
15.9968464
|
||||
],
|
||||
[
|
||||
108.15770824193828,
|
||||
15.996991923913033
|
||||
],
|
||||
[
|
||||
108.1580695,
|
||||
15.996341
|
||||
],
|
||||
[
|
||||
108.1577075,
|
||||
15.9968464
|
||||
]
|
||||
],
|
||||
[
|
||||
[
|
||||
108.16022107188016,
|
||||
16.034832868239565
|
||||
],
|
||||
[
|
||||
108.160336,
|
||||
16.0349249
|
||||
],
|
||||
[
|
||||
108.16055821891827,
|
||||
16.03492459005462
|
||||
],
|
||||
[
|
||||
108.16022107188016,
|
||||
16.034832868239565
|
||||
]
|
||||
],
|
||||
[
|
||||
[
|
||||
108.16088914579271,
|
||||
16.0349241284861
|
||||
],
|
||||
[
|
||||
108.1622001,
|
||||
16.0349223
|
||||
],
|
||||
[
|
||||
108.16228256376017,
|
||||
16.03484039674117
|
||||
],
|
||||
[
|
||||
108.16088914579271,
|
||||
16.0349241284861
|
||||
]
|
||||
],
|
||||
[
|
||||
[
|
||||
108.16240273711556,
|
||||
16.03472104019704
|
||||
],
|
||||
[
|
||||
108.1629063,
|
||||
16.0342209
|
||||
],
|
||||
[
|
||||
108.16312563354812,
|
||||
16.03379588770228
|
||||
],
|
||||
[
|
||||
108.16240273711556,
|
||||
16.03472104019704
|
||||
]
|
||||
],
|
||||
[
|
||||
[
|
||||
108.1631121,
|
||||
16.031602
|
||||
],
|
||||
[
|
||||
108.16318933010803,
|
||||
16.032120984957178
|
||||
],
|
||||
[
|
||||
108.16313449031094,
|
||||
16.031555032290377
|
||||
],
|
||||
[
|
||||
108.1631121,
|
||||
16.031602
|
||||
]
|
||||
],
|
||||
[
|
||||
[
|
||||
108.16316409983804,
|
||||
16.031492920974667
|
||||
],
|
||||
[
|
||||
108.164008,
|
||||
16.0305657
|
||||
],
|
||||
[
|
||||
108.1632759,
|
||||
16.0312584
|
||||
],
|
||||
[
|
||||
108.16316409983804,
|
||||
16.031492920974667
|
||||
]
|
||||
],
|
||||
[
|
||||
[
|
||||
108.1632816265041,
|
||||
16.03349361326586
|
||||
],
|
||||
[
|
||||
108.1633159,
|
||||
16.0334272
|
||||
],
|
||||
[
|
||||
108.16330634768717,
|
||||
16.033328619093243
|
||||
],
|
||||
[
|
||||
108.1632816265041,
|
||||
16.03349361326586
|
||||
]
|
||||
],
|
||||
[
|
||||
[
|
||||
108.1907680722458,
|
||||
16.026191802488903
|
||||
],
|
||||
[
|
||||
108.1911735836572,
|
||||
16.025089311751618
|
||||
],
|
||||
[
|
||||
108.1910655,
|
||||
16.0253073
|
||||
],
|
||||
[
|
||||
108.1907680722458,
|
||||
16.026191802488903
|
||||
]
|
||||
],
|
||||
[
|
||||
[
|
||||
108.19139150045966,
|
||||
16.024649806804298
|
||||
],
|
||||
[
|
||||
108.1925758,
|
||||
16.0230246
|
||||
],
|
||||
[
|
||||
108.1914988,
|
||||
16.0244334
|
||||
],
|
||||
[
|
||||
108.19139150045966,
|
||||
16.024649806804298
|
||||
]
|
||||
],
|
||||
[
|
||||
[
|
||||
108.2019395043177,
|
||||
16.064712361891978
|
||||
],
|
||||
[
|
||||
108.2020235,
|
||||
16.064716
|
||||
],
|
||||
[
|
||||
108.20206298578704,
|
||||
16.06468233013534
|
||||
],
|
||||
[
|
||||
108.2019395043177,
|
||||
16.064712361891978
|
||||
]
|
||||
],
|
||||
[
|
||||
[
|
||||
108.2022044623575,
|
||||
16.064561691866793
|
||||
],
|
||||
[
|
||||
108.2023521,
|
||||
16.0644358
|
||||
],
|
||||
[
|
||||
108.2039387,
|
||||
16.0613375
|
||||
],
|
||||
[
|
||||
108.2022044623575,
|
||||
16.064561691866793
|
||||
]
|
||||
],
|
||||
[
|
||||
[
|
||||
108.2105123,
|
||||
16.0524394
|
||||
],
|
||||
[
|
||||
108.21271319335202,
|
||||
16.0560258346521
|
||||
],
|
||||
[
|
||||
108.2120927,
|
||||
16.0548307
|
||||
],
|
||||
[
|
||||
108.2105123,
|
||||
16.0524394
|
||||
]
|
||||
],
|
||||
[
|
||||
[
|
||||
108.2128118564316,
|
||||
16.05635869394854
|
||||
],
|
||||
[
|
||||
108.2145593,
|
||||
16.0654636
|
||||
],
|
||||
[
|
||||
108.2129488,
|
||||
16.0568207
|
||||
],
|
||||
[
|
||||
108.2128118564316,
|
||||
16.05635869394854
|
||||
]
|
||||
],
|
||||
[
|
||||
[
|
||||
108.2168663,
|
||||
15.9704269
|
||||
],
|
||||
[
|
||||
108.21686895066311,
|
||||
15.97067122286418
|
||||
],
|
||||
[
|
||||
108.21691051178922,
|
||||
15.970259037642903
|
||||
],
|
||||
[
|
||||
108.2168663,
|
||||
15.9704269
|
||||
]
|
||||
],
|
||||
[
|
||||
[
|
||||
108.2171198343186,
|
||||
15.969464286428526
|
||||
],
|
||||
[
|
||||
108.21724241235806,
|
||||
15.969094169432301
|
||||
],
|
||||
[
|
||||
108.2171891,
|
||||
15.9692013
|
||||
],
|
||||
[
|
||||
108.2171198343186,
|
||||
15.969464286428526
|
||||
]
|
||||
],
|
||||
[
|
||||
[
|
||||
108.21802868002611,
|
||||
15.973749305205336
|
||||
],
|
||||
[
|
||||
108.2181906,
|
||||
15.974046
|
||||
],
|
||||
[
|
||||
108.21862255402532,
|
||||
15.974553163152773
|
||||
],
|
||||
[
|
||||
108.21802868002611,
|
||||
15.973749305205336
|
||||
]
|
||||
],
|
||||
[
|
||||
[
|
||||
108.21953497118656,
|
||||
15.964487298948132
|
||||
],
|
||||
[
|
||||
108.21965495381457,
|
||||
15.96429250177398
|
||||
],
|
||||
[
|
||||
108.2196131,
|
||||
15.9643303
|
||||
],
|
||||
[
|
||||
108.21953497118656,
|
||||
15.964487298948132
|
||||
]
|
||||
],
|
||||
[
|
||||
[
|
||||
108.2234852,
|
||||
15.9621025
|
||||
],
|
||||
[
|
||||
108.2244541,
|
||||
15.9610835
|
||||
],
|
||||
[
|
||||
108.22450040342555,
|
||||
15.960976722235495
|
||||
],
|
||||
[
|
||||
108.2234852,
|
||||
15.9621025
|
||||
]
|
||||
],
|
||||
[
|
||||
[
|
||||
108.22461575633884,
|
||||
15.96071071327262
|
||||
],
|
||||
[
|
||||
108.2247949,
|
||||
15.9602976
|
||||
],
|
||||
[
|
||||
108.22481349676606,
|
||||
15.960134751942578
|
||||
],
|
||||
[
|
||||
108.22461575633884,
|
||||
15.96071071327262
|
||||
]
|
||||
],
|
||||
[
|
||||
[
|
||||
108.22488039792634,
|
||||
15.959548912235272
|
||||
],
|
||||
[
|
||||
108.2249384,
|
||||
15.959041
|
||||
],
|
||||
[
|
||||
108.22492812723578,
|
||||
15.958913559764524
|
||||
],
|
||||
[
|
||||
108.22488039792634,
|
||||
15.959548912235272
|
||||
]
|
||||
]
|
||||
],
|
||||
[
|
||||
@@ -0,0 +1,710 @@
|
||||
{
|
||||
"type": "Feature",
|
||||
"properties": {
|
||||
"code": "HN-BACTULIEM",
|
||||
"name": "Bac Tu Liem",
|
||||
"level": "district",
|
||||
"parent": "HN",
|
||||
"basis": "pre-2025-merger extent, from OSM boundary/historic relations",
|
||||
"areaKm2": 45.4,
|
||||
"bbox": [
|
||||
105.71721,
|
||||
21.03798,
|
||||
105.80499,
|
||||
21.10812
|
||||
],
|
||||
"center": [
|
||||
21.07198,
|
||||
105.7613
|
||||
],
|
||||
"generatedAt": "2026-08-30T07:33:16.285Z",
|
||||
"osm": "relation/9421138"
|
||||
},
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [
|
||||
[
|
||||
[
|
||||
105.7172148,
|
||||
21.0712355
|
||||
],
|
||||
[
|
||||
105.7177709,
|
||||
21.071264
|
||||
],
|
||||
[
|
||||
105.7180273,
|
||||
21.0704879
|
||||
],
|
||||
[
|
||||
105.7177875,
|
||||
21.070236
|
||||
],
|
||||
[
|
||||
105.7209055,
|
||||
21.069546
|
||||
],
|
||||
[
|
||||
105.7206813,
|
||||
21.0686153
|
||||
],
|
||||
[
|
||||
105.7242902,
|
||||
21.0660372
|
||||
],
|
||||
[
|
||||
105.7290196,
|
||||
21.060956
|
||||
],
|
||||
[
|
||||
105.7296708,
|
||||
21.0590002
|
||||
],
|
||||
[
|
||||
105.7290123,
|
||||
21.0587964
|
||||
],
|
||||
[
|
||||
105.729456,
|
||||
21.0575117
|
||||
],
|
||||
[
|
||||
105.7287954,
|
||||
21.0569625
|
||||
],
|
||||
[
|
||||
105.7331864,
|
||||
21.0540117
|
||||
],
|
||||
[
|
||||
105.7372045,
|
||||
21.0441581
|
||||
],
|
||||
[
|
||||
105.7388708,
|
||||
21.0449021
|
||||
],
|
||||
[
|
||||
105.7505533,
|
||||
21.0406432
|
||||
],
|
||||
[
|
||||
105.7533722,
|
||||
21.0379753
|
||||
],
|
||||
[
|
||||
105.7550854,
|
||||
21.0385187
|
||||
],
|
||||
[
|
||||
105.7577856,
|
||||
21.038869
|
||||
],
|
||||
[
|
||||
105.7585526,
|
||||
21.0383606
|
||||
],
|
||||
[
|
||||
105.7606735,
|
||||
21.0400505
|
||||
],
|
||||
[
|
||||
105.7614552,
|
||||
21.0416514
|
||||
],
|
||||
[
|
||||
105.7677076,
|
||||
21.0395819
|
||||
],
|
||||
[
|
||||
105.7687531,
|
||||
21.0424519
|
||||
],
|
||||
[
|
||||
105.7745243,
|
||||
21.0484179
|
||||
],
|
||||
[
|
||||
105.7758297,
|
||||
21.0489147
|
||||
],
|
||||
[
|
||||
105.7814669,
|
||||
21.0489455
|
||||
],
|
||||
[
|
||||
105.781148,
|
||||
21.044065
|
||||
],
|
||||
[
|
||||
105.7847189,
|
||||
21.0440079
|
||||
],
|
||||
[
|
||||
105.7849287,
|
||||
21.0438414
|
||||
],
|
||||
[
|
||||
105.7855808,
|
||||
21.0445483
|
||||
],
|
||||
[
|
||||
105.7863727,
|
||||
21.044547
|
||||
],
|
||||
[
|
||||
105.7863683,
|
||||
21.0461652
|
||||
],
|
||||
[
|
||||
105.7902709,
|
||||
21.0461741
|
||||
],
|
||||
[
|
||||
105.7901611,
|
||||
21.0473156
|
||||
],
|
||||
[
|
||||
105.7882538,
|
||||
21.0466332
|
||||
],
|
||||
[
|
||||
105.7881109,
|
||||
21.0470506
|
||||
],
|
||||
[
|
||||
105.7886883,
|
||||
21.0472371
|
||||
],
|
||||
[
|
||||
105.7885454,
|
||||
21.0475958
|
||||
],
|
||||
[
|
||||
105.78821,
|
||||
21.0477957
|
||||
],
|
||||
[
|
||||
105.7880777,
|
||||
21.0481963
|
||||
],
|
||||
[
|
||||
105.7877438,
|
||||
21.0481887
|
||||
],
|
||||
[
|
||||
105.7877187,
|
||||
21.0486217
|
||||
],
|
||||
[
|
||||
105.7875544,
|
||||
21.0485959
|
||||
],
|
||||
[
|
||||
105.7867629,
|
||||
21.0493078
|
||||
],
|
||||
[
|
||||
105.7869581,
|
||||
21.0512359
|
||||
],
|
||||
[
|
||||
105.788501,
|
||||
21.0512736
|
||||
],
|
||||
[
|
||||
105.7892838,
|
||||
21.0518971
|
||||
],
|
||||
[
|
||||
105.7897289,
|
||||
21.0509462
|
||||
],
|
||||
[
|
||||
105.7899361,
|
||||
21.0492466
|
||||
],
|
||||
[
|
||||
105.7906035,
|
||||
21.0493027
|
||||
],
|
||||
[
|
||||
105.790804,
|
||||
21.0489432
|
||||
],
|
||||
[
|
||||
105.7914564,
|
||||
21.0491413
|
||||
],
|
||||
[
|
||||
105.7915076,
|
||||
21.047914
|
||||
],
|
||||
[
|
||||
105.7910959,
|
||||
21.0478662
|
||||
],
|
||||
[
|
||||
105.7908962,
|
||||
21.048326
|
||||
],
|
||||
[
|
||||
105.7900461,
|
||||
21.0482409
|
||||
],
|
||||
[
|
||||
105.7901045,
|
||||
21.0477222
|
||||
],
|
||||
[
|
||||
105.7906557,
|
||||
21.0478079
|
||||
],
|
||||
[
|
||||
105.7907356,
|
||||
21.0461762
|
||||
],
|
||||
[
|
||||
105.7946172,
|
||||
21.0461932
|
||||
],
|
||||
[
|
||||
105.7946129,
|
||||
21.048313
|
||||
],
|
||||
[
|
||||
105.7949588,
|
||||
21.0483131
|
||||
],
|
||||
[
|
||||
105.7949581,
|
||||
21.0485986
|
||||
],
|
||||
[
|
||||
105.7945973,
|
||||
21.0488423
|
||||
],
|
||||
[
|
||||
105.7947797,
|
||||
21.0489629
|
||||
],
|
||||
[
|
||||
105.7941513,
|
||||
21.0498371
|
||||
],
|
||||
[
|
||||
105.7948877,
|
||||
21.050306
|
||||
],
|
||||
[
|
||||
105.7948851,
|
||||
21.0518683
|
||||
],
|
||||
[
|
||||
105.8007069,
|
||||
21.0518971
|
||||
],
|
||||
[
|
||||
105.8007018,
|
||||
21.0567735
|
||||
],
|
||||
[
|
||||
105.7976453,
|
||||
21.0568003
|
||||
],
|
||||
[
|
||||
105.7976596,
|
||||
21.0597544
|
||||
],
|
||||
[
|
||||
105.7984755,
|
||||
21.0597574
|
||||
],
|
||||
[
|
||||
105.8049927,
|
||||
21.0658798
|
||||
],
|
||||
[
|
||||
105.8037064,
|
||||
21.0674998
|
||||
],
|
||||
[
|
||||
105.8047729,
|
||||
21.068625
|
||||
],
|
||||
[
|
||||
105.8041905,
|
||||
21.0690542
|
||||
],
|
||||
[
|
||||
105.804408,
|
||||
21.0693096
|
||||
],
|
||||
[
|
||||
105.8032616,
|
||||
21.0699916
|
||||
],
|
||||
[
|
||||
105.80403,
|
||||
21.0711055
|
||||
],
|
||||
[
|
||||
105.8033512,
|
||||
21.0714843
|
||||
],
|
||||
[
|
||||
105.8037447,
|
||||
21.0723715
|
||||
],
|
||||
[
|
||||
105.7999578,
|
||||
21.0738816
|
||||
],
|
||||
[
|
||||
105.8011754,
|
||||
21.0766859
|
||||
],
|
||||
[
|
||||
105.801256,
|
||||
21.0784971
|
||||
],
|
||||
[
|
||||
105.7993462,
|
||||
21.0787609
|
||||
],
|
||||
[
|
||||
105.7994434,
|
||||
21.0798267
|
||||
],
|
||||
[
|
||||
105.7986633,
|
||||
21.0797393
|
||||
],
|
||||
[
|
||||
105.7966414,
|
||||
21.0798818
|
||||
],
|
||||
[
|
||||
105.7944054,
|
||||
21.0811956
|
||||
],
|
||||
[
|
||||
105.7918724,
|
||||
21.0833089
|
||||
],
|
||||
[
|
||||
105.7927984,
|
||||
21.0842162
|
||||
],
|
||||
[
|
||||
105.792696,
|
||||
21.0854118
|
||||
],
|
||||
[
|
||||
105.7932273,
|
||||
21.0869727
|
||||
],
|
||||
[
|
||||
105.7931286,
|
||||
21.0901194
|
||||
],
|
||||
[
|
||||
105.7917802,
|
||||
21.0901655
|
||||
],
|
||||
[
|
||||
105.7917862,
|
||||
21.0911942
|
||||
],
|
||||
[
|
||||
105.7942616,
|
||||
21.0973691
|
||||
],
|
||||
[
|
||||
105.7731891,
|
||||
21.0982549
|
||||
],
|
||||
[
|
||||
105.7608903,
|
||||
21.0990715
|
||||
],
|
||||
[
|
||||
105.7517735,
|
||||
21.0977709
|
||||
],
|
||||
[
|
||||
105.748926,
|
||||
21.0989445
|
||||
],
|
||||
[
|
||||
105.740566,
|
||||
21.1081244
|
||||
],
|
||||
[
|
||||
105.7335099,
|
||||
21.1022258
|
||||
],
|
||||
[
|
||||
105.7318037,
|
||||
21.1013992
|
||||
],
|
||||
[
|
||||
105.731791,
|
||||
21.1012382
|
||||
],
|
||||
[
|
||||
105.7311677,
|
||||
21.101054
|
||||
],
|
||||
[
|
||||
105.7310919,
|
||||
21.100615
|
||||
],
|
||||
[
|
||||
105.7298323,
|
||||
21.1009218
|
||||
],
|
||||
[
|
||||
105.7294277,
|
||||
21.0995887
|
||||
],
|
||||
[
|
||||
105.7289175,
|
||||
21.0996337
|
||||
],
|
||||
[
|
||||
105.7286733,
|
||||
21.0998273
|
||||
],
|
||||
[
|
||||
105.7274975,
|
||||
21.0988334
|
||||
],
|
||||
[
|
||||
105.7262363,
|
||||
21.0991244
|
||||
],
|
||||
[
|
||||
105.7252674,
|
||||
21.096873
|
||||
],
|
||||
[
|
||||
105.7247853,
|
||||
21.0969532
|
||||
],
|
||||
[
|
||||
105.7259417,
|
||||
21.095795
|
||||
],
|
||||
[
|
||||
105.7269542,
|
||||
21.0939562
|
||||
],
|
||||
[
|
||||
105.7261293,
|
||||
21.0935348
|
||||
],
|
||||
[
|
||||
105.7262942,
|
||||
21.0932899
|
||||
],
|
||||
[
|
||||
105.7265122,
|
||||
21.0919147
|
||||
],
|
||||
[
|
||||
105.7261578,
|
||||
21.0891501
|
||||
],
|
||||
[
|
||||
105.726252,
|
||||
21.0884495
|
||||
],
|
||||
[
|
||||
105.7269955,
|
||||
21.0884496
|
||||
],
|
||||
[
|
||||
105.7269863,
|
||||
21.0875725
|
||||
],
|
||||
[
|
||||
105.7267844,
|
||||
21.08725
|
||||
],
|
||||
[
|
||||
105.7269128,
|
||||
21.0869468
|
||||
],
|
||||
[
|
||||
105.726599,
|
||||
21.0853843
|
||||
],
|
||||
[
|
||||
105.7266735,
|
||||
21.085195
|
||||
],
|
||||
[
|
||||
105.7279064,
|
||||
21.0844887
|
||||
],
|
||||
[
|
||||
105.728035,
|
||||
21.0836758
|
||||
],
|
||||
[
|
||||
105.7279997,
|
||||
21.083015
|
||||
],
|
||||
[
|
||||
105.727674,
|
||||
21.0830562
|
||||
],
|
||||
[
|
||||
105.7277621,
|
||||
21.081501
|
||||
],
|
||||
[
|
||||
105.727371,
|
||||
21.0815494
|
||||
],
|
||||
[
|
||||
105.7270607,
|
||||
21.0812317
|
||||
],
|
||||
[
|
||||
105.7263841,
|
||||
21.0799384
|
||||
],
|
||||
[
|
||||
105.7261638,
|
||||
21.0799543
|
||||
],
|
||||
[
|
||||
105.7261434,
|
||||
21.0797391
|
||||
],
|
||||
[
|
||||
105.7256631,
|
||||
21.0797413
|
||||
],
|
||||
[
|
||||
105.7258054,
|
||||
21.0791807
|
||||
],
|
||||
[
|
||||
105.7255006,
|
||||
21.0789379
|
||||
],
|
||||
[
|
||||
105.7255537,
|
||||
21.0785361
|
||||
],
|
||||
[
|
||||
105.7252699,
|
||||
21.0784772
|
||||
],
|
||||
[
|
||||
105.7252621,
|
||||
21.0779078
|
||||
],
|
||||
[
|
||||
105.7243497,
|
||||
21.0775992
|
||||
],
|
||||
[
|
||||
105.7242662,
|
||||
21.0769439
|
||||
],
|
||||
[
|
||||
105.7240961,
|
||||
21.0767373
|
||||
],
|
||||
[
|
||||
105.7242036,
|
||||
21.0760387
|
||||
],
|
||||
[
|
||||
105.7239456,
|
||||
21.0759283
|
||||
],
|
||||
[
|
||||
105.7238569,
|
||||
21.0756562
|
||||
],
|
||||
[
|
||||
105.7241279,
|
||||
21.075079
|
||||
],
|
||||
[
|
||||
105.7229578,
|
||||
21.0747123
|
||||
],
|
||||
[
|
||||
105.722399,
|
||||
21.0747635
|
||||
],
|
||||
[
|
||||
105.7226259,
|
||||
21.0744534
|
||||
],
|
||||
[
|
||||
105.7214755,
|
||||
21.0738322
|
||||
],
|
||||
[
|
||||
105.7213751,
|
||||
21.0738984
|
||||
],
|
||||
[
|
||||
105.7210955,
|
||||
21.0731548
|
||||
],
|
||||
[
|
||||
105.7205562,
|
||||
21.0729387
|
||||
],
|
||||
[
|
||||
105.7204228,
|
||||
21.072575
|
||||
],
|
||||
[
|
||||
105.7206415,
|
||||
21.0724823
|
||||
],
|
||||
[
|
||||
105.7205713,
|
||||
21.0721978
|
||||
],
|
||||
[
|
||||
105.7195249,
|
||||
21.0728198
|
||||
],
|
||||
[
|
||||
105.7188904,
|
||||
21.074765
|
||||
],
|
||||
[
|
||||
105.7179651,
|
||||
21.073752
|
||||
],
|
||||
[
|
||||
105.7182243,
|
||||
21.0734808
|
||||
],
|
||||
[
|
||||
105.7176567,
|
||||
21.0732149
|
||||
],
|
||||
[
|
||||
105.717893,
|
||||
21.0724994
|
||||
],
|
||||
[
|
||||
105.7172148,
|
||||
21.0712355
|
||||
]
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,610 @@
|
||||
{
|
||||
"type": "Feature",
|
||||
"properties": {
|
||||
"code": "HN-BADINH",
|
||||
"name": "Ba Dinh",
|
||||
"level": "district",
|
||||
"parent": "HN",
|
||||
"basis": "pre-2025-merger extent, from OSM boundary/historic relations",
|
||||
"areaKm2": 9.2,
|
||||
"bbox": [
|
||||
105.80135,
|
||||
21.01596,
|
||||
105.85597,
|
||||
21.05337
|
||||
],
|
||||
"center": [
|
||||
21.03578,
|
||||
105.826
|
||||
],
|
||||
"generatedAt": "2026-08-30T07:33:00.443Z",
|
||||
"osm": "relation/9421136"
|
||||
},
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [
|
||||
[
|
||||
[
|
||||
105.8013534,
|
||||
21.0302334
|
||||
],
|
||||
[
|
||||
105.8032273,
|
||||
21.0291131
|
||||
],
|
||||
[
|
||||
105.8050074,
|
||||
21.0282937
|
||||
],
|
||||
[
|
||||
105.8053147,
|
||||
21.0278839
|
||||
],
|
||||
[
|
||||
105.8067562,
|
||||
21.0275083
|
||||
],
|
||||
[
|
||||
105.8066266,
|
||||
21.0261623
|
||||
],
|
||||
[
|
||||
105.8068966,
|
||||
21.0261608
|
||||
],
|
||||
[
|
||||
105.8069025,
|
||||
21.025964
|
||||
],
|
||||
[
|
||||
105.8077162,
|
||||
21.025925
|
||||
],
|
||||
[
|
||||
105.8079102,
|
||||
21.0261342
|
||||
],
|
||||
[
|
||||
105.8096483,
|
||||
21.0252642
|
||||
],
|
||||
[
|
||||
105.8102855,
|
||||
21.0261951
|
||||
],
|
||||
[
|
||||
105.8115325,
|
||||
21.0246392
|
||||
],
|
||||
[
|
||||
105.8120498,
|
||||
21.0215589
|
||||
],
|
||||
[
|
||||
105.8108709,
|
||||
21.021345
|
||||
],
|
||||
[
|
||||
105.8104027,
|
||||
21.020536
|
||||
],
|
||||
[
|
||||
105.8113367,
|
||||
21.020062
|
||||
],
|
||||
[
|
||||
105.8105849,
|
||||
21.018711
|
||||
],
|
||||
[
|
||||
105.8149171,
|
||||
21.0165734
|
||||
],
|
||||
[
|
||||
105.8158704,
|
||||
21.0159567
|
||||
],
|
||||
[
|
||||
105.8165767,
|
||||
21.0169109
|
||||
],
|
||||
[
|
||||
105.8173608,
|
||||
21.0172825
|
||||
],
|
||||
[
|
||||
105.8183404,
|
||||
21.0184046
|
||||
],
|
||||
[
|
||||
105.819316,
|
||||
21.0190316
|
||||
],
|
||||
[
|
||||
105.8191122,
|
||||
21.0194952
|
||||
],
|
||||
[
|
||||
105.8192878,
|
||||
21.0201126
|
||||
],
|
||||
[
|
||||
105.8195228,
|
||||
21.0205859
|
||||
],
|
||||
[
|
||||
105.8196582,
|
||||
21.0205423
|
||||
],
|
||||
[
|
||||
105.82065,
|
||||
21.0226782
|
||||
],
|
||||
[
|
||||
105.8210299,
|
||||
21.0231258
|
||||
],
|
||||
[
|
||||
105.8197685,
|
||||
21.0234032
|
||||
],
|
||||
[
|
||||
105.8248194,
|
||||
21.0277351
|
||||
],
|
||||
[
|
||||
105.8272335,
|
||||
21.0295029
|
||||
],
|
||||
[
|
||||
105.8275747,
|
||||
21.03032
|
||||
],
|
||||
[
|
||||
105.8284603,
|
||||
21.0311379
|
||||
],
|
||||
[
|
||||
105.8283936,
|
||||
21.031386
|
||||
],
|
||||
[
|
||||
105.8286772,
|
||||
21.0315534
|
||||
],
|
||||
[
|
||||
105.8288463,
|
||||
21.0320518
|
||||
],
|
||||
[
|
||||
105.8298787,
|
||||
21.0317602
|
||||
],
|
||||
[
|
||||
105.8300006,
|
||||
21.0321494
|
||||
],
|
||||
[
|
||||
105.8302576,
|
||||
21.0320793
|
||||
],
|
||||
[
|
||||
105.8297502,
|
||||
21.0303218
|
||||
],
|
||||
[
|
||||
105.8307945,
|
||||
21.0300438
|
||||
],
|
||||
[
|
||||
105.8308857,
|
||||
21.0303829
|
||||
],
|
||||
[
|
||||
105.8317068,
|
||||
21.0301997
|
||||
],
|
||||
[
|
||||
105.8318224,
|
||||
21.0306412
|
||||
],
|
||||
[
|
||||
105.8315192,
|
||||
21.0307155
|
||||
],
|
||||
[
|
||||
105.8316065,
|
||||
21.0312824
|
||||
],
|
||||
[
|
||||
105.8320219,
|
||||
21.0312598
|
||||
],
|
||||
[
|
||||
105.8321158,
|
||||
21.0315737
|
||||
],
|
||||
[
|
||||
105.8329906,
|
||||
21.0313288
|
||||
],
|
||||
[
|
||||
105.8328713,
|
||||
21.0310019
|
||||
],
|
||||
[
|
||||
105.8332284,
|
||||
21.0308417
|
||||
],
|
||||
[
|
||||
105.8331747,
|
||||
21.0306737
|
||||
],
|
||||
[
|
||||
105.8335628,
|
||||
21.0305588
|
||||
],
|
||||
[
|
||||
105.8338136,
|
||||
21.0310934
|
||||
],
|
||||
[
|
||||
105.83403,
|
||||
21.0310278
|
||||
],
|
||||
[
|
||||
105.8347106,
|
||||
21.0298926
|
||||
],
|
||||
[
|
||||
105.8349492,
|
||||
21.0297952
|
||||
],
|
||||
[
|
||||
105.8357925,
|
||||
21.0305184
|
||||
],
|
||||
[
|
||||
105.8369865,
|
||||
21.0301779
|
||||
],
|
||||
[
|
||||
105.836893,
|
||||
21.0299308
|
||||
],
|
||||
[
|
||||
105.8375757,
|
||||
21.0295642
|
||||
],
|
||||
[
|
||||
105.8378684,
|
||||
21.0296354
|
||||
],
|
||||
[
|
||||
105.8383293,
|
||||
21.0294864
|
||||
],
|
||||
[
|
||||
105.8384777,
|
||||
21.0297506
|
||||
],
|
||||
[
|
||||
105.8391429,
|
||||
21.0295599
|
||||
],
|
||||
[
|
||||
105.8389332,
|
||||
21.0289616
|
||||
],
|
||||
[
|
||||
105.8391988,
|
||||
21.0288902
|
||||
],
|
||||
[
|
||||
105.8391332,
|
||||
21.0286196
|
||||
],
|
||||
[
|
||||
105.8394059,
|
||||
21.0285095
|
||||
],
|
||||
[
|
||||
105.8397698,
|
||||
21.0284323
|
||||
],
|
||||
[
|
||||
105.8398793,
|
||||
21.0289422
|
||||
],
|
||||
[
|
||||
105.8406544,
|
||||
21.0287987
|
||||
],
|
||||
[
|
||||
105.8406182,
|
||||
21.028701
|
||||
],
|
||||
[
|
||||
105.8408062,
|
||||
21.0286922
|
||||
],
|
||||
[
|
||||
105.8407663,
|
||||
21.0284898
|
||||
],
|
||||
[
|
||||
105.8410927,
|
||||
21.0283566
|
||||
],
|
||||
[
|
||||
105.8412352,
|
||||
21.0287727
|
||||
],
|
||||
[
|
||||
105.8415672,
|
||||
21.0287502
|
||||
],
|
||||
[
|
||||
105.841569,
|
||||
21.0288646
|
||||
],
|
||||
[
|
||||
105.8420875,
|
||||
21.0287131
|
||||
],
|
||||
[
|
||||
105.8440669,
|
||||
21.030125
|
||||
],
|
||||
[
|
||||
105.8439406,
|
||||
21.0301552
|
||||
],
|
||||
[
|
||||
105.8442575,
|
||||
21.0315898
|
||||
],
|
||||
[
|
||||
105.8438257,
|
||||
21.0316686
|
||||
],
|
||||
[
|
||||
105.8438773,
|
||||
21.0319139
|
||||
],
|
||||
[
|
||||
105.8437144,
|
||||
21.0319452
|
||||
],
|
||||
[
|
||||
105.8438605,
|
||||
21.0328868
|
||||
],
|
||||
[
|
||||
105.8440313,
|
||||
21.0330194
|
||||
],
|
||||
[
|
||||
105.8442093,
|
||||
21.0337496
|
||||
],
|
||||
[
|
||||
105.8440242,
|
||||
21.0337895
|
||||
],
|
||||
[
|
||||
105.8442231,
|
||||
21.0349021
|
||||
],
|
||||
[
|
||||
105.8441134,
|
||||
21.0350784
|
||||
],
|
||||
[
|
||||
105.8447417,
|
||||
21.0385729
|
||||
],
|
||||
[
|
||||
105.8444986,
|
||||
21.0386255
|
||||
],
|
||||
[
|
||||
105.8446653,
|
||||
21.0393873
|
||||
],
|
||||
[
|
||||
105.8453496,
|
||||
21.0393732
|
||||
],
|
||||
[
|
||||
105.8455139,
|
||||
21.0394981
|
||||
],
|
||||
[
|
||||
105.8456119,
|
||||
21.0399869
|
||||
],
|
||||
[
|
||||
105.8468736,
|
||||
21.0398071
|
||||
],
|
||||
[
|
||||
105.8498029,
|
||||
21.0406472
|
||||
],
|
||||
[
|
||||
105.850373,
|
||||
21.0409979
|
||||
],
|
||||
[
|
||||
105.8511635,
|
||||
21.0404082
|
||||
],
|
||||
[
|
||||
105.8559747,
|
||||
21.0422867
|
||||
],
|
||||
[
|
||||
105.8500753,
|
||||
21.049457
|
||||
],
|
||||
[
|
||||
105.8482762,
|
||||
21.0533744
|
||||
],
|
||||
[
|
||||
105.8442258,
|
||||
21.049303
|
||||
],
|
||||
[
|
||||
105.8440237,
|
||||
21.049552
|
||||
],
|
||||
[
|
||||
105.8432213,
|
||||
21.049163
|
||||
],
|
||||
[
|
||||
105.8431374,
|
||||
21.0492496
|
||||
],
|
||||
[
|
||||
105.8422694,
|
||||
21.0486018
|
||||
],
|
||||
[
|
||||
105.8408247,
|
||||
21.0501274
|
||||
],
|
||||
[
|
||||
105.840108,
|
||||
21.0506702
|
||||
],
|
||||
[
|
||||
105.8392367,
|
||||
21.050005
|
||||
],
|
||||
[
|
||||
105.8385809,
|
||||
21.0488143
|
||||
],
|
||||
[
|
||||
105.8368352,
|
||||
21.0466532
|
||||
],
|
||||
[
|
||||
105.8365711,
|
||||
21.0443813
|
||||
],
|
||||
[
|
||||
105.8363678,
|
||||
21.0444067
|
||||
],
|
||||
[
|
||||
105.835859,
|
||||
21.0429612
|
||||
],
|
||||
[
|
||||
105.8358479,
|
||||
21.0427603
|
||||
],
|
||||
[
|
||||
105.8362631,
|
||||
21.0423036
|
||||
],
|
||||
[
|
||||
105.8362002,
|
||||
21.0416431
|
||||
],
|
||||
[
|
||||
105.8334509,
|
||||
21.041894
|
||||
],
|
||||
[
|
||||
105.8310824,
|
||||
21.041664
|
||||
],
|
||||
[
|
||||
105.8305126,
|
||||
21.041389
|
||||
],
|
||||
[
|
||||
105.828857,
|
||||
21.0396975
|
||||
],
|
||||
[
|
||||
105.8284972,
|
||||
21.0395116
|
||||
],
|
||||
[
|
||||
105.8270974,
|
||||
21.0398022
|
||||
],
|
||||
[
|
||||
105.8220831,
|
||||
21.0417144
|
||||
],
|
||||
[
|
||||
105.8201032,
|
||||
21.0420842
|
||||
],
|
||||
[
|
||||
105.8170597,
|
||||
21.0422873
|
||||
],
|
||||
[
|
||||
105.8150272,
|
||||
21.0427711
|
||||
],
|
||||
[
|
||||
105.813445,
|
||||
21.0438392
|
||||
],
|
||||
[
|
||||
105.811321,
|
||||
21.0459144
|
||||
],
|
||||
[
|
||||
105.8105391,
|
||||
21.046368
|
||||
],
|
||||
[
|
||||
105.8080451,
|
||||
21.0473732
|
||||
],
|
||||
[
|
||||
105.806034,
|
||||
21.0475871
|
||||
],
|
||||
[
|
||||
105.8060641,
|
||||
21.0424787
|
||||
],
|
||||
[
|
||||
105.8066358,
|
||||
21.040179
|
||||
],
|
||||
[
|
||||
105.8062057,
|
||||
21.0400902
|
||||
],
|
||||
[
|
||||
105.806476,
|
||||
21.0387044
|
||||
],
|
||||
[
|
||||
105.8062751,
|
||||
21.0368524
|
||||
],
|
||||
[
|
||||
105.8036452,
|
||||
21.0328399
|
||||
],
|
||||
[
|
||||
105.8013534,
|
||||
21.0302334
|
||||
]
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,598 @@
|
||||
{
|
||||
"type": "Feature",
|
||||
"properties": {
|
||||
"code": "HN-CAUGIAY",
|
||||
"name": "Cau Giay",
|
||||
"level": "district",
|
||||
"parent": "HN",
|
||||
"basis": "pre-2025-merger extent, from OSM boundary/historic relations",
|
||||
"areaKm2": 12.5,
|
||||
"bbox": [
|
||||
105.76716,
|
||||
21.00066,
|
||||
105.81352,
|
||||
21.05879
|
||||
],
|
||||
"center": [
|
||||
21.0306,
|
||||
105.79168
|
||||
],
|
||||
"generatedAt": "2026-08-30T07:33:07.812Z",
|
||||
"osm": "relation/9421132"
|
||||
},
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [
|
||||
[
|
||||
[
|
||||
105.7671552,
|
||||
21.037974
|
||||
],
|
||||
[
|
||||
105.76838,
|
||||
21.0375786
|
||||
],
|
||||
[
|
||||
105.7681368,
|
||||
21.0369085
|
||||
],
|
||||
[
|
||||
105.7687349,
|
||||
21.0367241
|
||||
],
|
||||
[
|
||||
105.768541,
|
||||
21.0359941
|
||||
],
|
||||
[
|
||||
105.7704902,
|
||||
21.035438
|
||||
],
|
||||
[
|
||||
105.7715069,
|
||||
21.035458
|
||||
],
|
||||
[
|
||||
105.7713989,
|
||||
21.0351084
|
||||
],
|
||||
[
|
||||
105.7728314,
|
||||
21.0346244
|
||||
],
|
||||
[
|
||||
105.7739591,
|
||||
21.0335768
|
||||
],
|
||||
[
|
||||
105.773558,
|
||||
21.0329239
|
||||
],
|
||||
[
|
||||
105.7743821,
|
||||
21.0325638
|
||||
],
|
||||
[
|
||||
105.7744746,
|
||||
21.0327242
|
||||
],
|
||||
[
|
||||
105.774857,
|
||||
21.0325864
|
||||
],
|
||||
[
|
||||
105.7754638,
|
||||
21.0326857
|
||||
],
|
||||
[
|
||||
105.7754519,
|
||||
21.0316668
|
||||
],
|
||||
[
|
||||
105.7763153,
|
||||
21.0316715
|
||||
],
|
||||
[
|
||||
105.77687,
|
||||
21.0309349
|
||||
],
|
||||
[
|
||||
105.7788567,
|
||||
21.0317565
|
||||
],
|
||||
[
|
||||
105.7789263,
|
||||
21.0316035
|
||||
],
|
||||
[
|
||||
105.7798595,
|
||||
21.0323643
|
||||
],
|
||||
[
|
||||
105.7799473,
|
||||
21.0315499
|
||||
],
|
||||
[
|
||||
105.7818257,
|
||||
21.0312663
|
||||
],
|
||||
[
|
||||
105.7815738,
|
||||
21.0294594
|
||||
],
|
||||
[
|
||||
105.7838889,
|
||||
21.0292615
|
||||
],
|
||||
[
|
||||
105.7840713,
|
||||
21.0243703
|
||||
],
|
||||
[
|
||||
105.7815587,
|
||||
21.0221948
|
||||
],
|
||||
[
|
||||
105.7850393,
|
||||
21.0180582
|
||||
],
|
||||
[
|
||||
105.7850953,
|
||||
21.0170482
|
||||
],
|
||||
[
|
||||
105.785472,
|
||||
21.0162432
|
||||
],
|
||||
[
|
||||
105.7902222,
|
||||
21.0104008
|
||||
],
|
||||
[
|
||||
105.7896689,
|
||||
21.0100039
|
||||
],
|
||||
[
|
||||
105.7903402,
|
||||
21.0091788
|
||||
],
|
||||
[
|
||||
105.789528,
|
||||
21.0086363
|
||||
],
|
||||
[
|
||||
105.7923279,
|
||||
21.0051517
|
||||
],
|
||||
[
|
||||
105.7911792,
|
||||
21.0042771
|
||||
],
|
||||
[
|
||||
105.7923268,
|
||||
21.0029651
|
||||
],
|
||||
[
|
||||
105.7912413,
|
||||
21.0020701
|
||||
],
|
||||
[
|
||||
105.7923056,
|
||||
21.0006558
|
||||
],
|
||||
[
|
||||
105.7946544,
|
||||
21.0022956
|
||||
],
|
||||
[
|
||||
105.7941053,
|
||||
21.0030027
|
||||
],
|
||||
[
|
||||
105.7976586,
|
||||
21.0054696
|
||||
],
|
||||
[
|
||||
105.7987992,
|
||||
21.0040802
|
||||
],
|
||||
[
|
||||
105.8029771,
|
||||
21.007087
|
||||
],
|
||||
[
|
||||
105.8046619,
|
||||
21.0085679
|
||||
],
|
||||
[
|
||||
105.804979,
|
||||
21.0080815
|
||||
],
|
||||
[
|
||||
105.8054112,
|
||||
21.00838
|
||||
],
|
||||
[
|
||||
105.807369,
|
||||
21.0072094
|
||||
],
|
||||
[
|
||||
105.8086536,
|
||||
21.0080805
|
||||
],
|
||||
[
|
||||
105.8101905,
|
||||
21.0070798
|
||||
],
|
||||
[
|
||||
105.8106943,
|
||||
21.0069677
|
||||
],
|
||||
[
|
||||
105.812051,
|
||||
21.0071526
|
||||
],
|
||||
[
|
||||
105.8135162,
|
||||
21.0088516
|
||||
],
|
||||
[
|
||||
105.8076252,
|
||||
21.012689
|
||||
],
|
||||
[
|
||||
105.8041936,
|
||||
21.0157182
|
||||
],
|
||||
[
|
||||
105.8030396,
|
||||
21.0171604
|
||||
],
|
||||
[
|
||||
105.8012422,
|
||||
21.0203624
|
||||
],
|
||||
[
|
||||
105.8001477,
|
||||
21.0219911
|
||||
],
|
||||
[
|
||||
105.7980183,
|
||||
21.0237715
|
||||
],
|
||||
[
|
||||
105.7976474,
|
||||
21.0244273
|
||||
],
|
||||
[
|
||||
105.797701,
|
||||
21.0251591
|
||||
],
|
||||
[
|
||||
105.7983286,
|
||||
21.0268031
|
||||
],
|
||||
[
|
||||
105.8035437,
|
||||
21.032707
|
||||
],
|
||||
[
|
||||
105.8062751,
|
||||
21.0368524
|
||||
],
|
||||
[
|
||||
105.806476,
|
||||
21.0387044
|
||||
],
|
||||
[
|
||||
105.8062057,
|
||||
21.0400902
|
||||
],
|
||||
[
|
||||
105.8066358,
|
||||
21.040179
|
||||
],
|
||||
[
|
||||
105.8060641,
|
||||
21.0424787
|
||||
],
|
||||
[
|
||||
105.806034,
|
||||
21.0475871
|
||||
],
|
||||
[
|
||||
105.8064319,
|
||||
21.0485954
|
||||
],
|
||||
[
|
||||
105.8073546,
|
||||
21.0501456
|
||||
],
|
||||
[
|
||||
105.8091278,
|
||||
21.0524025
|
||||
],
|
||||
[
|
||||
105.8088619,
|
||||
21.0543138
|
||||
],
|
||||
[
|
||||
105.8088495,
|
||||
21.0587904
|
||||
],
|
||||
[
|
||||
105.8081818,
|
||||
21.0587044
|
||||
],
|
||||
[
|
||||
105.8081608,
|
||||
21.0556049
|
||||
],
|
||||
[
|
||||
105.8078521,
|
||||
21.0558373
|
||||
],
|
||||
[
|
||||
105.8072297,
|
||||
21.0555079
|
||||
],
|
||||
[
|
||||
105.8066696,
|
||||
21.0561522
|
||||
],
|
||||
[
|
||||
105.8062956,
|
||||
21.0558457
|
||||
],
|
||||
[
|
||||
105.8056078,
|
||||
21.0546947
|
||||
],
|
||||
[
|
||||
105.806194,
|
||||
21.0542752
|
||||
],
|
||||
[
|
||||
105.8054074,
|
||||
21.0530567
|
||||
],
|
||||
[
|
||||
105.8052993,
|
||||
21.0531065
|
||||
],
|
||||
[
|
||||
105.8048501,
|
||||
21.0523938
|
||||
],
|
||||
[
|
||||
105.8044541,
|
||||
21.052675
|
||||
],
|
||||
[
|
||||
105.8044655,
|
||||
21.052119
|
||||
],
|
||||
[
|
||||
105.8032079,
|
||||
21.0514579
|
||||
],
|
||||
[
|
||||
105.8017784,
|
||||
21.0495312
|
||||
],
|
||||
[
|
||||
105.8000849,
|
||||
21.0495234
|
||||
],
|
||||
[
|
||||
105.7983277,
|
||||
21.0477188
|
||||
],
|
||||
[
|
||||
105.7976126,
|
||||
21.047933
|
||||
],
|
||||
[
|
||||
105.7970657,
|
||||
21.0484688
|
||||
],
|
||||
[
|
||||
105.7970526,
|
||||
21.0489978
|
||||
],
|
||||
[
|
||||
105.7963237,
|
||||
21.0490036
|
||||
],
|
||||
[
|
||||
105.7963479,
|
||||
21.0502891
|
||||
],
|
||||
[
|
||||
105.7948877,
|
||||
21.050306
|
||||
],
|
||||
[
|
||||
105.7941513,
|
||||
21.0498371
|
||||
],
|
||||
[
|
||||
105.7947797,
|
||||
21.0489629
|
||||
],
|
||||
[
|
||||
105.7945973,
|
||||
21.0488423
|
||||
],
|
||||
[
|
||||
105.7949581,
|
||||
21.0485986
|
||||
],
|
||||
[
|
||||
105.7949588,
|
||||
21.0483131
|
||||
],
|
||||
[
|
||||
105.7946129,
|
||||
21.048313
|
||||
],
|
||||
[
|
||||
105.7946172,
|
||||
21.0461932
|
||||
],
|
||||
[
|
||||
105.7907356,
|
||||
21.0461762
|
||||
],
|
||||
[
|
||||
105.7906557,
|
||||
21.0478079
|
||||
],
|
||||
[
|
||||
105.7901045,
|
||||
21.0477222
|
||||
],
|
||||
[
|
||||
105.7900461,
|
||||
21.0482409
|
||||
],
|
||||
[
|
||||
105.7908962,
|
||||
21.048326
|
||||
],
|
||||
[
|
||||
105.7910959,
|
||||
21.0478662
|
||||
],
|
||||
[
|
||||
105.7915076,
|
||||
21.047914
|
||||
],
|
||||
[
|
||||
105.7914564,
|
||||
21.0491413
|
||||
],
|
||||
[
|
||||
105.790804,
|
||||
21.0489432
|
||||
],
|
||||
[
|
||||
105.7906035,
|
||||
21.0493027
|
||||
],
|
||||
[
|
||||
105.7899361,
|
||||
21.0492466
|
||||
],
|
||||
[
|
||||
105.7897289,
|
||||
21.0509462
|
||||
],
|
||||
[
|
||||
105.7892838,
|
||||
21.0518971
|
||||
],
|
||||
[
|
||||
105.788501,
|
||||
21.0512736
|
||||
],
|
||||
[
|
||||
105.7869581,
|
||||
21.0512359
|
||||
],
|
||||
[
|
||||
105.7867629,
|
||||
21.0493078
|
||||
],
|
||||
[
|
||||
105.7875544,
|
||||
21.0485959
|
||||
],
|
||||
[
|
||||
105.7877187,
|
||||
21.0486217
|
||||
],
|
||||
[
|
||||
105.7877438,
|
||||
21.0481887
|
||||
],
|
||||
[
|
||||
105.7880777,
|
||||
21.0481963
|
||||
],
|
||||
[
|
||||
105.78821,
|
||||
21.0477957
|
||||
],
|
||||
[
|
||||
105.7885454,
|
||||
21.0475958
|
||||
],
|
||||
[
|
||||
105.7886883,
|
||||
21.0472371
|
||||
],
|
||||
[
|
||||
105.7881109,
|
||||
21.0470506
|
||||
],
|
||||
[
|
||||
105.7882538,
|
||||
21.0466332
|
||||
],
|
||||
[
|
||||
105.7901611,
|
||||
21.0473156
|
||||
],
|
||||
[
|
||||
105.7902709,
|
||||
21.0461741
|
||||
],
|
||||
[
|
||||
105.7863683,
|
||||
21.0461652
|
||||
],
|
||||
[
|
||||
105.7863727,
|
||||
21.044547
|
||||
],
|
||||
[
|
||||
105.7855808,
|
||||
21.0445483
|
||||
],
|
||||
[
|
||||
105.7849287,
|
||||
21.0438414
|
||||
],
|
||||
[
|
||||
105.7847189,
|
||||
21.0440079
|
||||
],
|
||||
[
|
||||
105.781148,
|
||||
21.044065
|
||||
],
|
||||
[
|
||||
105.7814669,
|
||||
21.0489455
|
||||
],
|
||||
[
|
||||
105.7758297,
|
||||
21.0489147
|
||||
],
|
||||
[
|
||||
105.7745243,
|
||||
21.0484179
|
||||
],
|
||||
[
|
||||
105.7687531,
|
||||
21.0424519
|
||||
],
|
||||
[
|
||||
105.7671552,
|
||||
21.037974
|
||||
]
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,998 @@
|
||||
{
|
||||
"type": "Feature",
|
||||
"properties": {
|
||||
"code": "HN-DANPHUONG",
|
||||
"name": "Dan Phuong",
|
||||
"level": "district",
|
||||
"parent": "HN",
|
||||
"basis": "pre-2025-merger extent, from OSM boundary/historic relations",
|
||||
"areaKm2": 78.4,
|
||||
"bbox": [
|
||||
105.62321,
|
||||
21.06614,
|
||||
105.74057,
|
||||
21.17336
|
||||
],
|
||||
"center": [
|
||||
21.11385,
|
||||
105.67739
|
||||
],
|
||||
"generatedAt": "2026-08-30T07:33:31.743Z",
|
||||
"osm": "relation/7115780"
|
||||
},
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [
|
||||
[
|
||||
[
|
||||
105.6232142,
|
||||
21.1351267
|
||||
],
|
||||
[
|
||||
105.6234883,
|
||||
21.1341216
|
||||
],
|
||||
[
|
||||
105.6253135,
|
||||
21.1317565
|
||||
],
|
||||
[
|
||||
105.6286663,
|
||||
21.1287595
|
||||
],
|
||||
[
|
||||
105.6299934,
|
||||
21.1270003
|
||||
],
|
||||
[
|
||||
105.6304026,
|
||||
21.1261905
|
||||
],
|
||||
[
|
||||
105.6303826,
|
||||
21.1245616
|
||||
],
|
||||
[
|
||||
105.6298338,
|
||||
21.1238542
|
||||
],
|
||||
[
|
||||
105.6280875,
|
||||
21.1225418
|
||||
],
|
||||
[
|
||||
105.6272215,
|
||||
21.1222092
|
||||
],
|
||||
[
|
||||
105.625225,
|
||||
21.1209983
|
||||
],
|
||||
[
|
||||
105.6243925,
|
||||
21.1202217
|
||||
],
|
||||
[
|
||||
105.6243925,
|
||||
21.1185962
|
||||
],
|
||||
[
|
||||
105.6250627,
|
||||
21.1175761
|
||||
],
|
||||
[
|
||||
105.6260433,
|
||||
21.1166614
|
||||
],
|
||||
[
|
||||
105.629789,
|
||||
21.113978
|
||||
],
|
||||
[
|
||||
105.6312804,
|
||||
21.1136292
|
||||
],
|
||||
[
|
||||
105.6369767,
|
||||
21.1116116
|
||||
],
|
||||
[
|
||||
105.6371333,
|
||||
21.1117231
|
||||
],
|
||||
[
|
||||
105.6375546,
|
||||
21.1114975
|
||||
],
|
||||
[
|
||||
105.6372728,
|
||||
21.1099561
|
||||
],
|
||||
[
|
||||
105.6394702,
|
||||
21.1095991
|
||||
],
|
||||
[
|
||||
105.6385442,
|
||||
21.1054658
|
||||
],
|
||||
[
|
||||
105.6363967,
|
||||
21.107024
|
||||
],
|
||||
[
|
||||
105.6342593,
|
||||
21.1007474
|
||||
],
|
||||
[
|
||||
105.6400122,
|
||||
21.0987586
|
||||
],
|
||||
[
|
||||
105.6425373,
|
||||
21.0973355
|
||||
],
|
||||
[
|
||||
105.6446079,
|
||||
21.0951669
|
||||
],
|
||||
[
|
||||
105.6458912,
|
||||
21.0940879
|
||||
],
|
||||
[
|
||||
105.647371,
|
||||
21.0909683
|
||||
],
|
||||
[
|
||||
105.647399,
|
||||
21.0894163
|
||||
],
|
||||
[
|
||||
105.646813,
|
||||
21.0886543
|
||||
],
|
||||
[
|
||||
105.645531,
|
||||
21.0878063
|
||||
],
|
||||
[
|
||||
105.645177,
|
||||
21.0871933
|
||||
],
|
||||
[
|
||||
105.6448,
|
||||
21.0858583
|
||||
],
|
||||
[
|
||||
105.644816,
|
||||
21.0846963
|
||||
],
|
||||
[
|
||||
105.645676,
|
||||
21.0833093
|
||||
],
|
||||
[
|
||||
105.645715,
|
||||
21.0828633
|
||||
],
|
||||
[
|
||||
105.64533,
|
||||
21.0795713
|
||||
],
|
||||
[
|
||||
105.645231,
|
||||
21.075942
|
||||
],
|
||||
[
|
||||
105.646384,
|
||||
21.0704763
|
||||
],
|
||||
[
|
||||
105.646589,
|
||||
21.0700493
|
||||
],
|
||||
[
|
||||
105.648172,
|
||||
21.0688803
|
||||
],
|
||||
[
|
||||
105.648913,
|
||||
21.0684903
|
||||
],
|
||||
[
|
||||
105.649902,
|
||||
21.0682543
|
||||
],
|
||||
[
|
||||
105.651964,
|
||||
21.0681883
|
||||
],
|
||||
[
|
||||
105.652708,
|
||||
21.0684563
|
||||
],
|
||||
[
|
||||
105.656842,
|
||||
21.0704743
|
||||
],
|
||||
[
|
||||
105.657941,
|
||||
21.0717683
|
||||
],
|
||||
[
|
||||
105.658479,
|
||||
21.0720763
|
||||
],
|
||||
[
|
||||
105.6591826,
|
||||
21.0721043
|
||||
],
|
||||
[
|
||||
105.6595379,
|
||||
21.071964
|
||||
],
|
||||
[
|
||||
105.660167,
|
||||
21.0713533
|
||||
],
|
||||
[
|
||||
105.661558,
|
||||
21.0687133
|
||||
],
|
||||
[
|
||||
105.6636599,
|
||||
21.0661423
|
||||
],
|
||||
[
|
||||
105.6655423,
|
||||
21.0666612
|
||||
],
|
||||
[
|
||||
105.6667126,
|
||||
21.0675512
|
||||
],
|
||||
[
|
||||
105.6660082,
|
||||
21.0683503
|
||||
],
|
||||
[
|
||||
105.6656136,
|
||||
21.0679953
|
||||
],
|
||||
[
|
||||
105.6646736,
|
||||
21.0679168
|
||||
],
|
||||
[
|
||||
105.6638742,
|
||||
21.0680984
|
||||
],
|
||||
[
|
||||
105.6637555,
|
||||
21.0706619
|
||||
],
|
||||
[
|
||||
105.6667732,
|
||||
21.0726336
|
||||
],
|
||||
[
|
||||
105.6670585,
|
||||
21.0724021
|
||||
],
|
||||
[
|
||||
105.6687745,
|
||||
21.0718207
|
||||
],
|
||||
[
|
||||
105.6698921,
|
||||
21.0704552
|
||||
],
|
||||
[
|
||||
105.6704125,
|
||||
21.0700465
|
||||
],
|
||||
[
|
||||
105.6709285,
|
||||
21.0703379
|
||||
],
|
||||
[
|
||||
105.6714671,
|
||||
21.0704021
|
||||
],
|
||||
[
|
||||
105.6723339,
|
||||
21.0702111
|
||||
],
|
||||
[
|
||||
105.6725343,
|
||||
21.0703123
|
||||
],
|
||||
[
|
||||
105.6718014,
|
||||
21.0716138
|
||||
],
|
||||
[
|
||||
105.6731037,
|
||||
21.0723544
|
||||
],
|
||||
[
|
||||
105.6730113,
|
||||
21.0725328
|
||||
],
|
||||
[
|
||||
105.6760818,
|
||||
21.073839
|
||||
],
|
||||
[
|
||||
105.6748646,
|
||||
21.0755127
|
||||
],
|
||||
[
|
||||
105.6759457,
|
||||
21.0762558
|
||||
],
|
||||
[
|
||||
105.6755436,
|
||||
21.0766896
|
||||
],
|
||||
[
|
||||
105.6795661,
|
||||
21.0810304
|
||||
],
|
||||
[
|
||||
105.6774992,
|
||||
21.0815813
|
||||
],
|
||||
[
|
||||
105.6776299,
|
||||
21.0820597
|
||||
],
|
||||
[
|
||||
105.6772528,
|
||||
21.0828747
|
||||
],
|
||||
[
|
||||
105.6774194,
|
||||
21.0829666
|
||||
],
|
||||
[
|
||||
105.6774119,
|
||||
21.0834286
|
||||
],
|
||||
[
|
||||
105.6770984,
|
||||
21.0835894
|
||||
],
|
||||
[
|
||||
105.6773275,
|
||||
21.0843117
|
||||
],
|
||||
[
|
||||
105.6787659,
|
||||
21.0849019
|
||||
],
|
||||
[
|
||||
105.6791144,
|
||||
21.0849433
|
||||
],
|
||||
[
|
||||
105.6799874,
|
||||
21.0846818
|
||||
],
|
||||
[
|
||||
105.680754,
|
||||
21.0853376
|
||||
],
|
||||
[
|
||||
105.6810443,
|
||||
21.0853318
|
||||
],
|
||||
[
|
||||
105.681772,
|
||||
21.0858425
|
||||
],
|
||||
[
|
||||
105.6832408,
|
||||
21.0871174
|
||||
],
|
||||
[
|
||||
105.6838481,
|
||||
21.0873435
|
||||
],
|
||||
[
|
||||
105.6837754,
|
||||
21.0875349
|
||||
],
|
||||
[
|
||||
105.6850631,
|
||||
21.0878272
|
||||
],
|
||||
[
|
||||
105.6853127,
|
||||
21.0885772
|
||||
],
|
||||
[
|
||||
105.6857238,
|
||||
21.089108
|
||||
],
|
||||
[
|
||||
105.6857241,
|
||||
21.0894216
|
||||
],
|
||||
[
|
||||
105.6868217,
|
||||
21.090284
|
||||
],
|
||||
[
|
||||
105.6868197,
|
||||
21.0898583
|
||||
],
|
||||
[
|
||||
105.6872821,
|
||||
21.0894921
|
||||
],
|
||||
[
|
||||
105.6874715,
|
||||
21.0895577
|
||||
],
|
||||
[
|
||||
105.6881985,
|
||||
21.0892207
|
||||
],
|
||||
[
|
||||
105.6883117,
|
||||
21.0887667
|
||||
],
|
||||
[
|
||||
105.6886311,
|
||||
21.0888257
|
||||
],
|
||||
[
|
||||
105.6887389,
|
||||
21.0884804
|
||||
],
|
||||
[
|
||||
105.6900243,
|
||||
21.0885836
|
||||
],
|
||||
[
|
||||
105.6901243,
|
||||
21.0883206
|
||||
],
|
||||
[
|
||||
105.6907182,
|
||||
21.0882351
|
||||
],
|
||||
[
|
||||
105.6955784,
|
||||
21.0863373
|
||||
],
|
||||
[
|
||||
105.6968242,
|
||||
21.0867724
|
||||
],
|
||||
[
|
||||
105.6976076,
|
||||
21.084954
|
||||
],
|
||||
[
|
||||
105.6964687,
|
||||
21.0849105
|
||||
],
|
||||
[
|
||||
105.6964734,
|
||||
21.0834671
|
||||
],
|
||||
[
|
||||
105.6982362,
|
||||
21.0835771
|
||||
],
|
||||
[
|
||||
105.6980501,
|
||||
21.0825966
|
||||
],
|
||||
[
|
||||
105.7014809,
|
||||
21.0808355
|
||||
],
|
||||
[
|
||||
105.7011983,
|
||||
21.0800199
|
||||
],
|
||||
[
|
||||
105.7014318,
|
||||
21.0797668
|
||||
],
|
||||
[
|
||||
105.7005633,
|
||||
21.07777
|
||||
],
|
||||
[
|
||||
105.6999267,
|
||||
21.0773198
|
||||
],
|
||||
[
|
||||
105.6997612,
|
||||
21.0765248
|
||||
],
|
||||
[
|
||||
105.6988449,
|
||||
21.0750055
|
||||
],
|
||||
[
|
||||
105.7048673,
|
||||
21.0714623
|
||||
],
|
||||
[
|
||||
105.7053572,
|
||||
21.0722031
|
||||
],
|
||||
[
|
||||
105.706726,
|
||||
21.0718596
|
||||
],
|
||||
[
|
||||
105.7071262,
|
||||
21.0720236
|
||||
],
|
||||
[
|
||||
105.7071802,
|
||||
21.0718758
|
||||
],
|
||||
[
|
||||
105.7076695,
|
||||
21.0719715
|
||||
],
|
||||
[
|
||||
105.7080063,
|
||||
21.0715577
|
||||
],
|
||||
[
|
||||
105.7079076,
|
||||
21.0712771
|
||||
],
|
||||
[
|
||||
105.7082411,
|
||||
21.071109
|
||||
],
|
||||
[
|
||||
105.7091892,
|
||||
21.0740592
|
||||
],
|
||||
[
|
||||
105.7159534,
|
||||
21.0722462
|
||||
],
|
||||
[
|
||||
105.7159004,
|
||||
21.0715435
|
||||
],
|
||||
[
|
||||
105.7172148,
|
||||
21.0712355
|
||||
],
|
||||
[
|
||||
105.717893,
|
||||
21.0724994
|
||||
],
|
||||
[
|
||||
105.7176567,
|
||||
21.0732149
|
||||
],
|
||||
[
|
||||
105.7182243,
|
||||
21.0734808
|
||||
],
|
||||
[
|
||||
105.7179651,
|
||||
21.073752
|
||||
],
|
||||
[
|
||||
105.7188904,
|
||||
21.074765
|
||||
],
|
||||
[
|
||||
105.7195249,
|
||||
21.0728198
|
||||
],
|
||||
[
|
||||
105.7205713,
|
||||
21.0721978
|
||||
],
|
||||
[
|
||||
105.7206415,
|
||||
21.0724823
|
||||
],
|
||||
[
|
||||
105.7204228,
|
||||
21.072575
|
||||
],
|
||||
[
|
||||
105.7205562,
|
||||
21.0729387
|
||||
],
|
||||
[
|
||||
105.7210955,
|
||||
21.0731548
|
||||
],
|
||||
[
|
||||
105.7213751,
|
||||
21.0738984
|
||||
],
|
||||
[
|
||||
105.7214755,
|
||||
21.0738322
|
||||
],
|
||||
[
|
||||
105.7226259,
|
||||
21.0744534
|
||||
],
|
||||
[
|
||||
105.722399,
|
||||
21.0747635
|
||||
],
|
||||
[
|
||||
105.7229578,
|
||||
21.0747123
|
||||
],
|
||||
[
|
||||
105.7241279,
|
||||
21.075079
|
||||
],
|
||||
[
|
||||
105.7238569,
|
||||
21.0756562
|
||||
],
|
||||
[
|
||||
105.7239456,
|
||||
21.0759283
|
||||
],
|
||||
[
|
||||
105.7242036,
|
||||
21.0760387
|
||||
],
|
||||
[
|
||||
105.7240961,
|
||||
21.0767373
|
||||
],
|
||||
[
|
||||
105.7242662,
|
||||
21.0769439
|
||||
],
|
||||
[
|
||||
105.7243497,
|
||||
21.0775992
|
||||
],
|
||||
[
|
||||
105.7252621,
|
||||
21.0779078
|
||||
],
|
||||
[
|
||||
105.7252699,
|
||||
21.0784772
|
||||
],
|
||||
[
|
||||
105.7255537,
|
||||
21.0785361
|
||||
],
|
||||
[
|
||||
105.7255006,
|
||||
21.0789379
|
||||
],
|
||||
[
|
||||
105.7258054,
|
||||
21.0791807
|
||||
],
|
||||
[
|
||||
105.7256631,
|
||||
21.0797413
|
||||
],
|
||||
[
|
||||
105.7261434,
|
||||
21.0797391
|
||||
],
|
||||
[
|
||||
105.7261638,
|
||||
21.0799543
|
||||
],
|
||||
[
|
||||
105.7263841,
|
||||
21.0799384
|
||||
],
|
||||
[
|
||||
105.7270607,
|
||||
21.0812317
|
||||
],
|
||||
[
|
||||
105.727371,
|
||||
21.0815494
|
||||
],
|
||||
[
|
||||
105.7277116,
|
||||
21.0814124
|
||||
],
|
||||
[
|
||||
105.7278099,
|
||||
21.081731
|
||||
],
|
||||
[
|
||||
105.727674,
|
||||
21.0830562
|
||||
],
|
||||
[
|
||||
105.7279997,
|
||||
21.083015
|
||||
],
|
||||
[
|
||||
105.7279064,
|
||||
21.0844887
|
||||
],
|
||||
[
|
||||
105.7266735,
|
||||
21.085195
|
||||
],
|
||||
[
|
||||
105.726599,
|
||||
21.0853843
|
||||
],
|
||||
[
|
||||
105.7269128,
|
||||
21.0869468
|
||||
],
|
||||
[
|
||||
105.7267844,
|
||||
21.08725
|
||||
],
|
||||
[
|
||||
105.7269863,
|
||||
21.0875725
|
||||
],
|
||||
[
|
||||
105.7269955,
|
||||
21.0884496
|
||||
],
|
||||
[
|
||||
105.726252,
|
||||
21.0884495
|
||||
],
|
||||
[
|
||||
105.7261578,
|
||||
21.0891501
|
||||
],
|
||||
[
|
||||
105.7265122,
|
||||
21.0919147
|
||||
],
|
||||
[
|
||||
105.7262942,
|
||||
21.0932899
|
||||
],
|
||||
[
|
||||
105.7261293,
|
||||
21.0935348
|
||||
],
|
||||
[
|
||||
105.7269542,
|
||||
21.0939562
|
||||
],
|
||||
[
|
||||
105.7259417,
|
||||
21.095795
|
||||
],
|
||||
[
|
||||
105.7247853,
|
||||
21.0969532
|
||||
],
|
||||
[
|
||||
105.7252674,
|
||||
21.096873
|
||||
],
|
||||
[
|
||||
105.7262363,
|
||||
21.0991244
|
||||
],
|
||||
[
|
||||
105.7274975,
|
||||
21.0988334
|
||||
],
|
||||
[
|
||||
105.7286733,
|
||||
21.0998273
|
||||
],
|
||||
[
|
||||
105.7289175,
|
||||
21.0996337
|
||||
],
|
||||
[
|
||||
105.7294277,
|
||||
21.0995887
|
||||
],
|
||||
[
|
||||
105.7298323,
|
||||
21.1009218
|
||||
],
|
||||
[
|
||||
105.7310919,
|
||||
21.100615
|
||||
],
|
||||
[
|
||||
105.7311677,
|
||||
21.101054
|
||||
],
|
||||
[
|
||||
105.731791,
|
||||
21.1012382
|
||||
],
|
||||
[
|
||||
105.7318037,
|
||||
21.1013992
|
||||
],
|
||||
[
|
||||
105.7335099,
|
||||
21.1022258
|
||||
],
|
||||
[
|
||||
105.740566,
|
||||
21.1081244
|
||||
],
|
||||
[
|
||||
105.738269,
|
||||
21.1096484
|
||||
],
|
||||
[
|
||||
105.735302,
|
||||
21.1131721
|
||||
],
|
||||
[
|
||||
105.7363918,
|
||||
21.113523
|
||||
],
|
||||
[
|
||||
105.7321391,
|
||||
21.1218232
|
||||
],
|
||||
[
|
||||
105.7304145,
|
||||
21.1263186
|
||||
],
|
||||
[
|
||||
105.7286309,
|
||||
21.127151
|
||||
],
|
||||
[
|
||||
105.7279578,
|
||||
21.1276248
|
||||
],
|
||||
[
|
||||
105.7269119,
|
||||
21.1296485
|
||||
],
|
||||
[
|
||||
105.7244352,
|
||||
21.13199
|
||||
],
|
||||
[
|
||||
105.7249459,
|
||||
21.1325338
|
||||
],
|
||||
[
|
||||
105.7239945,
|
||||
21.1330415
|
||||
],
|
||||
[
|
||||
105.7238063,
|
||||
21.1327841
|
||||
],
|
||||
[
|
||||
105.7195116,
|
||||
21.1341786
|
||||
],
|
||||
[
|
||||
105.7192349,
|
||||
21.1321075
|
||||
],
|
||||
[
|
||||
105.71642,
|
||||
21.133357
|
||||
],
|
||||
[
|
||||
105.711291,
|
||||
21.136382
|
||||
],
|
||||
[
|
||||
105.706103,
|
||||
21.139114
|
||||
],
|
||||
[
|
||||
105.701448,
|
||||
21.141234
|
||||
],
|
||||
[
|
||||
105.694598,
|
||||
21.143035
|
||||
],
|
||||
[
|
||||
105.685496,
|
||||
21.146238
|
||||
],
|
||||
[
|
||||
105.675185,
|
||||
21.147611
|
||||
],
|
||||
[
|
||||
105.6731391,
|
||||
21.1476627
|
||||
],
|
||||
[
|
||||
105.667178,
|
||||
21.148283
|
||||
],
|
||||
[
|
||||
105.661881,
|
||||
21.148384
|
||||
],
|
||||
[
|
||||
105.657907,
|
||||
21.150081
|
||||
],
|
||||
[
|
||||
105.6554277,
|
||||
21.1522683
|
||||
],
|
||||
[
|
||||
105.6368172,
|
||||
21.1580849
|
||||
],
|
||||
[
|
||||
105.633042,
|
||||
21.1686774
|
||||
],
|
||||
[
|
||||
105.6310592,
|
||||
21.1703849
|
||||
],
|
||||
[
|
||||
105.6263194,
|
||||
21.1733562
|
||||
],
|
||||
[
|
||||
105.6276567,
|
||||
21.1496248
|
||||
],
|
||||
[
|
||||
105.6277266,
|
||||
21.1458104
|
||||
],
|
||||
[
|
||||
105.6278687,
|
||||
21.145583
|
||||
],
|
||||
[
|
||||
105.6278458,
|
||||
21.1439434
|
||||
],
|
||||
[
|
||||
105.6276399,
|
||||
21.1435062
|
||||
],
|
||||
[
|
||||
105.6264573,
|
||||
21.1425442
|
||||
],
|
||||
[
|
||||
105.626241,
|
||||
21.1421571
|
||||
],
|
||||
[
|
||||
105.6234945,
|
||||
21.1360542
|
||||
],
|
||||
[
|
||||
105.6232142,
|
||||
21.1351267
|
||||
]
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,482 @@
|
||||
{
|
||||
"type": "Feature",
|
||||
"properties": {
|
||||
"code": "HN-DONGDA",
|
||||
"name": "Dong Da",
|
||||
"level": "district",
|
||||
"parent": "HN",
|
||||
"basis": "pre-2025-merger extent, from OSM boundary/historic relations",
|
||||
"areaKm2": 9.9,
|
||||
"bbox": [
|
||||
105.79763,
|
||||
20.99742,
|
||||
105.84263,
|
||||
21.03215
|
||||
],
|
||||
"center": [
|
||||
21.01547,
|
||||
105.82503
|
||||
],
|
||||
"generatedAt": "2026-08-30T07:33:09.725Z",
|
||||
"osm": "relation/9421135"
|
||||
},
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [
|
||||
[
|
||||
[
|
||||
105.79763,
|
||||
21.0246548
|
||||
],
|
||||
[
|
||||
105.7980183,
|
||||
21.0237715
|
||||
],
|
||||
[
|
||||
105.8001477,
|
||||
21.0219911
|
||||
],
|
||||
[
|
||||
105.8012422,
|
||||
21.0203624
|
||||
],
|
||||
[
|
||||
105.8030396,
|
||||
21.0171604
|
||||
],
|
||||
[
|
||||
105.8041936,
|
||||
21.0157182
|
||||
],
|
||||
[
|
||||
105.8076252,
|
||||
21.012689
|
||||
],
|
||||
[
|
||||
105.8138247,
|
||||
21.0086444
|
||||
],
|
||||
[
|
||||
105.8149192,
|
||||
21.007683
|
||||
],
|
||||
[
|
||||
105.8166057,
|
||||
21.0051511
|
||||
],
|
||||
[
|
||||
105.8181305,
|
||||
21.001585
|
||||
],
|
||||
[
|
||||
105.8202057,
|
||||
21.003109
|
||||
],
|
||||
[
|
||||
105.8267548,
|
||||
21.0014485
|
||||
],
|
||||
[
|
||||
105.8329565,
|
||||
20.9996053
|
||||
],
|
||||
[
|
||||
105.8369412,
|
||||
20.9988668
|
||||
],
|
||||
[
|
||||
105.8426251,
|
||||
20.997416
|
||||
],
|
||||
[
|
||||
105.8413071,
|
||||
21.0004611
|
||||
],
|
||||
[
|
||||
105.8413457,
|
||||
21.0162192
|
||||
],
|
||||
[
|
||||
105.8410033,
|
||||
21.0174836
|
||||
],
|
||||
[
|
||||
105.8413359,
|
||||
21.0175282
|
||||
],
|
||||
[
|
||||
105.841352,
|
||||
21.0188286
|
||||
],
|
||||
[
|
||||
105.8411571,
|
||||
21.0191634
|
||||
],
|
||||
[
|
||||
105.8409006,
|
||||
21.0192317
|
||||
],
|
||||
[
|
||||
105.8409566,
|
||||
21.0228612
|
||||
],
|
||||
[
|
||||
105.8410133,
|
||||
21.02333
|
||||
],
|
||||
[
|
||||
105.8412059,
|
||||
21.0233319
|
||||
],
|
||||
[
|
||||
105.8412317,
|
||||
21.0247813
|
||||
],
|
||||
[
|
||||
105.841053,
|
||||
21.0247821
|
||||
],
|
||||
[
|
||||
105.8410075,
|
||||
21.0268439
|
||||
],
|
||||
[
|
||||
105.8410999,
|
||||
21.027382
|
||||
],
|
||||
[
|
||||
105.8415518,
|
||||
21.0276316
|
||||
],
|
||||
[
|
||||
105.8415672,
|
||||
21.0287502
|
||||
],
|
||||
[
|
||||
105.8412352,
|
||||
21.0287727
|
||||
],
|
||||
[
|
||||
105.8410927,
|
||||
21.0283566
|
||||
],
|
||||
[
|
||||
105.8407663,
|
||||
21.0284898
|
||||
],
|
||||
[
|
||||
105.8408062,
|
||||
21.0286922
|
||||
],
|
||||
[
|
||||
105.8406182,
|
||||
21.028701
|
||||
],
|
||||
[
|
||||
105.8406544,
|
||||
21.0287987
|
||||
],
|
||||
[
|
||||
105.8398793,
|
||||
21.0289422
|
||||
],
|
||||
[
|
||||
105.8397698,
|
||||
21.0284323
|
||||
],
|
||||
[
|
||||
105.8394059,
|
||||
21.0285095
|
||||
],
|
||||
[
|
||||
105.8391332,
|
||||
21.0286196
|
||||
],
|
||||
[
|
||||
105.8391988,
|
||||
21.0288902
|
||||
],
|
||||
[
|
||||
105.8389332,
|
||||
21.0289616
|
||||
],
|
||||
[
|
||||
105.8391429,
|
||||
21.0295599
|
||||
],
|
||||
[
|
||||
105.8384777,
|
||||
21.0297506
|
||||
],
|
||||
[
|
||||
105.8383293,
|
||||
21.0294864
|
||||
],
|
||||
[
|
||||
105.8378684,
|
||||
21.0296354
|
||||
],
|
||||
[
|
||||
105.8375757,
|
||||
21.0295642
|
||||
],
|
||||
[
|
||||
105.836893,
|
||||
21.0299308
|
||||
],
|
||||
[
|
||||
105.8369865,
|
||||
21.0301779
|
||||
],
|
||||
[
|
||||
105.8357925,
|
||||
21.0305184
|
||||
],
|
||||
[
|
||||
105.8349492,
|
||||
21.0297952
|
||||
],
|
||||
[
|
||||
105.8347106,
|
||||
21.0298926
|
||||
],
|
||||
[
|
||||
105.83403,
|
||||
21.0310278
|
||||
],
|
||||
[
|
||||
105.8338136,
|
||||
21.0310934
|
||||
],
|
||||
[
|
||||
105.8335628,
|
||||
21.0305588
|
||||
],
|
||||
[
|
||||
105.8331747,
|
||||
21.0306737
|
||||
],
|
||||
[
|
||||
105.8332284,
|
||||
21.0308417
|
||||
],
|
||||
[
|
||||
105.8328713,
|
||||
21.0310019
|
||||
],
|
||||
[
|
||||
105.8329906,
|
||||
21.0313288
|
||||
],
|
||||
[
|
||||
105.8321158,
|
||||
21.0315737
|
||||
],
|
||||
[
|
||||
105.8320219,
|
||||
21.0312598
|
||||
],
|
||||
[
|
||||
105.8316065,
|
||||
21.0312824
|
||||
],
|
||||
[
|
||||
105.8315192,
|
||||
21.0307155
|
||||
],
|
||||
[
|
||||
105.8318224,
|
||||
21.0306412
|
||||
],
|
||||
[
|
||||
105.8317068,
|
||||
21.0301997
|
||||
],
|
||||
[
|
||||
105.8308857,
|
||||
21.0303829
|
||||
],
|
||||
[
|
||||
105.8307945,
|
||||
21.0300438
|
||||
],
|
||||
[
|
||||
105.8297502,
|
||||
21.0303218
|
||||
],
|
||||
[
|
||||
105.8302576,
|
||||
21.0320793
|
||||
],
|
||||
[
|
||||
105.8300006,
|
||||
21.0321494
|
||||
],
|
||||
[
|
||||
105.8298787,
|
||||
21.0317602
|
||||
],
|
||||
[
|
||||
105.8288463,
|
||||
21.0320518
|
||||
],
|
||||
[
|
||||
105.8286772,
|
||||
21.0315534
|
||||
],
|
||||
[
|
||||
105.8283936,
|
||||
21.031386
|
||||
],
|
||||
[
|
||||
105.8284603,
|
||||
21.0311379
|
||||
],
|
||||
[
|
||||
105.8275747,
|
||||
21.03032
|
||||
],
|
||||
[
|
||||
105.8272335,
|
||||
21.0295029
|
||||
],
|
||||
[
|
||||
105.8248194,
|
||||
21.0277351
|
||||
],
|
||||
[
|
||||
105.8197685,
|
||||
21.0234032
|
||||
],
|
||||
[
|
||||
105.8210299,
|
||||
21.0231258
|
||||
],
|
||||
[
|
||||
105.82065,
|
||||
21.0226782
|
||||
],
|
||||
[
|
||||
105.8196582,
|
||||
21.0205423
|
||||
],
|
||||
[
|
||||
105.8195228,
|
||||
21.0205859
|
||||
],
|
||||
[
|
||||
105.8192878,
|
||||
21.0201126
|
||||
],
|
||||
[
|
||||
105.8191122,
|
||||
21.0194952
|
||||
],
|
||||
[
|
||||
105.819316,
|
||||
21.0190316
|
||||
],
|
||||
[
|
||||
105.8183404,
|
||||
21.0184046
|
||||
],
|
||||
[
|
||||
105.8173608,
|
||||
21.0172825
|
||||
],
|
||||
[
|
||||
105.8165767,
|
||||
21.0169109
|
||||
],
|
||||
[
|
||||
105.8158704,
|
||||
21.0159567
|
||||
],
|
||||
[
|
||||
105.8149171,
|
||||
21.0165734
|
||||
],
|
||||
[
|
||||
105.8105849,
|
||||
21.018711
|
||||
],
|
||||
[
|
||||
105.8113367,
|
||||
21.020062
|
||||
],
|
||||
[
|
||||
105.8104027,
|
||||
21.020536
|
||||
],
|
||||
[
|
||||
105.8108709,
|
||||
21.021345
|
||||
],
|
||||
[
|
||||
105.8120498,
|
||||
21.0215589
|
||||
],
|
||||
[
|
||||
105.8115325,
|
||||
21.0246392
|
||||
],
|
||||
[
|
||||
105.8102855,
|
||||
21.0261951
|
||||
],
|
||||
[
|
||||
105.8096483,
|
||||
21.0252642
|
||||
],
|
||||
[
|
||||
105.8079102,
|
||||
21.0261342
|
||||
],
|
||||
[
|
||||
105.8077162,
|
||||
21.025925
|
||||
],
|
||||
[
|
||||
105.8069025,
|
||||
21.025964
|
||||
],
|
||||
[
|
||||
105.8068966,
|
||||
21.0261608
|
||||
],
|
||||
[
|
||||
105.8066266,
|
||||
21.0261623
|
||||
],
|
||||
[
|
||||
105.8067562,
|
||||
21.0275083
|
||||
],
|
||||
[
|
||||
105.8053147,
|
||||
21.0278839
|
||||
],
|
||||
[
|
||||
105.8050074,
|
||||
21.0282937
|
||||
],
|
||||
[
|
||||
105.8032273,
|
||||
21.0291131
|
||||
],
|
||||
[
|
||||
105.8013534,
|
||||
21.0302334
|
||||
],
|
||||
[
|
||||
105.7983286,
|
||||
21.0268031
|
||||
],
|
||||
[
|
||||
105.79763,
|
||||
21.0246548
|
||||
]
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,562 @@
|
||||
{
|
||||
"type": "Feature",
|
||||
"properties": {
|
||||
"code": "HN-HAIBATRUNG",
|
||||
"name": "Hai Ba Trung",
|
||||
"level": "district",
|
||||
"parent": "HN",
|
||||
"basis": "pre-2025-merger extent, from OSM boundary/historic relations",
|
||||
"areaKm2": 10.2,
|
||||
"bbox": [
|
||||
105.8409,
|
||||
20.98996,
|
||||
105.88241,
|
||||
21.02127
|
||||
],
|
||||
"center": [
|
||||
21.00564,
|
||||
105.8585
|
||||
],
|
||||
"generatedAt": "2026-08-30T07:33:11.569Z",
|
||||
"osm": "relation/9421134"
|
||||
},
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [
|
||||
[
|
||||
[
|
||||
105.8409006,
|
||||
21.0192317
|
||||
],
|
||||
[
|
||||
105.8411571,
|
||||
21.0191634
|
||||
],
|
||||
[
|
||||
105.841352,
|
||||
21.0188286
|
||||
],
|
||||
[
|
||||
105.8413359,
|
||||
21.0175282
|
||||
],
|
||||
[
|
||||
105.8410033,
|
||||
21.0174836
|
||||
],
|
||||
[
|
||||
105.8413457,
|
||||
21.0162192
|
||||
],
|
||||
[
|
||||
105.8413071,
|
||||
21.0004611
|
||||
],
|
||||
[
|
||||
105.8426824,
|
||||
20.997311
|
||||
],
|
||||
[
|
||||
105.8428695,
|
||||
20.9957401
|
||||
],
|
||||
[
|
||||
105.8425839,
|
||||
20.994849
|
||||
],
|
||||
[
|
||||
105.8414087,
|
||||
20.9929659
|
||||
],
|
||||
[
|
||||
105.8411886,
|
||||
20.9929628
|
||||
],
|
||||
[
|
||||
105.8411756,
|
||||
20.9919248
|
||||
],
|
||||
[
|
||||
105.8428248,
|
||||
20.9920654
|
||||
],
|
||||
[
|
||||
105.843141,
|
||||
20.9931785
|
||||
],
|
||||
[
|
||||
105.8443126,
|
||||
20.9940228
|
||||
],
|
||||
[
|
||||
105.8458192,
|
||||
20.9925998
|
||||
],
|
||||
[
|
||||
105.8456544,
|
||||
20.9922011
|
||||
],
|
||||
[
|
||||
105.8459961,
|
||||
20.9917533
|
||||
],
|
||||
[
|
||||
105.8467974,
|
||||
20.9911812
|
||||
],
|
||||
[
|
||||
105.8475677,
|
||||
20.9909222
|
||||
],
|
||||
[
|
||||
105.8479098,
|
||||
20.9905742
|
||||
],
|
||||
[
|
||||
105.8482817,
|
||||
20.9908963
|
||||
],
|
||||
[
|
||||
105.8484787,
|
||||
20.9904435
|
||||
],
|
||||
[
|
||||
105.8487434,
|
||||
20.9902447
|
||||
],
|
||||
[
|
||||
105.8489188,
|
||||
20.9903358
|
||||
],
|
||||
[
|
||||
105.849041,
|
||||
20.9901407
|
||||
],
|
||||
[
|
||||
105.8496863,
|
||||
20.990697
|
||||
],
|
||||
[
|
||||
105.8505542,
|
||||
20.9899563
|
||||
],
|
||||
[
|
||||
105.8511636,
|
||||
20.9901261
|
||||
],
|
||||
[
|
||||
105.8510641,
|
||||
20.9905631
|
||||
],
|
||||
[
|
||||
105.8515647,
|
||||
20.9908538
|
||||
],
|
||||
[
|
||||
105.8514088,
|
||||
20.9912889
|
||||
],
|
||||
[
|
||||
105.8514058,
|
||||
20.9923667
|
||||
],
|
||||
[
|
||||
105.8505306,
|
||||
20.9923501
|
||||
],
|
||||
[
|
||||
105.8506917,
|
||||
20.9926461
|
||||
],
|
||||
[
|
||||
105.8504896,
|
||||
20.993027
|
||||
],
|
||||
[
|
||||
105.851882,
|
||||
20.9942179
|
||||
],
|
||||
[
|
||||
105.852036,
|
||||
20.9942139
|
||||
],
|
||||
[
|
||||
105.851999,
|
||||
20.9940662
|
||||
],
|
||||
[
|
||||
105.8536312,
|
||||
20.9937274
|
||||
],
|
||||
[
|
||||
105.8541609,
|
||||
20.9940503
|
||||
],
|
||||
[
|
||||
105.85431,
|
||||
20.9938819
|
||||
],
|
||||
[
|
||||
105.854684,
|
||||
20.9941274
|
||||
],
|
||||
[
|
||||
105.8548683,
|
||||
20.9938775
|
||||
],
|
||||
[
|
||||
105.8550186,
|
||||
20.9940436
|
||||
],
|
||||
[
|
||||
105.8551597,
|
||||
20.9938418
|
||||
],
|
||||
[
|
||||
105.8555695,
|
||||
20.9941406
|
||||
],
|
||||
[
|
||||
105.8557034,
|
||||
20.9939455
|
||||
],
|
||||
[
|
||||
105.8560404,
|
||||
20.9941804
|
||||
],
|
||||
[
|
||||
105.8566845,
|
||||
20.9935897
|
||||
],
|
||||
[
|
||||
105.8568523,
|
||||
20.9932301
|
||||
],
|
||||
[
|
||||
105.8579469,
|
||||
20.9932692
|
||||
],
|
||||
[
|
||||
105.8581562,
|
||||
20.9935798
|
||||
],
|
||||
[
|
||||
105.8579425,
|
||||
20.993756
|
||||
],
|
||||
[
|
||||
105.8585435,
|
||||
20.994838
|
||||
],
|
||||
[
|
||||
105.8593716,
|
||||
20.9947641
|
||||
],
|
||||
[
|
||||
105.8594805,
|
||||
20.9950567
|
||||
],
|
||||
[
|
||||
105.8608939,
|
||||
20.9954426
|
||||
],
|
||||
[
|
||||
105.8614024,
|
||||
20.9947667
|
||||
],
|
||||
[
|
||||
105.8615169,
|
||||
20.9948506
|
||||
],
|
||||
[
|
||||
105.861818,
|
||||
20.9945149
|
||||
],
|
||||
[
|
||||
105.8616064,
|
||||
20.9942574
|
||||
],
|
||||
[
|
||||
105.8608399,
|
||||
20.9939673
|
||||
],
|
||||
[
|
||||
105.8611964,
|
||||
20.9927201
|
||||
],
|
||||
[
|
||||
105.8626307,
|
||||
20.9928521
|
||||
],
|
||||
[
|
||||
105.8622775,
|
||||
20.9958169
|
||||
],
|
||||
[
|
||||
105.8629987,
|
||||
20.9959648
|
||||
],
|
||||
[
|
||||
105.8630106,
|
||||
20.9957128
|
||||
],
|
||||
[
|
||||
105.8636336,
|
||||
20.9956965
|
||||
],
|
||||
[
|
||||
105.8636403,
|
||||
20.9946166
|
||||
],
|
||||
[
|
||||
105.8659151,
|
||||
20.9946297
|
||||
],
|
||||
[
|
||||
105.8659215,
|
||||
20.9930825
|
||||
],
|
||||
[
|
||||
105.8709818,
|
||||
20.9931021
|
||||
],
|
||||
[
|
||||
105.8716353,
|
||||
20.9929267
|
||||
],
|
||||
[
|
||||
105.873143,
|
||||
20.9928553
|
||||
],
|
||||
[
|
||||
105.8735265,
|
||||
20.9926631
|
||||
],
|
||||
[
|
||||
105.8737572,
|
||||
20.9929762
|
||||
],
|
||||
[
|
||||
105.8740158,
|
||||
20.9943008
|
||||
],
|
||||
[
|
||||
105.8759682,
|
||||
20.994278
|
||||
],
|
||||
[
|
||||
105.8758187,
|
||||
20.9970787
|
||||
],
|
||||
[
|
||||
105.8762911,
|
||||
20.9972551
|
||||
],
|
||||
[
|
||||
105.877997,
|
||||
20.9972956
|
||||
],
|
||||
[
|
||||
105.8780005,
|
||||
20.9989079
|
||||
],
|
||||
[
|
||||
105.8781821,
|
||||
20.9989131
|
||||
],
|
||||
[
|
||||
105.8782384,
|
||||
21.0001483
|
||||
],
|
||||
[
|
||||
105.8768964,
|
||||
21.0007996
|
||||
],
|
||||
[
|
||||
105.8753194,
|
||||
21.001117
|
||||
],
|
||||
[
|
||||
105.8754403,
|
||||
21.0021742
|
||||
],
|
||||
[
|
||||
105.8765913,
|
||||
21.0021076
|
||||
],
|
||||
[
|
||||
105.8794424,
|
||||
21.000662
|
||||
],
|
||||
[
|
||||
105.8817704,
|
||||
21.0012523
|
||||
],
|
||||
[
|
||||
105.8824116,
|
||||
21.0037588
|
||||
],
|
||||
[
|
||||
105.8814425,
|
||||
21.0041734
|
||||
],
|
||||
[
|
||||
105.8786959,
|
||||
21.006401
|
||||
],
|
||||
[
|
||||
105.8731684,
|
||||
21.011577
|
||||
],
|
||||
[
|
||||
105.8680358,
|
||||
21.0179708
|
||||
],
|
||||
[
|
||||
105.8672823,
|
||||
21.020032
|
||||
],
|
||||
[
|
||||
105.8612895,
|
||||
21.0183922
|
||||
],
|
||||
[
|
||||
105.8587169,
|
||||
21.0190341
|
||||
],
|
||||
[
|
||||
105.8566804,
|
||||
21.0186808
|
||||
],
|
||||
[
|
||||
105.8567313,
|
||||
21.0188421
|
||||
],
|
||||
[
|
||||
105.8560218,
|
||||
21.0189765
|
||||
],
|
||||
[
|
||||
105.8555275,
|
||||
21.0187658
|
||||
],
|
||||
[
|
||||
105.8555972,
|
||||
21.0184582
|
||||
],
|
||||
[
|
||||
105.8546249,
|
||||
21.0181302
|
||||
],
|
||||
[
|
||||
105.8534767,
|
||||
21.0184226
|
||||
],
|
||||
[
|
||||
105.853418,
|
||||
21.0183008
|
||||
],
|
||||
[
|
||||
105.8526657,
|
||||
21.0183963
|
||||
],
|
||||
[
|
||||
105.8527107,
|
||||
21.0186607
|
||||
],
|
||||
[
|
||||
105.8522116,
|
||||
21.0188035
|
||||
],
|
||||
[
|
||||
105.8519903,
|
||||
21.0182123
|
||||
],
|
||||
[
|
||||
105.8508894,
|
||||
21.0184278
|
||||
],
|
||||
[
|
||||
105.8510876,
|
||||
21.0191321
|
||||
],
|
||||
[
|
||||
105.8504023,
|
||||
21.019334
|
||||
],
|
||||
[
|
||||
105.8503176,
|
||||
21.0191076
|
||||
],
|
||||
[
|
||||
105.8498617,
|
||||
21.0190691
|
||||
],
|
||||
[
|
||||
105.8497252,
|
||||
21.0186569
|
||||
],
|
||||
[
|
||||
105.8488176,
|
||||
21.0188274
|
||||
],
|
||||
[
|
||||
105.8476581,
|
||||
21.0190908
|
||||
],
|
||||
[
|
||||
105.8477513,
|
||||
21.0193581
|
||||
],
|
||||
[
|
||||
105.8472204,
|
||||
21.019478
|
||||
],
|
||||
[
|
||||
105.8472985,
|
||||
21.0197226
|
||||
],
|
||||
[
|
||||
105.8469863,
|
||||
21.0198135
|
||||
],
|
||||
[
|
||||
105.846934,
|
||||
21.0196501
|
||||
],
|
||||
[
|
||||
105.8440974,
|
||||
21.0201524
|
||||
],
|
||||
[
|
||||
105.8441356,
|
||||
21.0202818
|
||||
],
|
||||
[
|
||||
105.8417808,
|
||||
21.021032
|
||||
],
|
||||
[
|
||||
105.8418589,
|
||||
21.0212625
|
||||
],
|
||||
[
|
||||
105.8411301,
|
||||
21.021271
|
||||
],
|
||||
[
|
||||
105.8411255,
|
||||
21.0210586
|
||||
],
|
||||
[
|
||||
105.8409287,
|
||||
21.0210536
|
||||
],
|
||||
[
|
||||
105.8409006,
|
||||
21.0192317
|
||||
]
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,858 @@
|
||||
{
|
||||
"type": "Feature",
|
||||
"properties": {
|
||||
"code": "HN-HOANGMAI",
|
||||
"name": "Hoang Mai",
|
||||
"level": "district",
|
||||
"parent": "HN",
|
||||
"basis": "pre-2025-merger extent, from OSM boundary/historic relations",
|
||||
"areaKm2": 40.1,
|
||||
"bbox": [
|
||||
105.80739,
|
||||
20.94544,
|
||||
105.90879,
|
||||
21.00376
|
||||
],
|
||||
"center": [
|
||||
20.97454,
|
||||
105.86322
|
||||
],
|
||||
"generatedAt": "2026-08-30T07:33:13.064Z",
|
||||
"osm": "relation/9421133"
|
||||
},
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [
|
||||
[
|
||||
[
|
||||
105.8073883,
|
||||
20.9739057
|
||||
],
|
||||
[
|
||||
105.8087599,
|
||||
20.973206
|
||||
],
|
||||
[
|
||||
105.8082688,
|
||||
20.9724803
|
||||
],
|
||||
[
|
||||
105.8131645,
|
||||
20.9696438
|
||||
],
|
||||
[
|
||||
105.8140956,
|
||||
20.9710772
|
||||
],
|
||||
[
|
||||
105.8152349,
|
||||
20.9705667
|
||||
],
|
||||
[
|
||||
105.8157566,
|
||||
20.9700722
|
||||
],
|
||||
[
|
||||
105.8166237,
|
||||
20.971319
|
||||
],
|
||||
[
|
||||
105.8180039,
|
||||
20.9711294
|
||||
],
|
||||
[
|
||||
105.8183579,
|
||||
20.9717445
|
||||
],
|
||||
[
|
||||
105.8196487,
|
||||
20.9723682
|
||||
],
|
||||
[
|
||||
105.8249893,
|
||||
20.9707413
|
||||
],
|
||||
[
|
||||
105.8237194,
|
||||
20.967736
|
||||
],
|
||||
[
|
||||
105.8219327,
|
||||
20.966127
|
||||
],
|
||||
[
|
||||
105.8192678,
|
||||
20.9642224
|
||||
],
|
||||
[
|
||||
105.8180924,
|
||||
20.963129
|
||||
],
|
||||
[
|
||||
105.8182594,
|
||||
20.9623872
|
||||
],
|
||||
[
|
||||
105.8185481,
|
||||
20.9624377
|
||||
],
|
||||
[
|
||||
105.8185567,
|
||||
20.9611756
|
||||
],
|
||||
[
|
||||
105.8166564,
|
||||
20.9608991
|
||||
],
|
||||
[
|
||||
105.8163757,
|
||||
20.9599285
|
||||
],
|
||||
[
|
||||
105.817469,
|
||||
20.9597115
|
||||
],
|
||||
[
|
||||
105.8182167,
|
||||
20.959376
|
||||
],
|
||||
[
|
||||
105.8195475,
|
||||
20.9579177
|
||||
],
|
||||
[
|
||||
105.8203648,
|
||||
20.9573014
|
||||
],
|
||||
[
|
||||
105.8256989,
|
||||
20.956419
|
||||
],
|
||||
[
|
||||
105.8271392,
|
||||
20.9555681
|
||||
],
|
||||
[
|
||||
105.8289386,
|
||||
20.954084
|
||||
],
|
||||
[
|
||||
105.8307969,
|
||||
20.9530369
|
||||
],
|
||||
[
|
||||
105.8311049,
|
||||
20.9535796
|
||||
],
|
||||
[
|
||||
105.8314843,
|
||||
20.9534231
|
||||
],
|
||||
[
|
||||
105.8318021,
|
||||
20.9540825
|
||||
],
|
||||
[
|
||||
105.8310231,
|
||||
20.9543572
|
||||
],
|
||||
[
|
||||
105.8314232,
|
||||
20.9554382
|
||||
],
|
||||
[
|
||||
105.8337708,
|
||||
20.955162
|
||||
],
|
||||
[
|
||||
105.833992,
|
||||
20.9566307
|
||||
],
|
||||
[
|
||||
105.8347473,
|
||||
20.9567337
|
||||
],
|
||||
[
|
||||
105.8351364,
|
||||
20.9583798
|
||||
],
|
||||
[
|
||||
105.8358761,
|
||||
20.9583165
|
||||
],
|
||||
[
|
||||
105.8359567,
|
||||
20.9588966
|
||||
],
|
||||
[
|
||||
105.8362429,
|
||||
20.9589126
|
||||
],
|
||||
[
|
||||
105.8363521,
|
||||
20.9579625
|
||||
],
|
||||
[
|
||||
105.8381068,
|
||||
20.9581249
|
||||
],
|
||||
[
|
||||
105.8386025,
|
||||
20.9570166
|
||||
],
|
||||
[
|
||||
105.8390306,
|
||||
20.9574783
|
||||
],
|
||||
[
|
||||
105.8426819,
|
||||
20.9581852
|
||||
],
|
||||
[
|
||||
105.8441773,
|
||||
20.9511261
|
||||
],
|
||||
[
|
||||
105.8464429,
|
||||
20.9517253
|
||||
],
|
||||
[
|
||||
105.8499454,
|
||||
20.9529268
|
||||
],
|
||||
[
|
||||
105.852479,
|
||||
20.9541788
|
||||
],
|
||||
[
|
||||
105.8537395,
|
||||
20.9550156
|
||||
],
|
||||
[
|
||||
105.8584958,
|
||||
20.9573193
|
||||
],
|
||||
[
|
||||
105.8600388,
|
||||
20.9536839
|
||||
],
|
||||
[
|
||||
105.8637161,
|
||||
20.9513283
|
||||
],
|
||||
[
|
||||
105.866502,
|
||||
20.950883
|
||||
],
|
||||
[
|
||||
105.8668097,
|
||||
20.9516407
|
||||
],
|
||||
[
|
||||
105.8687627,
|
||||
20.950853
|
||||
],
|
||||
[
|
||||
105.872107,
|
||||
20.9559281
|
||||
],
|
||||
[
|
||||
105.8792114,
|
||||
20.951191
|
||||
],
|
||||
[
|
||||
105.8860035,
|
||||
20.9454443
|
||||
],
|
||||
[
|
||||
105.890152,
|
||||
20.9516888
|
||||
],
|
||||
[
|
||||
105.8975067,
|
||||
20.9601173
|
||||
],
|
||||
[
|
||||
105.9028778,
|
||||
20.9677639
|
||||
],
|
||||
[
|
||||
105.9078727,
|
||||
20.9778785
|
||||
],
|
||||
[
|
||||
105.9087898,
|
||||
20.9820266
|
||||
],
|
||||
[
|
||||
105.9086238,
|
||||
20.9846903
|
||||
],
|
||||
[
|
||||
105.9067548,
|
||||
20.9878294
|
||||
],
|
||||
[
|
||||
105.9035184,
|
||||
20.9915822
|
||||
],
|
||||
[
|
||||
105.9007397,
|
||||
20.9941093
|
||||
],
|
||||
[
|
||||
105.897819,
|
||||
20.9964489
|
||||
],
|
||||
[
|
||||
105.8943127,
|
||||
20.9988433
|
||||
],
|
||||
[
|
||||
105.8874507,
|
||||
21.001401
|
||||
],
|
||||
[
|
||||
105.8824116,
|
||||
21.0037588
|
||||
],
|
||||
[
|
||||
105.8817704,
|
||||
21.0012523
|
||||
],
|
||||
[
|
||||
105.8797133,
|
||||
21.0006589
|
||||
],
|
||||
[
|
||||
105.8794424,
|
||||
21.000662
|
||||
],
|
||||
[
|
||||
105.8765913,
|
||||
21.0021076
|
||||
],
|
||||
[
|
||||
105.8754403,
|
||||
21.0021742
|
||||
],
|
||||
[
|
||||
105.8753194,
|
||||
21.001117
|
||||
],
|
||||
[
|
||||
105.8768964,
|
||||
21.0007996
|
||||
],
|
||||
[
|
||||
105.8782384,
|
||||
21.0001483
|
||||
],
|
||||
[
|
||||
105.8781821,
|
||||
20.9989131
|
||||
],
|
||||
[
|
||||
105.8780005,
|
||||
20.9989079
|
||||
],
|
||||
[
|
||||
105.877997,
|
||||
20.9972956
|
||||
],
|
||||
[
|
||||
105.8762911,
|
||||
20.9972551
|
||||
],
|
||||
[
|
||||
105.8758187,
|
||||
20.9970787
|
||||
],
|
||||
[
|
||||
105.8759682,
|
||||
20.994278
|
||||
],
|
||||
[
|
||||
105.8740158,
|
||||
20.9943008
|
||||
],
|
||||
[
|
||||
105.8737572,
|
||||
20.9929762
|
||||
],
|
||||
[
|
||||
105.8735265,
|
||||
20.9926631
|
||||
],
|
||||
[
|
||||
105.873143,
|
||||
20.9928553
|
||||
],
|
||||
[
|
||||
105.8716353,
|
||||
20.9929267
|
||||
],
|
||||
[
|
||||
105.8709818,
|
||||
20.9931021
|
||||
],
|
||||
[
|
||||
105.8659215,
|
||||
20.9930825
|
||||
],
|
||||
[
|
||||
105.8659151,
|
||||
20.9946297
|
||||
],
|
||||
[
|
||||
105.8636403,
|
||||
20.9946166
|
||||
],
|
||||
[
|
||||
105.8636336,
|
||||
20.9956965
|
||||
],
|
||||
[
|
||||
105.8630106,
|
||||
20.9957128
|
||||
],
|
||||
[
|
||||
105.8629987,
|
||||
20.9959648
|
||||
],
|
||||
[
|
||||
105.8622775,
|
||||
20.9958169
|
||||
],
|
||||
[
|
||||
105.8626307,
|
||||
20.9928521
|
||||
],
|
||||
[
|
||||
105.8611964,
|
||||
20.9927201
|
||||
],
|
||||
[
|
||||
105.8608399,
|
||||
20.9939673
|
||||
],
|
||||
[
|
||||
105.8616064,
|
||||
20.9942574
|
||||
],
|
||||
[
|
||||
105.861818,
|
||||
20.9945149
|
||||
],
|
||||
[
|
||||
105.8615169,
|
||||
20.9948506
|
||||
],
|
||||
[
|
||||
105.8614024,
|
||||
20.9947667
|
||||
],
|
||||
[
|
||||
105.8608939,
|
||||
20.9954426
|
||||
],
|
||||
[
|
||||
105.8594805,
|
||||
20.9950567
|
||||
],
|
||||
[
|
||||
105.8593716,
|
||||
20.9947641
|
||||
],
|
||||
[
|
||||
105.8585435,
|
||||
20.994838
|
||||
],
|
||||
[
|
||||
105.8579425,
|
||||
20.993756
|
||||
],
|
||||
[
|
||||
105.8581562,
|
||||
20.9935798
|
||||
],
|
||||
[
|
||||
105.8579469,
|
||||
20.9932692
|
||||
],
|
||||
[
|
||||
105.8568523,
|
||||
20.9932301
|
||||
],
|
||||
[
|
||||
105.8566845,
|
||||
20.9935897
|
||||
],
|
||||
[
|
||||
105.8560404,
|
||||
20.9941804
|
||||
],
|
||||
[
|
||||
105.8557034,
|
||||
20.9939455
|
||||
],
|
||||
[
|
||||
105.8555695,
|
||||
20.9941406
|
||||
],
|
||||
[
|
||||
105.8551597,
|
||||
20.9938418
|
||||
],
|
||||
[
|
||||
105.8550186,
|
||||
20.9940436
|
||||
],
|
||||
[
|
||||
105.8548683,
|
||||
20.9938775
|
||||
],
|
||||
[
|
||||
105.854684,
|
||||
20.9941274
|
||||
],
|
||||
[
|
||||
105.85431,
|
||||
20.9938819
|
||||
],
|
||||
[
|
||||
105.8541609,
|
||||
20.9940503
|
||||
],
|
||||
[
|
||||
105.8536312,
|
||||
20.9937274
|
||||
],
|
||||
[
|
||||
105.851999,
|
||||
20.9940662
|
||||
],
|
||||
[
|
||||
105.852036,
|
||||
20.9942139
|
||||
],
|
||||
[
|
||||
105.851882,
|
||||
20.9942179
|
||||
],
|
||||
[
|
||||
105.8504896,
|
||||
20.993027
|
||||
],
|
||||
[
|
||||
105.8506917,
|
||||
20.9926461
|
||||
],
|
||||
[
|
||||
105.8505306,
|
||||
20.9923501
|
||||
],
|
||||
[
|
||||
105.8514058,
|
||||
20.9923667
|
||||
],
|
||||
[
|
||||
105.8514088,
|
||||
20.9912889
|
||||
],
|
||||
[
|
||||
105.8515647,
|
||||
20.9908538
|
||||
],
|
||||
[
|
||||
105.8510641,
|
||||
20.9905631
|
||||
],
|
||||
[
|
||||
105.8511636,
|
||||
20.9901261
|
||||
],
|
||||
[
|
||||
105.8505542,
|
||||
20.9899563
|
||||
],
|
||||
[
|
||||
105.8496863,
|
||||
20.990697
|
||||
],
|
||||
[
|
||||
105.849041,
|
||||
20.9901407
|
||||
],
|
||||
[
|
||||
105.8489188,
|
||||
20.9903358
|
||||
],
|
||||
[
|
||||
105.8487434,
|
||||
20.9902447
|
||||
],
|
||||
[
|
||||
105.8484787,
|
||||
20.9904435
|
||||
],
|
||||
[
|
||||
105.8482817,
|
||||
20.9908963
|
||||
],
|
||||
[
|
||||
105.8479098,
|
||||
20.9905742
|
||||
],
|
||||
[
|
||||
105.8475677,
|
||||
20.9909222
|
||||
],
|
||||
[
|
||||
105.8467974,
|
||||
20.9911812
|
||||
],
|
||||
[
|
||||
105.8459961,
|
||||
20.9917533
|
||||
],
|
||||
[
|
||||
105.8456544,
|
||||
20.9922011
|
||||
],
|
||||
[
|
||||
105.8458192,
|
||||
20.9925998
|
||||
],
|
||||
[
|
||||
105.8443126,
|
||||
20.9940228
|
||||
],
|
||||
[
|
||||
105.843141,
|
||||
20.9931785
|
||||
],
|
||||
[
|
||||
105.8428248,
|
||||
20.9920654
|
||||
],
|
||||
[
|
||||
105.8411756,
|
||||
20.9919248
|
||||
],
|
||||
[
|
||||
105.8410587,
|
||||
20.9854146
|
||||
],
|
||||
[
|
||||
105.8375177,
|
||||
20.9838812
|
||||
],
|
||||
[
|
||||
105.8365391,
|
||||
20.9857867
|
||||
],
|
||||
[
|
||||
105.8356537,
|
||||
20.9855105
|
||||
],
|
||||
[
|
||||
105.835289,
|
||||
20.9865866
|
||||
],
|
||||
[
|
||||
105.8349722,
|
||||
20.9867733
|
||||
],
|
||||
[
|
||||
105.8339923,
|
||||
20.9882282
|
||||
],
|
||||
[
|
||||
105.8338235,
|
||||
20.9880845
|
||||
],
|
||||
[
|
||||
105.8335351,
|
||||
20.9884566
|
||||
],
|
||||
[
|
||||
105.8337948,
|
||||
20.9886077
|
||||
],
|
||||
[
|
||||
105.8335481,
|
||||
20.9893916
|
||||
],
|
||||
[
|
||||
105.8332758,
|
||||
20.9891907
|
||||
],
|
||||
[
|
||||
105.8331242,
|
||||
20.9892538
|
||||
],
|
||||
[
|
||||
105.832593,
|
||||
20.9898237
|
||||
],
|
||||
[
|
||||
105.8324136,
|
||||
20.9903439
|
||||
],
|
||||
[
|
||||
105.8319262,
|
||||
20.990325
|
||||
],
|
||||
[
|
||||
105.8309168,
|
||||
20.9919633
|
||||
],
|
||||
[
|
||||
105.8305148,
|
||||
20.9932046
|
||||
],
|
||||
[
|
||||
105.8302384,
|
||||
20.993544
|
||||
],
|
||||
[
|
||||
105.8293818,
|
||||
20.9930265
|
||||
],
|
||||
[
|
||||
105.8284586,
|
||||
20.993581
|
||||
],
|
||||
[
|
||||
105.8281027,
|
||||
20.9930484
|
||||
],
|
||||
[
|
||||
105.827863,
|
||||
20.993133
|
||||
],
|
||||
[
|
||||
105.8275234,
|
||||
20.9926901
|
||||
],
|
||||
[
|
||||
105.8272994,
|
||||
20.9916723
|
||||
],
|
||||
[
|
||||
105.8270288,
|
||||
20.9914228
|
||||
],
|
||||
[
|
||||
105.8267682,
|
||||
20.9915716
|
||||
],
|
||||
[
|
||||
105.8263722,
|
||||
20.991313
|
||||
],
|
||||
[
|
||||
105.8244454,
|
||||
20.9892345
|
||||
],
|
||||
[
|
||||
105.8249429,
|
||||
20.988673
|
||||
],
|
||||
[
|
||||
105.8229859,
|
||||
20.9868674
|
||||
],
|
||||
[
|
||||
105.820343,
|
||||
20.9854372
|
||||
],
|
||||
[
|
||||
105.8204299,
|
||||
20.9852386
|
||||
],
|
||||
[
|
||||
105.8194547,
|
||||
20.9848938
|
||||
],
|
||||
[
|
||||
105.819547,
|
||||
20.9846233
|
||||
],
|
||||
[
|
||||
105.8186721,
|
||||
20.9843114
|
||||
],
|
||||
[
|
||||
105.8188331,
|
||||
20.9839253
|
||||
],
|
||||
[
|
||||
105.8187154,
|
||||
20.9830612
|
||||
],
|
||||
[
|
||||
105.8189901,
|
||||
20.9828929
|
||||
],
|
||||
[
|
||||
105.8189662,
|
||||
20.9824693
|
||||
],
|
||||
[
|
||||
105.8187176,
|
||||
20.9813471
|
||||
],
|
||||
[
|
||||
105.8183402,
|
||||
20.9807013
|
||||
],
|
||||
[
|
||||
105.8166696,
|
||||
20.9820833
|
||||
],
|
||||
[
|
||||
105.8156492,
|
||||
20.9832811
|
||||
],
|
||||
[
|
||||
105.8145841,
|
||||
20.9823579
|
||||
],
|
||||
[
|
||||
105.8134186,
|
||||
20.9805625
|
||||
],
|
||||
[
|
||||
105.8112612,
|
||||
20.9815285
|
||||
],
|
||||
[
|
||||
105.8108342,
|
||||
20.9812607
|
||||
],
|
||||
[
|
||||
105.8096224,
|
||||
20.9828803
|
||||
],
|
||||
[
|
||||
105.8092191,
|
||||
20.9817649
|
||||
],
|
||||
[
|
||||
105.8081011,
|
||||
20.9810101
|
||||
],
|
||||
[
|
||||
105.8097509,
|
||||
20.9792912
|
||||
],
|
||||
[
|
||||
105.8073883,
|
||||
20.9739057
|
||||
]
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,310 @@
|
||||
{
|
||||
"type": "Feature",
|
||||
"properties": {
|
||||
"code": "HN-HOANKIEM",
|
||||
"name": "Hoan Kiem",
|
||||
"level": "district",
|
||||
"parent": "HN",
|
||||
"basis": "pre-2025-merger extent, from OSM boundary/historic relations",
|
||||
"areaKm2": 5.3,
|
||||
"bbox": [
|
||||
105.84093,
|
||||
21.01813,
|
||||
105.86728,
|
||||
21.04321
|
||||
],
|
||||
"center": [
|
||||
21.02901,
|
||||
105.85378
|
||||
],
|
||||
"generatedAt": "2026-08-30T07:33:02.346Z",
|
||||
"osm": "relation/9421131"
|
||||
},
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [
|
||||
[
|
||||
[
|
||||
105.8409287,
|
||||
21.0210536
|
||||
],
|
||||
[
|
||||
105.8411255,
|
||||
21.0210586
|
||||
],
|
||||
[
|
||||
105.8411301,
|
||||
21.021271
|
||||
],
|
||||
[
|
||||
105.8418589,
|
||||
21.0212625
|
||||
],
|
||||
[
|
||||
105.8417808,
|
||||
21.021032
|
||||
],
|
||||
[
|
||||
105.8441356,
|
||||
21.0202818
|
||||
],
|
||||
[
|
||||
105.8440974,
|
||||
21.0201524
|
||||
],
|
||||
[
|
||||
105.846934,
|
||||
21.0196501
|
||||
],
|
||||
[
|
||||
105.8469863,
|
||||
21.0198135
|
||||
],
|
||||
[
|
||||
105.8472985,
|
||||
21.0197226
|
||||
],
|
||||
[
|
||||
105.8472204,
|
||||
21.019478
|
||||
],
|
||||
[
|
||||
105.8477513,
|
||||
21.0193581
|
||||
],
|
||||
[
|
||||
105.8476581,
|
||||
21.0190908
|
||||
],
|
||||
[
|
||||
105.8497252,
|
||||
21.0186569
|
||||
],
|
||||
[
|
||||
105.8498617,
|
||||
21.0190691
|
||||
],
|
||||
[
|
||||
105.8503176,
|
||||
21.0191076
|
||||
],
|
||||
[
|
||||
105.8504023,
|
||||
21.019334
|
||||
],
|
||||
[
|
||||
105.8510876,
|
||||
21.0191321
|
||||
],
|
||||
[
|
||||
105.8508894,
|
||||
21.0184278
|
||||
],
|
||||
[
|
||||
105.8519903,
|
||||
21.0182123
|
||||
],
|
||||
[
|
||||
105.8522116,
|
||||
21.0188035
|
||||
],
|
||||
[
|
||||
105.8527107,
|
||||
21.0186607
|
||||
],
|
||||
[
|
||||
105.8526657,
|
||||
21.0183963
|
||||
],
|
||||
[
|
||||
105.853418,
|
||||
21.0183008
|
||||
],
|
||||
[
|
||||
105.8534767,
|
||||
21.0184226
|
||||
],
|
||||
[
|
||||
105.8546249,
|
||||
21.0181302
|
||||
],
|
||||
[
|
||||
105.8555972,
|
||||
21.0184582
|
||||
],
|
||||
[
|
||||
105.8555275,
|
||||
21.0187658
|
||||
],
|
||||
[
|
||||
105.8560218,
|
||||
21.0189765
|
||||
],
|
||||
[
|
||||
105.8567313,
|
||||
21.0188421
|
||||
],
|
||||
[
|
||||
105.8566804,
|
||||
21.0186808
|
||||
],
|
||||
[
|
||||
105.8587169,
|
||||
21.0190341
|
||||
],
|
||||
[
|
||||
105.8612895,
|
||||
21.0183922
|
||||
],
|
||||
[
|
||||
105.8672823,
|
||||
21.020032
|
||||
],
|
||||
[
|
||||
105.8653775,
|
||||
21.0275484
|
||||
],
|
||||
[
|
||||
105.8628687,
|
||||
21.0336415
|
||||
],
|
||||
[
|
||||
105.8583502,
|
||||
21.04321
|
||||
],
|
||||
[
|
||||
105.8511635,
|
||||
21.0404082
|
||||
],
|
||||
[
|
||||
105.850373,
|
||||
21.0409979
|
||||
],
|
||||
[
|
||||
105.8498029,
|
||||
21.0406472
|
||||
],
|
||||
[
|
||||
105.8468736,
|
||||
21.0398071
|
||||
],
|
||||
[
|
||||
105.8456119,
|
||||
21.0399869
|
||||
],
|
||||
[
|
||||
105.8455139,
|
||||
21.0394981
|
||||
],
|
||||
[
|
||||
105.8453496,
|
||||
21.0393732
|
||||
],
|
||||
[
|
||||
105.8446653,
|
||||
21.0393873
|
||||
],
|
||||
[
|
||||
105.8444986,
|
||||
21.0386255
|
||||
],
|
||||
[
|
||||
105.8447417,
|
||||
21.0385729
|
||||
],
|
||||
[
|
||||
105.8441134,
|
||||
21.0350784
|
||||
],
|
||||
[
|
||||
105.8442231,
|
||||
21.0349021
|
||||
],
|
||||
[
|
||||
105.8440242,
|
||||
21.0337895
|
||||
],
|
||||
[
|
||||
105.8442093,
|
||||
21.0337496
|
||||
],
|
||||
[
|
||||
105.8440313,
|
||||
21.0330194
|
||||
],
|
||||
[
|
||||
105.8438605,
|
||||
21.0328868
|
||||
],
|
||||
[
|
||||
105.8437144,
|
||||
21.0319452
|
||||
],
|
||||
[
|
||||
105.8438773,
|
||||
21.0319139
|
||||
],
|
||||
[
|
||||
105.8438257,
|
||||
21.0316686
|
||||
],
|
||||
[
|
||||
105.8442575,
|
||||
21.0315898
|
||||
],
|
||||
[
|
||||
105.8439406,
|
||||
21.0301552
|
||||
],
|
||||
[
|
||||
105.8440669,
|
||||
21.030125
|
||||
],
|
||||
[
|
||||
105.8420875,
|
||||
21.0287131
|
||||
],
|
||||
[
|
||||
105.841569,
|
||||
21.0288646
|
||||
],
|
||||
[
|
||||
105.8415518,
|
||||
21.0276316
|
||||
],
|
||||
[
|
||||
105.8410999,
|
||||
21.027382
|
||||
],
|
||||
[
|
||||
105.8410075,
|
||||
21.0268439
|
||||
],
|
||||
[
|
||||
105.841053,
|
||||
21.0247821
|
||||
],
|
||||
[
|
||||
105.8412317,
|
||||
21.0247813
|
||||
],
|
||||
[
|
||||
105.8412059,
|
||||
21.0233319
|
||||
],
|
||||
[
|
||||
105.8410133,
|
||||
21.02333
|
||||
],
|
||||
[
|
||||
105.8409566,
|
||||
21.0228612
|
||||
],
|
||||
[
|
||||
105.8409287,
|
||||
21.0210536
|
||||
]
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,386 @@
|
||||
{
|
||||
"type": "Feature",
|
||||
"properties": {
|
||||
"code": "HN-LONGBIEN",
|
||||
"name": "Long Bien",
|
||||
"level": "district",
|
||||
"parent": "HN",
|
||||
"basis": "pre-2025-merger extent, from OSM boundary/historic relations",
|
||||
"areaKm2": 60,
|
||||
"bbox": [
|
||||
105.84106,
|
||||
20.99411,
|
||||
105.93877,
|
||||
21.08081
|
||||
],
|
||||
"center": [
|
||||
21.04246,
|
||||
105.89397
|
||||
],
|
||||
"generatedAt": "2026-08-30T07:33:05.809Z",
|
||||
"osm": "relation/7115863"
|
||||
},
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [
|
||||
[
|
||||
[
|
||||
105.8410552,
|
||||
21.0808132
|
||||
],
|
||||
[
|
||||
105.8425051,
|
||||
21.0741428
|
||||
],
|
||||
[
|
||||
105.8451151,
|
||||
21.0583303
|
||||
],
|
||||
[
|
||||
105.8482762,
|
||||
21.0533744
|
||||
],
|
||||
[
|
||||
105.8500753,
|
||||
21.049457
|
||||
],
|
||||
[
|
||||
105.8539784,
|
||||
21.0445588
|
||||
],
|
||||
[
|
||||
105.8559747,
|
||||
21.0422867
|
||||
],
|
||||
[
|
||||
105.8583502,
|
||||
21.04321
|
||||
],
|
||||
[
|
||||
105.8628687,
|
||||
21.0336415
|
||||
],
|
||||
[
|
||||
105.8653775,
|
||||
21.0275484
|
||||
],
|
||||
[
|
||||
105.8672823,
|
||||
21.020032
|
||||
],
|
||||
[
|
||||
105.8680358,
|
||||
21.0179708
|
||||
],
|
||||
[
|
||||
105.8731684,
|
||||
21.011577
|
||||
],
|
||||
[
|
||||
105.8786959,
|
||||
21.006401
|
||||
],
|
||||
[
|
||||
105.8814425,
|
||||
21.0041734
|
||||
],
|
||||
[
|
||||
105.8874507,
|
||||
21.001401
|
||||
],
|
||||
[
|
||||
105.8943127,
|
||||
20.9988433
|
||||
],
|
||||
[
|
||||
105.897819,
|
||||
20.9964489
|
||||
],
|
||||
[
|
||||
105.9007397,
|
||||
20.9941093
|
||||
],
|
||||
[
|
||||
105.9043846,
|
||||
21.0008137
|
||||
],
|
||||
[
|
||||
105.9033156,
|
||||
21.0025557
|
||||
],
|
||||
[
|
||||
105.9038626,
|
||||
21.0028187
|
||||
],
|
||||
[
|
||||
105.9052356,
|
||||
21.0031057
|
||||
],
|
||||
[
|
||||
105.9083546,
|
||||
21.0044997
|
||||
],
|
||||
[
|
||||
105.9087246,
|
||||
21.0039587
|
||||
],
|
||||
[
|
||||
105.9100155,
|
||||
21.004964
|
||||
],
|
||||
[
|
||||
105.9110797,
|
||||
21.004051
|
||||
],
|
||||
[
|
||||
105.9120181,
|
||||
21.0051272
|
||||
],
|
||||
[
|
||||
105.9127359,
|
||||
21.0033699
|
||||
],
|
||||
[
|
||||
105.915022,
|
||||
21.0022128
|
||||
],
|
||||
[
|
||||
105.9190838,
|
||||
21.0035474
|
||||
],
|
||||
[
|
||||
105.9185546,
|
||||
21.0044067
|
||||
],
|
||||
[
|
||||
105.9204944,
|
||||
21.0044573
|
||||
],
|
||||
[
|
||||
105.9197986,
|
||||
21.0120237
|
||||
],
|
||||
[
|
||||
105.9225365,
|
||||
21.0121697
|
||||
],
|
||||
[
|
||||
105.9222444,
|
||||
21.0141154
|
||||
],
|
||||
[
|
||||
105.9242871,
|
||||
21.0144345
|
||||
],
|
||||
[
|
||||
105.9241676,
|
||||
21.0151067
|
||||
],
|
||||
[
|
||||
105.9236632,
|
||||
21.0150335
|
||||
],
|
||||
[
|
||||
105.9234018,
|
||||
21.0169388
|
||||
],
|
||||
[
|
||||
105.9261194,
|
||||
21.0173509
|
||||
],
|
||||
[
|
||||
105.9259879,
|
||||
21.0198422
|
||||
],
|
||||
[
|
||||
105.9376942,
|
||||
21.0211843
|
||||
],
|
||||
[
|
||||
105.9344436,
|
||||
21.0225857
|
||||
],
|
||||
[
|
||||
105.9350089,
|
||||
21.0237395
|
||||
],
|
||||
[
|
||||
105.9342452,
|
||||
21.0240047
|
||||
],
|
||||
[
|
||||
105.9348428,
|
||||
21.0246706
|
||||
],
|
||||
[
|
||||
105.9294986,
|
||||
21.0261197
|
||||
],
|
||||
[
|
||||
105.9288995,
|
||||
21.0248291
|
||||
],
|
||||
[
|
||||
105.9269211,
|
||||
21.025679
|
||||
],
|
||||
[
|
||||
105.9277037,
|
||||
21.0270713
|
||||
],
|
||||
[
|
||||
105.9289069,
|
||||
21.0278544
|
||||
],
|
||||
[
|
||||
105.9312824,
|
||||
21.0299682
|
||||
],
|
||||
[
|
||||
105.9339097,
|
||||
21.0348313
|
||||
],
|
||||
[
|
||||
105.9342198,
|
||||
21.034549
|
||||
],
|
||||
[
|
||||
105.9342153,
|
||||
21.0342177
|
||||
],
|
||||
[
|
||||
105.9343787,
|
||||
21.0340708
|
||||
],
|
||||
[
|
||||
105.9352619,
|
||||
21.0352395
|
||||
],
|
||||
[
|
||||
105.9348101,
|
||||
21.0356593
|
||||
],
|
||||
[
|
||||
105.9387681,
|
||||
21.0410105
|
||||
],
|
||||
[
|
||||
105.937179,
|
||||
21.0431956
|
||||
],
|
||||
[
|
||||
105.9352365,
|
||||
21.0483503
|
||||
],
|
||||
[
|
||||
105.9330641,
|
||||
21.056503
|
||||
],
|
||||
[
|
||||
105.9269924,
|
||||
21.0694404
|
||||
],
|
||||
[
|
||||
105.924045,
|
||||
21.072633
|
||||
],
|
||||
[
|
||||
105.9208897,
|
||||
21.0749899
|
||||
],
|
||||
[
|
||||
105.9157,
|
||||
21.076792
|
||||
],
|
||||
[
|
||||
105.902726,
|
||||
21.0784186
|
||||
],
|
||||
[
|
||||
105.9011892,
|
||||
21.0793852
|
||||
],
|
||||
[
|
||||
105.8993696,
|
||||
21.0798257
|
||||
],
|
||||
[
|
||||
105.8975328,
|
||||
21.0799138
|
||||
],
|
||||
[
|
||||
105.8956617,
|
||||
21.0797776
|
||||
],
|
||||
[
|
||||
105.8934199,
|
||||
21.0792939
|
||||
],
|
||||
[
|
||||
105.8907941,
|
||||
21.0785021
|
||||
],
|
||||
[
|
||||
105.8879581,
|
||||
21.0774476
|
||||
],
|
||||
[
|
||||
105.8848871,
|
||||
21.0759447
|
||||
],
|
||||
[
|
||||
105.8830911,
|
||||
21.0748154
|
||||
],
|
||||
[
|
||||
105.8778604,
|
||||
21.0709307
|
||||
],
|
||||
[
|
||||
105.8769172,
|
||||
21.0704398
|
||||
],
|
||||
[
|
||||
105.86863,
|
||||
21.0672214
|
||||
],
|
||||
[
|
||||
105.866492,
|
||||
21.0668914
|
||||
],
|
||||
[
|
||||
105.864115,
|
||||
21.0669794
|
||||
],
|
||||
[
|
||||
105.861983,
|
||||
21.0677094
|
||||
],
|
||||
[
|
||||
105.859975,
|
||||
21.0688544
|
||||
],
|
||||
[
|
||||
105.852719,
|
||||
21.0748454
|
||||
],
|
||||
[
|
||||
105.848901,
|
||||
21.0775404
|
||||
],
|
||||
[
|
||||
105.846071,
|
||||
21.0799644
|
||||
],
|
||||
[
|
||||
105.8441845,
|
||||
21.0804038
|
||||
],
|
||||
[
|
||||
105.8410552,
|
||||
21.0808132
|
||||
]
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,954 @@
|
||||
{
|
||||
"type": "Feature",
|
||||
"properties": {
|
||||
"code": "HN-NAMTULIEM",
|
||||
"name": "Nam Tu Liem",
|
||||
"level": "district",
|
||||
"parent": "HN",
|
||||
"basis": "pre-2025-merger extent, from OSM boundary/historic relations",
|
||||
"areaKm2": 32.1,
|
||||
"bbox": [
|
||||
105.72656,
|
||||
20.97966,
|
||||
105.7992,
|
||||
21.05656
|
||||
],
|
||||
"center": [
|
||||
21.01256,
|
||||
105.76151
|
||||
],
|
||||
"generatedAt": "2026-08-30T07:33:18.197Z",
|
||||
"osm": "relation/9421137"
|
||||
},
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [
|
||||
[
|
||||
[
|
||||
105.7265554,
|
||||
21.0527566
|
||||
],
|
||||
[
|
||||
105.7282015,
|
||||
21.0512385
|
||||
],
|
||||
[
|
||||
105.7281444,
|
||||
21.0508357
|
||||
],
|
||||
[
|
||||
105.7286835,
|
||||
21.050539
|
||||
],
|
||||
[
|
||||
105.7279511,
|
||||
21.0484161
|
||||
],
|
||||
[
|
||||
105.729502,
|
||||
21.0480528
|
||||
],
|
||||
[
|
||||
105.7293435,
|
||||
21.047176
|
||||
],
|
||||
[
|
||||
105.7301375,
|
||||
21.0470275
|
||||
],
|
||||
[
|
||||
105.7301069,
|
||||
21.046918
|
||||
],
|
||||
[
|
||||
105.7309605,
|
||||
21.0464972
|
||||
],
|
||||
[
|
||||
105.731203,
|
||||
21.0467185
|
||||
],
|
||||
[
|
||||
105.7318508,
|
||||
21.046273
|
||||
],
|
||||
[
|
||||
105.732584,
|
||||
21.0454148
|
||||
],
|
||||
[
|
||||
105.7325861,
|
||||
21.0449842
|
||||
],
|
||||
[
|
||||
105.7320982,
|
||||
21.0447302
|
||||
],
|
||||
[
|
||||
105.7324254,
|
||||
21.0441141
|
||||
],
|
||||
[
|
||||
105.7323085,
|
||||
21.0437831
|
||||
],
|
||||
[
|
||||
105.7330194,
|
||||
21.0437805
|
||||
],
|
||||
[
|
||||
105.7334706,
|
||||
21.0432823
|
||||
],
|
||||
[
|
||||
105.733554,
|
||||
21.0427936
|
||||
],
|
||||
[
|
||||
105.7332142,
|
||||
21.0427168
|
||||
],
|
||||
[
|
||||
105.7331304,
|
||||
21.0425022
|
||||
],
|
||||
[
|
||||
105.7335238,
|
||||
21.0421012
|
||||
],
|
||||
[
|
||||
105.7334343,
|
||||
21.041596
|
||||
],
|
||||
[
|
||||
105.7340858,
|
||||
21.0407417
|
||||
],
|
||||
[
|
||||
105.7336209,
|
||||
21.0402557
|
||||
],
|
||||
[
|
||||
105.7336747,
|
||||
21.0401029
|
||||
],
|
||||
[
|
||||
105.7339636,
|
||||
21.0400469
|
||||
],
|
||||
[
|
||||
105.7341082,
|
||||
21.0397201
|
||||
],
|
||||
[
|
||||
105.7335914,
|
||||
21.0382606
|
||||
],
|
||||
[
|
||||
105.7339299,
|
||||
21.0381434
|
||||
],
|
||||
[
|
||||
105.7344242,
|
||||
21.037534
|
||||
],
|
||||
[
|
||||
105.7347822,
|
||||
21.0373786
|
||||
],
|
||||
[
|
||||
105.7347716,
|
||||
21.0368658
|
||||
],
|
||||
[
|
||||
105.7364153,
|
||||
21.0369263
|
||||
],
|
||||
[
|
||||
105.737761,
|
||||
21.0364883
|
||||
],
|
||||
[
|
||||
105.7385201,
|
||||
21.0365003
|
||||
],
|
||||
[
|
||||
105.7385343,
|
||||
21.0362903
|
||||
],
|
||||
[
|
||||
105.7381194,
|
||||
21.0361481
|
||||
],
|
||||
[
|
||||
105.7382584,
|
||||
21.035574
|
||||
],
|
||||
[
|
||||
105.7385907,
|
||||
21.0356285
|
||||
],
|
||||
[
|
||||
105.738698,
|
||||
21.0336842
|
||||
],
|
||||
[
|
||||
105.738947,
|
||||
21.0327142
|
||||
],
|
||||
[
|
||||
105.7384347,
|
||||
21.0326087
|
||||
],
|
||||
[
|
||||
105.7386467,
|
||||
21.0317996
|
||||
],
|
||||
[
|
||||
105.7371934,
|
||||
21.0314655
|
||||
],
|
||||
[
|
||||
105.7371318,
|
||||
21.031008
|
||||
],
|
||||
[
|
||||
105.7371653,
|
||||
21.03044
|
||||
],
|
||||
[
|
||||
105.7374506,
|
||||
21.0299225
|
||||
],
|
||||
[
|
||||
105.7372438,
|
||||
21.0292288
|
||||
],
|
||||
[
|
||||
105.7377724,
|
||||
21.0283352
|
||||
],
|
||||
[
|
||||
105.7382135,
|
||||
21.0284153
|
||||
],
|
||||
[
|
||||
105.7383666,
|
||||
21.0280314
|
||||
],
|
||||
[
|
||||
105.7379724,
|
||||
21.0277714
|
||||
],
|
||||
[
|
||||
105.7384084,
|
||||
21.0273801
|
||||
],
|
||||
[
|
||||
105.7383855,
|
||||
21.0268519
|
||||
],
|
||||
[
|
||||
105.7376945,
|
||||
21.0263062
|
||||
],
|
||||
[
|
||||
105.737573,
|
||||
21.025782
|
||||
],
|
||||
[
|
||||
105.7387183,
|
||||
21.0256557
|
||||
],
|
||||
[
|
||||
105.7390352,
|
||||
21.0253674
|
||||
],
|
||||
[
|
||||
105.7391098,
|
||||
21.0250557
|
||||
],
|
||||
[
|
||||
105.7389711,
|
||||
21.0237957
|
||||
],
|
||||
[
|
||||
105.7385766,
|
||||
21.0226287
|
||||
],
|
||||
[
|
||||
105.7381521,
|
||||
21.022317
|
||||
],
|
||||
[
|
||||
105.7377577,
|
||||
21.0223356
|
||||
],
|
||||
[
|
||||
105.7375958,
|
||||
21.0225302
|
||||
],
|
||||
[
|
||||
105.7377985,
|
||||
21.0227527
|
||||
],
|
||||
[
|
||||
105.7377014,
|
||||
21.0232163
|
||||
],
|
||||
[
|
||||
105.7373888,
|
||||
21.0233561
|
||||
],
|
||||
[
|
||||
105.736992,
|
||||
21.0233022
|
||||
],
|
||||
[
|
||||
105.7365049,
|
||||
21.0223737
|
||||
],
|
||||
[
|
||||
105.7360296,
|
||||
21.022637
|
||||
],
|
||||
[
|
||||
105.7361321,
|
||||
21.0218284
|
||||
],
|
||||
[
|
||||
105.7356748,
|
||||
21.0216587
|
||||
],
|
||||
[
|
||||
105.7349928,
|
||||
21.0219194
|
||||
],
|
||||
[
|
||||
105.7350108,
|
||||
21.0225604
|
||||
],
|
||||
[
|
||||
105.7344356,
|
||||
21.0226617
|
||||
],
|
||||
[
|
||||
105.7339401,
|
||||
21.0214691
|
||||
],
|
||||
[
|
||||
105.7334891,
|
||||
21.0213493
|
||||
],
|
||||
[
|
||||
105.7318943,
|
||||
21.0195202
|
||||
],
|
||||
[
|
||||
105.7298245,
|
||||
21.0179927
|
||||
],
|
||||
[
|
||||
105.7312698,
|
||||
21.016531
|
||||
],
|
||||
[
|
||||
105.7314199,
|
||||
21.0170569
|
||||
],
|
||||
[
|
||||
105.7325516,
|
||||
21.0168076
|
||||
],
|
||||
[
|
||||
105.7339706,
|
||||
21.0154285
|
||||
],
|
||||
[
|
||||
105.7322403,
|
||||
21.0118537
|
||||
],
|
||||
[
|
||||
105.7331006,
|
||||
21.0088612
|
||||
],
|
||||
[
|
||||
105.7330678,
|
||||
21.0081356
|
||||
],
|
||||
[
|
||||
105.7358041,
|
||||
21.0081832
|
||||
],
|
||||
[
|
||||
105.7352881,
|
||||
21.0065041
|
||||
],
|
||||
[
|
||||
105.7354352,
|
||||
21.0057915
|
||||
],
|
||||
[
|
||||
105.7351481,
|
||||
21.0050517
|
||||
],
|
||||
[
|
||||
105.7336731,
|
||||
21.0042858
|
||||
],
|
||||
[
|
||||
105.7341395,
|
||||
21.0027566
|
||||
],
|
||||
[
|
||||
105.734642,
|
||||
21.0027733
|
||||
],
|
||||
[
|
||||
105.7352933,
|
||||
20.9965275
|
||||
],
|
||||
[
|
||||
105.7361368,
|
||||
20.9969824
|
||||
],
|
||||
[
|
||||
105.7366529,
|
||||
20.9967289
|
||||
],
|
||||
[
|
||||
105.7377685,
|
||||
20.9966434
|
||||
],
|
||||
[
|
||||
105.7388871,
|
||||
20.99484
|
||||
],
|
||||
[
|
||||
105.7401505,
|
||||
20.9954395
|
||||
],
|
||||
[
|
||||
105.7404099,
|
||||
20.9945812
|
||||
],
|
||||
[
|
||||
105.7441635,
|
||||
20.9955025
|
||||
],
|
||||
[
|
||||
105.7443237,
|
||||
20.9941521
|
||||
],
|
||||
[
|
||||
105.7444992,
|
||||
20.9939051
|
||||
],
|
||||
[
|
||||
105.7525767,
|
||||
20.9909776
|
||||
],
|
||||
[
|
||||
105.7548599,
|
||||
20.9898737
|
||||
],
|
||||
[
|
||||
105.7553275,
|
||||
20.9894294
|
||||
],
|
||||
[
|
||||
105.756219,
|
||||
20.9899041
|
||||
],
|
||||
[
|
||||
105.7581558,
|
||||
20.985199
|
||||
],
|
||||
[
|
||||
105.7584133,
|
||||
20.9849471
|
||||
],
|
||||
[
|
||||
105.7576629,
|
||||
20.9823668
|
||||
],
|
||||
[
|
||||
105.7615828,
|
||||
20.97966
|
||||
],
|
||||
[
|
||||
105.7641513,
|
||||
20.9801265
|
||||
],
|
||||
[
|
||||
105.7642056,
|
||||
20.9811898
|
||||
],
|
||||
[
|
||||
105.7645049,
|
||||
20.9812494
|
||||
],
|
||||
[
|
||||
105.7647552,
|
||||
20.9829025
|
||||
],
|
||||
[
|
||||
105.7660663,
|
||||
20.9830231
|
||||
],
|
||||
[
|
||||
105.7659272,
|
||||
20.984543
|
||||
],
|
||||
[
|
||||
105.7669373,
|
||||
20.9847527
|
||||
],
|
||||
[
|
||||
105.7672867,
|
||||
20.984035
|
||||
],
|
||||
[
|
||||
105.7671158,
|
||||
20.9839577
|
||||
],
|
||||
[
|
||||
105.7673187,
|
||||
20.9834042
|
||||
],
|
||||
[
|
||||
105.7678482,
|
||||
20.9835775
|
||||
],
|
||||
[
|
||||
105.7681407,
|
||||
20.9834497
|
||||
],
|
||||
[
|
||||
105.7707865,
|
||||
20.9853404
|
||||
],
|
||||
[
|
||||
105.7716595,
|
||||
20.9857806
|
||||
],
|
||||
[
|
||||
105.7721358,
|
||||
20.98516
|
||||
],
|
||||
[
|
||||
105.7728386,
|
||||
20.9854614
|
||||
],
|
||||
[
|
||||
105.7733568,
|
||||
20.9848879
|
||||
],
|
||||
[
|
||||
105.7749279,
|
||||
20.9858929
|
||||
],
|
||||
[
|
||||
105.7754288,
|
||||
20.9859777
|
||||
],
|
||||
[
|
||||
105.7760811,
|
||||
20.9856467
|
||||
],
|
||||
[
|
||||
105.777975,
|
||||
20.9840923
|
||||
],
|
||||
[
|
||||
105.7783358,
|
||||
20.9849664
|
||||
],
|
||||
[
|
||||
105.779169,
|
||||
20.9862188
|
||||
],
|
||||
[
|
||||
105.7805248,
|
||||
20.9875245
|
||||
],
|
||||
[
|
||||
105.7806688,
|
||||
20.9880135
|
||||
],
|
||||
[
|
||||
105.7804894,
|
||||
20.9886834
|
||||
],
|
||||
[
|
||||
105.7808261,
|
||||
20.9888688
|
||||
],
|
||||
[
|
||||
105.7814921,
|
||||
20.9886629
|
||||
],
|
||||
[
|
||||
105.782013,
|
||||
20.9880076
|
||||
],
|
||||
[
|
||||
105.7826911,
|
||||
20.9875166
|
||||
],
|
||||
[
|
||||
105.7836958,
|
||||
20.9870738
|
||||
],
|
||||
[
|
||||
105.7839731,
|
||||
20.987276
|
||||
],
|
||||
[
|
||||
105.784454,
|
||||
20.9881959
|
||||
],
|
||||
[
|
||||
105.7838931,
|
||||
20.988496
|
||||
],
|
||||
[
|
||||
105.7841529,
|
||||
20.9890349
|
||||
],
|
||||
[
|
||||
105.78511,
|
||||
20.9886388
|
||||
],
|
||||
[
|
||||
105.7852904,
|
||||
20.988882
|
||||
],
|
||||
[
|
||||
105.7864437,
|
||||
20.9881802
|
||||
],
|
||||
[
|
||||
105.7861041,
|
||||
20.9877465
|
||||
],
|
||||
[
|
||||
105.7882976,
|
||||
20.9868821
|
||||
],
|
||||
[
|
||||
105.7881639,
|
||||
20.9864807
|
||||
],
|
||||
[
|
||||
105.7879352,
|
||||
20.9862953
|
||||
],
|
||||
[
|
||||
105.7874252,
|
||||
20.9864116
|
||||
],
|
||||
[
|
||||
105.78731,
|
||||
20.9859762
|
||||
],
|
||||
[
|
||||
105.7867901,
|
||||
20.9859547
|
||||
],
|
||||
[
|
||||
105.7843063,
|
||||
20.9868325
|
||||
],
|
||||
[
|
||||
105.7840789,
|
||||
20.9865921
|
||||
],
|
||||
[
|
||||
105.7869418,
|
||||
20.9855189
|
||||
],
|
||||
[
|
||||
105.7897013,
|
||||
20.9852567
|
||||
],
|
||||
[
|
||||
105.7928814,
|
||||
20.9854332
|
||||
],
|
||||
[
|
||||
105.7931848,
|
||||
20.9843837
|
||||
],
|
||||
[
|
||||
105.7992035,
|
||||
20.9886543
|
||||
],
|
||||
[
|
||||
105.7988803,
|
||||
20.9889204
|
||||
],
|
||||
[
|
||||
105.7985698,
|
||||
20.988713
|
||||
],
|
||||
[
|
||||
105.7955672,
|
||||
20.9916585
|
||||
],
|
||||
[
|
||||
105.7967488,
|
||||
20.9952297
|
||||
],
|
||||
[
|
||||
105.7953683,
|
||||
20.9955984
|
||||
],
|
||||
[
|
||||
105.7959189,
|
||||
20.9987431
|
||||
],
|
||||
[
|
||||
105.7954202,
|
||||
20.9985766
|
||||
],
|
||||
[
|
||||
105.7925225,
|
||||
20.9990195
|
||||
],
|
||||
[
|
||||
105.7921847,
|
||||
20.9994064
|
||||
],
|
||||
[
|
||||
105.7924921,
|
||||
20.9996747
|
||||
],
|
||||
[
|
||||
105.7926058,
|
||||
21.0000689
|
||||
],
|
||||
[
|
||||
105.7931229,
|
||||
20.9999287
|
||||
],
|
||||
[
|
||||
105.7932928,
|
||||
21.0006987
|
||||
],
|
||||
[
|
||||
105.7929681,
|
||||
21.0011358
|
||||
],
|
||||
[
|
||||
105.7923056,
|
||||
21.0006558
|
||||
],
|
||||
[
|
||||
105.7912413,
|
||||
21.0020701
|
||||
],
|
||||
[
|
||||
105.7923268,
|
||||
21.0029651
|
||||
],
|
||||
[
|
||||
105.7911792,
|
||||
21.0042771
|
||||
],
|
||||
[
|
||||
105.7923279,
|
||||
21.0051517
|
||||
],
|
||||
[
|
||||
105.789528,
|
||||
21.0086363
|
||||
],
|
||||
[
|
||||
105.7903402,
|
||||
21.0091788
|
||||
],
|
||||
[
|
||||
105.7896689,
|
||||
21.0100039
|
||||
],
|
||||
[
|
||||
105.7902222,
|
||||
21.0104008
|
||||
],
|
||||
[
|
||||
105.785472,
|
||||
21.0162432
|
||||
],
|
||||
[
|
||||
105.7850953,
|
||||
21.0170482
|
||||
],
|
||||
[
|
||||
105.7850393,
|
||||
21.0180582
|
||||
],
|
||||
[
|
||||
105.7815587,
|
||||
21.0221948
|
||||
],
|
||||
[
|
||||
105.7840713,
|
||||
21.0243703
|
||||
],
|
||||
[
|
||||
105.7838889,
|
||||
21.0292615
|
||||
],
|
||||
[
|
||||
105.7815738,
|
||||
21.0294594
|
||||
],
|
||||
[
|
||||
105.7818257,
|
||||
21.0312663
|
||||
],
|
||||
[
|
||||
105.7799473,
|
||||
21.0315499
|
||||
],
|
||||
[
|
||||
105.7798595,
|
||||
21.0323643
|
||||
],
|
||||
[
|
||||
105.7789263,
|
||||
21.0316035
|
||||
],
|
||||
[
|
||||
105.7788567,
|
||||
21.0317565
|
||||
],
|
||||
[
|
||||
105.77687,
|
||||
21.0309349
|
||||
],
|
||||
[
|
||||
105.7763153,
|
||||
21.0316715
|
||||
],
|
||||
[
|
||||
105.7754519,
|
||||
21.0316668
|
||||
],
|
||||
[
|
||||
105.7754638,
|
||||
21.0326857
|
||||
],
|
||||
[
|
||||
105.774857,
|
||||
21.0325864
|
||||
],
|
||||
[
|
||||
105.7744746,
|
||||
21.0327242
|
||||
],
|
||||
[
|
||||
105.7743821,
|
||||
21.0325638
|
||||
],
|
||||
[
|
||||
105.773558,
|
||||
21.0329239
|
||||
],
|
||||
[
|
||||
105.7739591,
|
||||
21.0335768
|
||||
],
|
||||
[
|
||||
105.7728314,
|
||||
21.0346244
|
||||
],
|
||||
[
|
||||
105.7713989,
|
||||
21.0351084
|
||||
],
|
||||
[
|
||||
105.7715069,
|
||||
21.035458
|
||||
],
|
||||
[
|
||||
105.7704902,
|
||||
21.035438
|
||||
],
|
||||
[
|
||||
105.768541,
|
||||
21.0359941
|
||||
],
|
||||
[
|
||||
105.7687349,
|
||||
21.0367241
|
||||
],
|
||||
[
|
||||
105.7681368,
|
||||
21.0369085
|
||||
],
|
||||
[
|
||||
105.76838,
|
||||
21.0375786
|
||||
],
|
||||
[
|
||||
105.7671552,
|
||||
21.037974
|
||||
],
|
||||
[
|
||||
105.7677076,
|
||||
21.0395819
|
||||
],
|
||||
[
|
||||
105.7614552,
|
||||
21.0416514
|
||||
],
|
||||
[
|
||||
105.7606735,
|
||||
21.0400505
|
||||
],
|
||||
[
|
||||
105.7585526,
|
||||
21.0383606
|
||||
],
|
||||
[
|
||||
105.7577856,
|
||||
21.038869
|
||||
],
|
||||
[
|
||||
105.7550854,
|
||||
21.0385187
|
||||
],
|
||||
[
|
||||
105.7533722,
|
||||
21.0379753
|
||||
],
|
||||
[
|
||||
105.7505533,
|
||||
21.0406432
|
||||
],
|
||||
[
|
||||
105.7388708,
|
||||
21.0449021
|
||||
],
|
||||
[
|
||||
105.7372045,
|
||||
21.0441581
|
||||
],
|
||||
[
|
||||
105.7331864,
|
||||
21.0540117
|
||||
],
|
||||
[
|
||||
105.7294564,
|
||||
21.0565602
|
||||
],
|
||||
[
|
||||
105.7265554,
|
||||
21.0527566
|
||||
]
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,422 @@
|
||||
{
|
||||
"type": "Feature",
|
||||
"properties": {
|
||||
"code": "HN-TAYHO",
|
||||
"name": "Tay Ho",
|
||||
"level": "district",
|
||||
"parent": "HN",
|
||||
"basis": "pre-2025-merger extent, from OSM boundary/historic relations",
|
||||
"areaKm2": 24.6,
|
||||
"bbox": [
|
||||
105.79178,
|
||||
21.03951,
|
||||
105.84828,
|
||||
21.09737
|
||||
],
|
||||
"center": [
|
||||
21.06931,
|
||||
105.81988
|
||||
],
|
||||
"generatedAt": "2026-08-30T07:33:03.809Z",
|
||||
"osm": "relation/9421139"
|
||||
},
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [
|
||||
[
|
||||
[
|
||||
105.7917802,
|
||||
21.0901655
|
||||
],
|
||||
[
|
||||
105.7931286,
|
||||
21.0901194
|
||||
],
|
||||
[
|
||||
105.7932273,
|
||||
21.0869727
|
||||
],
|
||||
[
|
||||
105.792696,
|
||||
21.0854118
|
||||
],
|
||||
[
|
||||
105.7927984,
|
||||
21.0842162
|
||||
],
|
||||
[
|
||||
105.7918724,
|
||||
21.0833089
|
||||
],
|
||||
[
|
||||
105.7944054,
|
||||
21.0811956
|
||||
],
|
||||
[
|
||||
105.7966414,
|
||||
21.0798818
|
||||
],
|
||||
[
|
||||
105.7986633,
|
||||
21.0797393
|
||||
],
|
||||
[
|
||||
105.7994434,
|
||||
21.0798267
|
||||
],
|
||||
[
|
||||
105.7993462,
|
||||
21.0787609
|
||||
],
|
||||
[
|
||||
105.801256,
|
||||
21.0784971
|
||||
],
|
||||
[
|
||||
105.8011754,
|
||||
21.0766859
|
||||
],
|
||||
[
|
||||
105.7999578,
|
||||
21.0738816
|
||||
],
|
||||
[
|
||||
105.8037447,
|
||||
21.0723715
|
||||
],
|
||||
[
|
||||
105.8033512,
|
||||
21.0714843
|
||||
],
|
||||
[
|
||||
105.80403,
|
||||
21.0711055
|
||||
],
|
||||
[
|
||||
105.8032616,
|
||||
21.0699916
|
||||
],
|
||||
[
|
||||
105.804408,
|
||||
21.0693096
|
||||
],
|
||||
[
|
||||
105.8041905,
|
||||
21.0690542
|
||||
],
|
||||
[
|
||||
105.8047729,
|
||||
21.068625
|
||||
],
|
||||
[
|
||||
105.8037064,
|
||||
21.0674998
|
||||
],
|
||||
[
|
||||
105.8049927,
|
||||
21.0658798
|
||||
],
|
||||
[
|
||||
105.7984755,
|
||||
21.0597574
|
||||
],
|
||||
[
|
||||
105.7976596,
|
||||
21.0597544
|
||||
],
|
||||
[
|
||||
105.7976453,
|
||||
21.0568003
|
||||
],
|
||||
[
|
||||
105.8007018,
|
||||
21.0567735
|
||||
],
|
||||
[
|
||||
105.8007069,
|
||||
21.0518971
|
||||
],
|
||||
[
|
||||
105.7948851,
|
||||
21.0518683
|
||||
],
|
||||
[
|
||||
105.7948877,
|
||||
21.050306
|
||||
],
|
||||
[
|
||||
105.7963479,
|
||||
21.0502891
|
||||
],
|
||||
[
|
||||
105.7963237,
|
||||
21.0490036
|
||||
],
|
||||
[
|
||||
105.7970526,
|
||||
21.0489978
|
||||
],
|
||||
[
|
||||
105.7970657,
|
||||
21.0484688
|
||||
],
|
||||
[
|
||||
105.7976126,
|
||||
21.047933
|
||||
],
|
||||
[
|
||||
105.7983277,
|
||||
21.0477188
|
||||
],
|
||||
[
|
||||
105.8000849,
|
||||
21.0495234
|
||||
],
|
||||
[
|
||||
105.8017784,
|
||||
21.0495312
|
||||
],
|
||||
[
|
||||
105.8032079,
|
||||
21.0514579
|
||||
],
|
||||
[
|
||||
105.8044655,
|
||||
21.052119
|
||||
],
|
||||
[
|
||||
105.8044541,
|
||||
21.052675
|
||||
],
|
||||
[
|
||||
105.8048501,
|
||||
21.0523938
|
||||
],
|
||||
[
|
||||
105.806194,
|
||||
21.0542752
|
||||
],
|
||||
[
|
||||
105.8056078,
|
||||
21.0546947
|
||||
],
|
||||
[
|
||||
105.8062956,
|
||||
21.0558457
|
||||
],
|
||||
[
|
||||
105.8066696,
|
||||
21.0561522
|
||||
],
|
||||
[
|
||||
105.8072297,
|
||||
21.0555079
|
||||
],
|
||||
[
|
||||
105.8078521,
|
||||
21.0558373
|
||||
],
|
||||
[
|
||||
105.8081608,
|
||||
21.0556049
|
||||
],
|
||||
[
|
||||
105.8081818,
|
||||
21.0587044
|
||||
],
|
||||
[
|
||||
105.8088495,
|
||||
21.0587904
|
||||
],
|
||||
[
|
||||
105.8088619,
|
||||
21.0543138
|
||||
],
|
||||
[
|
||||
105.8091278,
|
||||
21.0524025
|
||||
],
|
||||
[
|
||||
105.8073546,
|
||||
21.0501456
|
||||
],
|
||||
[
|
||||
105.8064319,
|
||||
21.0485954
|
||||
],
|
||||
[
|
||||
105.806034,
|
||||
21.0475871
|
||||
],
|
||||
[
|
||||
105.8080451,
|
||||
21.0473732
|
||||
],
|
||||
[
|
||||
105.8105391,
|
||||
21.046368
|
||||
],
|
||||
[
|
||||
105.811321,
|
||||
21.0459144
|
||||
],
|
||||
[
|
||||
105.813445,
|
||||
21.0438392
|
||||
],
|
||||
[
|
||||
105.8150272,
|
||||
21.0427711
|
||||
],
|
||||
[
|
||||
105.8170597,
|
||||
21.0422873
|
||||
],
|
||||
[
|
||||
105.8201032,
|
||||
21.0420842
|
||||
],
|
||||
[
|
||||
105.8220831,
|
||||
21.0417144
|
||||
],
|
||||
[
|
||||
105.8270974,
|
||||
21.0398022
|
||||
],
|
||||
[
|
||||
105.8284972,
|
||||
21.0395116
|
||||
],
|
||||
[
|
||||
105.828857,
|
||||
21.0396975
|
||||
],
|
||||
[
|
||||
105.8305126,
|
||||
21.041389
|
||||
],
|
||||
[
|
||||
105.8310824,
|
||||
21.041664
|
||||
],
|
||||
[
|
||||
105.8334509,
|
||||
21.041894
|
||||
],
|
||||
[
|
||||
105.8362002,
|
||||
21.0416431
|
||||
],
|
||||
[
|
||||
105.8362631,
|
||||
21.0423036
|
||||
],
|
||||
[
|
||||
105.8358479,
|
||||
21.0427603
|
||||
],
|
||||
[
|
||||
105.835859,
|
||||
21.0429612
|
||||
],
|
||||
[
|
||||
105.8363678,
|
||||
21.0444067
|
||||
],
|
||||
[
|
||||
105.8365711,
|
||||
21.0443813
|
||||
],
|
||||
[
|
||||
105.8368352,
|
||||
21.0466532
|
||||
],
|
||||
[
|
||||
105.8385809,
|
||||
21.0488143
|
||||
],
|
||||
[
|
||||
105.8392367,
|
||||
21.050005
|
||||
],
|
||||
[
|
||||
105.840108,
|
||||
21.0506702
|
||||
],
|
||||
[
|
||||
105.8408247,
|
||||
21.0501274
|
||||
],
|
||||
[
|
||||
105.8422694,
|
||||
21.0486018
|
||||
],
|
||||
[
|
||||
105.8431374,
|
||||
21.0492496
|
||||
],
|
||||
[
|
||||
105.8432213,
|
||||
21.049163
|
||||
],
|
||||
[
|
||||
105.8440237,
|
||||
21.049552
|
||||
],
|
||||
[
|
||||
105.8442258,
|
||||
21.049303
|
||||
],
|
||||
[
|
||||
105.8482762,
|
||||
21.0533744
|
||||
],
|
||||
[
|
||||
105.8451151,
|
||||
21.0583303
|
||||
],
|
||||
[
|
||||
105.8425051,
|
||||
21.0741428
|
||||
],
|
||||
[
|
||||
105.8410552,
|
||||
21.0808132
|
||||
],
|
||||
[
|
||||
105.834592,
|
||||
21.0865736
|
||||
],
|
||||
[
|
||||
105.8271842,
|
||||
21.0913874
|
||||
],
|
||||
[
|
||||
105.8181654,
|
||||
21.0949711
|
||||
],
|
||||
[
|
||||
105.8115686,
|
||||
21.0964006
|
||||
],
|
||||
[
|
||||
105.8093796,
|
||||
21.0966938
|
||||
],
|
||||
[
|
||||
105.7942616,
|
||||
21.0973691
|
||||
],
|
||||
[
|
||||
105.7917862,
|
||||
21.0911942
|
||||
],
|
||||
[
|
||||
105.7917802,
|
||||
21.0901655
|
||||
]
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,538 @@
|
||||
{
|
||||
"type": "Feature",
|
||||
"properties": {
|
||||
"code": "HN-THANHXUAN",
|
||||
"name": "Thanh Xuan",
|
||||
"level": "district",
|
||||
"parent": "HN",
|
||||
"basis": "pre-2025-merger extent, from OSM boundary/historic relations",
|
||||
"areaKm2": 9.4,
|
||||
"bbox": [
|
||||
105.79218,
|
||||
20.98056,
|
||||
105.84287,
|
||||
21.00885
|
||||
],
|
||||
"center": [
|
||||
20.99482,
|
||||
105.81531
|
||||
],
|
||||
"generatedAt": "2026-08-30T07:33:14.827Z",
|
||||
"osm": "relation/9421130"
|
||||
},
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [
|
||||
[
|
||||
[
|
||||
105.7921847,
|
||||
20.9994064
|
||||
],
|
||||
[
|
||||
105.7925225,
|
||||
20.9990195
|
||||
],
|
||||
[
|
||||
105.7954202,
|
||||
20.9985766
|
||||
],
|
||||
[
|
||||
105.7959189,
|
||||
20.9987431
|
||||
],
|
||||
[
|
||||
105.7953683,
|
||||
20.9955984
|
||||
],
|
||||
[
|
||||
105.7967488,
|
||||
20.9952297
|
||||
],
|
||||
[
|
||||
105.7955672,
|
||||
20.9916585
|
||||
],
|
||||
[
|
||||
105.7985698,
|
||||
20.988713
|
||||
],
|
||||
[
|
||||
105.7988803,
|
||||
20.9889204
|
||||
],
|
||||
[
|
||||
105.7992035,
|
||||
20.9886543
|
||||
],
|
||||
[
|
||||
105.7941755,
|
||||
20.9850986
|
||||
],
|
||||
[
|
||||
105.7944442,
|
||||
20.984765
|
||||
],
|
||||
[
|
||||
105.7948593,
|
||||
20.9850655
|
||||
],
|
||||
[
|
||||
105.7949499,
|
||||
20.9849325
|
||||
],
|
||||
[
|
||||
105.7959401,
|
||||
20.9853732
|
||||
],
|
||||
[
|
||||
105.7964739,
|
||||
20.9847567
|
||||
],
|
||||
[
|
||||
105.7965189,
|
||||
20.9843631
|
||||
],
|
||||
[
|
||||
105.7971245,
|
||||
20.9846793
|
||||
],
|
||||
[
|
||||
105.7978722,
|
||||
20.9838082
|
||||
],
|
||||
[
|
||||
105.7977493,
|
||||
20.9837244
|
||||
],
|
||||
[
|
||||
105.797853,
|
||||
20.9835807
|
||||
],
|
||||
[
|
||||
105.7987951,
|
||||
20.984228
|
||||
],
|
||||
[
|
||||
105.7992386,
|
||||
20.9836897
|
||||
],
|
||||
[
|
||||
105.7996975,
|
||||
20.984021
|
||||
],
|
||||
[
|
||||
105.7998257,
|
||||
20.9838713
|
||||
],
|
||||
[
|
||||
105.7999996,
|
||||
20.9840066
|
||||
],
|
||||
[
|
||||
105.8006303,
|
||||
20.9833566
|
||||
],
|
||||
[
|
||||
105.8007427,
|
||||
20.9834923
|
||||
],
|
||||
[
|
||||
105.8010852,
|
||||
20.9832965
|
||||
],
|
||||
[
|
||||
105.8016484,
|
||||
20.9840717
|
||||
],
|
||||
[
|
||||
105.801182,
|
||||
20.984282
|
||||
],
|
||||
[
|
||||
105.8014912,
|
||||
20.9846976
|
||||
],
|
||||
[
|
||||
105.8003989,
|
||||
20.9859725
|
||||
],
|
||||
[
|
||||
105.8011118,
|
||||
20.9864966
|
||||
],
|
||||
[
|
||||
105.8018128,
|
||||
20.9856294
|
||||
],
|
||||
[
|
||||
105.8030888,
|
||||
20.9865328
|
||||
],
|
||||
[
|
||||
105.8033007,
|
||||
20.9865156
|
||||
],
|
||||
[
|
||||
105.8039447,
|
||||
20.9857809
|
||||
],
|
||||
[
|
||||
105.8043126,
|
||||
20.9859939
|
||||
],
|
||||
[
|
||||
105.8048369,
|
||||
20.9858746
|
||||
],
|
||||
[
|
||||
105.8049948,
|
||||
20.9856688
|
||||
],
|
||||
[
|
||||
105.805136,
|
||||
20.9857563
|
||||
],
|
||||
[
|
||||
105.8074937,
|
||||
20.9851444
|
||||
],
|
||||
[
|
||||
105.8074035,
|
||||
20.9849114
|
||||
],
|
||||
[
|
||||
105.8080981,
|
||||
20.9840396
|
||||
],
|
||||
[
|
||||
105.8085415,
|
||||
20.9843294
|
||||
],
|
||||
[
|
||||
105.8108342,
|
||||
20.9812607
|
||||
],
|
||||
[
|
||||
105.8112612,
|
||||
20.9815285
|
||||
],
|
||||
[
|
||||
105.8134186,
|
||||
20.9805625
|
||||
],
|
||||
[
|
||||
105.8145841,
|
||||
20.9823579
|
||||
],
|
||||
[
|
||||
105.8156492,
|
||||
20.9832811
|
||||
],
|
||||
[
|
||||
105.8166696,
|
||||
20.9820833
|
||||
],
|
||||
[
|
||||
105.8183402,
|
||||
20.9807013
|
||||
],
|
||||
[
|
||||
105.8187176,
|
||||
20.9813471
|
||||
],
|
||||
[
|
||||
105.8189662,
|
||||
20.9824693
|
||||
],
|
||||
[
|
||||
105.8189901,
|
||||
20.9828929
|
||||
],
|
||||
[
|
||||
105.8187154,
|
||||
20.9830612
|
||||
],
|
||||
[
|
||||
105.8188331,
|
||||
20.9839253
|
||||
],
|
||||
[
|
||||
105.8186721,
|
||||
20.9843114
|
||||
],
|
||||
[
|
||||
105.819547,
|
||||
20.9846233
|
||||
],
|
||||
[
|
||||
105.8194547,
|
||||
20.9848938
|
||||
],
|
||||
[
|
||||
105.8204299,
|
||||
20.9852386
|
||||
],
|
||||
[
|
||||
105.820343,
|
||||
20.9854372
|
||||
],
|
||||
[
|
||||
105.8229859,
|
||||
20.9868674
|
||||
],
|
||||
[
|
||||
105.8249429,
|
||||
20.988673
|
||||
],
|
||||
[
|
||||
105.8244454,
|
||||
20.9892345
|
||||
],
|
||||
[
|
||||
105.8263722,
|
||||
20.991313
|
||||
],
|
||||
[
|
||||
105.8267682,
|
||||
20.9915716
|
||||
],
|
||||
[
|
||||
105.8270288,
|
||||
20.9914228
|
||||
],
|
||||
[
|
||||
105.8272994,
|
||||
20.9916723
|
||||
],
|
||||
[
|
||||
105.8275234,
|
||||
20.9926901
|
||||
],
|
||||
[
|
||||
105.827863,
|
||||
20.993133
|
||||
],
|
||||
[
|
||||
105.8281027,
|
||||
20.9930484
|
||||
],
|
||||
[
|
||||
105.8284586,
|
||||
20.993581
|
||||
],
|
||||
[
|
||||
105.8293818,
|
||||
20.9930265
|
||||
],
|
||||
[
|
||||
105.8302384,
|
||||
20.993544
|
||||
],
|
||||
[
|
||||
105.8305148,
|
||||
20.9932046
|
||||
],
|
||||
[
|
||||
105.8309168,
|
||||
20.9919633
|
||||
],
|
||||
[
|
||||
105.8319262,
|
||||
20.990325
|
||||
],
|
||||
[
|
||||
105.8324136,
|
||||
20.9903439
|
||||
],
|
||||
[
|
||||
105.832593,
|
||||
20.9898237
|
||||
],
|
||||
[
|
||||
105.8331242,
|
||||
20.9892538
|
||||
],
|
||||
[
|
||||
105.8332758,
|
||||
20.9891907
|
||||
],
|
||||
[
|
||||
105.8335481,
|
||||
20.9893916
|
||||
],
|
||||
[
|
||||
105.8337948,
|
||||
20.9886077
|
||||
],
|
||||
[
|
||||
105.8335351,
|
||||
20.9884566
|
||||
],
|
||||
[
|
||||
105.8338235,
|
||||
20.9880845
|
||||
],
|
||||
[
|
||||
105.8339923,
|
||||
20.9882282
|
||||
],
|
||||
[
|
||||
105.8349722,
|
||||
20.9867733
|
||||
],
|
||||
[
|
||||
105.835289,
|
||||
20.9865866
|
||||
],
|
||||
[
|
||||
105.8356537,
|
||||
20.9855105
|
||||
],
|
||||
[
|
||||
105.8365391,
|
||||
20.9857867
|
||||
],
|
||||
[
|
||||
105.8375177,
|
||||
20.9838812
|
||||
],
|
||||
[
|
||||
105.8410587,
|
||||
20.9854146
|
||||
],
|
||||
[
|
||||
105.8411886,
|
||||
20.9929628
|
||||
],
|
||||
[
|
||||
105.8414087,
|
||||
20.9929659
|
||||
],
|
||||
[
|
||||
105.8427682,
|
||||
20.9952614
|
||||
],
|
||||
[
|
||||
105.8428695,
|
||||
20.9957401
|
||||
],
|
||||
[
|
||||
105.8426251,
|
||||
20.997416
|
||||
],
|
||||
[
|
||||
105.8369412,
|
||||
20.9988668
|
||||
],
|
||||
[
|
||||
105.8329565,
|
||||
20.9996053
|
||||
],
|
||||
[
|
||||
105.8267548,
|
||||
21.0014485
|
||||
],
|
||||
[
|
||||
105.8202057,
|
||||
21.003109
|
||||
],
|
||||
[
|
||||
105.8181305,
|
||||
21.001585
|
||||
],
|
||||
[
|
||||
105.8166057,
|
||||
21.0051511
|
||||
],
|
||||
[
|
||||
105.8158997,
|
||||
21.0063725
|
||||
],
|
||||
[
|
||||
105.8149192,
|
||||
21.007683
|
||||
],
|
||||
[
|
||||
105.8135162,
|
||||
21.0088516
|
||||
],
|
||||
[
|
||||
105.812051,
|
||||
21.0071526
|
||||
],
|
||||
[
|
||||
105.8106943,
|
||||
21.0069677
|
||||
],
|
||||
[
|
||||
105.8101905,
|
||||
21.0070798
|
||||
],
|
||||
[
|
||||
105.8086536,
|
||||
21.0080805
|
||||
],
|
||||
[
|
||||
105.807369,
|
||||
21.0072094
|
||||
],
|
||||
[
|
||||
105.8054112,
|
||||
21.00838
|
||||
],
|
||||
[
|
||||
105.804979,
|
||||
21.0080815
|
||||
],
|
||||
[
|
||||
105.8046619,
|
||||
21.0085679
|
||||
],
|
||||
[
|
||||
105.8029771,
|
||||
21.007087
|
||||
],
|
||||
[
|
||||
105.7987992,
|
||||
21.0040802
|
||||
],
|
||||
[
|
||||
105.7976586,
|
||||
21.0054696
|
||||
],
|
||||
[
|
||||
105.7941053,
|
||||
21.0030027
|
||||
],
|
||||
[
|
||||
105.7946544,
|
||||
21.0022956
|
||||
],
|
||||
[
|
||||
105.7929681,
|
||||
21.0011358
|
||||
],
|
||||
[
|
||||
105.7932928,
|
||||
21.0006987
|
||||
],
|
||||
[
|
||||
105.7931229,
|
||||
20.9999287
|
||||
],
|
||||
[
|
||||
105.7926058,
|
||||
21.0000689
|
||||
],
|
||||
[
|
||||
105.7924921,
|
||||
20.9996747
|
||||
],
|
||||
[
|
||||
105.7921847,
|
||||
20.9994064
|
||||
]
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
+132
-12
@@ -1,18 +1,138 @@
|
||||
// Generated by scripts/build-city-boundaries.mjs. Do not edit by hand.
|
||||
// Generated by scripts/build-region-boundaries.mjs. Do not edit by hand.
|
||||
//
|
||||
// Lists only the boundaries that have actually been built, so a partial
|
||||
// build still compiles.
|
||||
|
||||
import dh from './dh.json';
|
||||
import dl from './dl.json';
|
||||
import dn from './dn.json';
|
||||
import hn from './hn.json';
|
||||
import tphcm from './tphcm.json';
|
||||
import dn_camle from './dn/dn-camle.json';
|
||||
import dn_haichau from './dn/dn-haichau.json';
|
||||
import dn_hoavang from './dn/dn-hoavang.json';
|
||||
import dn_lienchieu from './dn/dn-lienchieu.json';
|
||||
import dn_nguhanhson from './dn/dn-nguhanhson.json';
|
||||
import dn_sontra from './dn/dn-sontra.json';
|
||||
import dn_thanhkhe from './dn/dn-thanhkhe.json';
|
||||
import dn from './dn/dn.json';
|
||||
import hn_bactuliem from './hn/hn-bactuliem.json';
|
||||
import hn_badinh from './hn/hn-badinh.json';
|
||||
import hn_bavi from './hn/hn-bavi.json';
|
||||
import hn_caugiay from './hn/hn-caugiay.json';
|
||||
import hn_chuongmy from './hn/hn-chuongmy.json';
|
||||
import hn_danphuong from './hn/hn-danphuong.json';
|
||||
import hn_donganh from './hn/hn-donganh.json';
|
||||
import hn_dongda from './hn/hn-dongda.json';
|
||||
import hn_gialam from './hn/hn-gialam.json';
|
||||
import hn_hadong from './hn/hn-hadong.json';
|
||||
import hn_haibatrung from './hn/hn-haibatrung.json';
|
||||
import hn_hoaiduc from './hn/hn-hoaiduc.json';
|
||||
import hn_hoangmai from './hn/hn-hoangmai.json';
|
||||
import hn_hoankiem from './hn/hn-hoankiem.json';
|
||||
import hn_longbien from './hn/hn-longbien.json';
|
||||
import hn_melinh from './hn/hn-melinh.json';
|
||||
import hn_myduc from './hn/hn-myduc.json';
|
||||
import hn_namtuliem from './hn/hn-namtuliem.json';
|
||||
import hn_phuctho from './hn/hn-phuctho.json';
|
||||
import hn_phuxuyen from './hn/hn-phuxuyen.json';
|
||||
import hn_quocoai from './hn/hn-quocoai.json';
|
||||
import hn_socson from './hn/hn-socson.json';
|
||||
import hn_sontay from './hn/hn-sontay.json';
|
||||
import hn_tayho from './hn/hn-tayho.json';
|
||||
import hn_thachthat from './hn/hn-thachthat.json';
|
||||
import hn_thanhoai from './hn/hn-thanhoai.json';
|
||||
import hn_thanhtri from './hn/hn-thanhtri.json';
|
||||
import hn_thanhxuan from './hn/hn-thanhxuan.json';
|
||||
import hn_thuongtin from './hn/hn-thuongtin.json';
|
||||
import hn_unghoa from './hn/hn-unghoa.json';
|
||||
import hn from './hn/hn.json';
|
||||
import dh from './la/dh.json';
|
||||
import la from './la/la.json';
|
||||
import dl from './ld/dl.json';
|
||||
import ld from './ld/ld.json';
|
||||
import tphcm_binhchanh from './tphcm/tphcm-binhchanh.json';
|
||||
import tphcm_binhtan from './tphcm/tphcm-binhtan.json';
|
||||
import tphcm_binhthanh from './tphcm/tphcm-binhthanh.json';
|
||||
import tphcm_cangio from './tphcm/tphcm-cangio.json';
|
||||
import tphcm_govap from './tphcm/tphcm-govap.json';
|
||||
import tphcm_hocmon from './tphcm/tphcm-hocmon.json';
|
||||
import tphcm_nhabe from './tphcm/tphcm-nhabe.json';
|
||||
import tphcm_phunhuan from './tphcm/tphcm-phunhuan.json';
|
||||
import tphcm_q1 from './tphcm/tphcm-q1.json';
|
||||
import tphcm_q10 from './tphcm/tphcm-q10.json';
|
||||
import tphcm_q11 from './tphcm/tphcm-q11.json';
|
||||
import tphcm_q12 from './tphcm/tphcm-q12.json';
|
||||
import tphcm_q3 from './tphcm/tphcm-q3.json';
|
||||
import tphcm_q4 from './tphcm/tphcm-q4.json';
|
||||
import tphcm_q5 from './tphcm/tphcm-q5.json';
|
||||
import tphcm_q6 from './tphcm/tphcm-q6.json';
|
||||
import tphcm_q7 from './tphcm/tphcm-q7.json';
|
||||
import tphcm_q8 from './tphcm/tphcm-q8.json';
|
||||
import tphcm_tanbinh from './tphcm/tphcm-tanbinh.json';
|
||||
import tphcm_tanphu from './tphcm/tphcm-tanphu.json';
|
||||
import tphcm_thuduc from './tphcm/tphcm-thuduc.json';
|
||||
import tphcm from './tphcm/tphcm.json';
|
||||
|
||||
export const CITY_BOUNDARIES = {
|
||||
DH: dh,
|
||||
DL: dl,
|
||||
DN: dn,
|
||||
HN: hn,
|
||||
TPHCM: tphcm,
|
||||
export const REGION_BOUNDARIES = {
|
||||
"DN-CAMLE": dn_camle,
|
||||
"DN-HAICHAU": dn_haichau,
|
||||
"DN-HOAVANG": dn_hoavang,
|
||||
"DN-LIENCHIEU": dn_lienchieu,
|
||||
"DN-NGUHANHSON": dn_nguhanhson,
|
||||
"DN-SONTRA": dn_sontra,
|
||||
"DN-THANHKHE": dn_thanhkhe,
|
||||
"DN": dn,
|
||||
"HN-BACTULIEM": hn_bactuliem,
|
||||
"HN-BADINH": hn_badinh,
|
||||
"HN-BAVI": hn_bavi,
|
||||
"HN-CAUGIAY": hn_caugiay,
|
||||
"HN-CHUONGMY": hn_chuongmy,
|
||||
"HN-DANPHUONG": hn_danphuong,
|
||||
"HN-DONGANH": hn_donganh,
|
||||
"HN-DONGDA": hn_dongda,
|
||||
"HN-GIALAM": hn_gialam,
|
||||
"HN-HADONG": hn_hadong,
|
||||
"HN-HAIBATRUNG": hn_haibatrung,
|
||||
"HN-HOAIDUC": hn_hoaiduc,
|
||||
"HN-HOANGMAI": hn_hoangmai,
|
||||
"HN-HOANKIEM": hn_hoankiem,
|
||||
"HN-LONGBIEN": hn_longbien,
|
||||
"HN-MELINH": hn_melinh,
|
||||
"HN-MYDUC": hn_myduc,
|
||||
"HN-NAMTULIEM": hn_namtuliem,
|
||||
"HN-PHUCTHO": hn_phuctho,
|
||||
"HN-PHUXUYEN": hn_phuxuyen,
|
||||
"HN-QUOCOAI": hn_quocoai,
|
||||
"HN-SOCSON": hn_socson,
|
||||
"HN-SONTAY": hn_sontay,
|
||||
"HN-TAYHO": hn_tayho,
|
||||
"HN-THACHTHAT": hn_thachthat,
|
||||
"HN-THANHOAI": hn_thanhoai,
|
||||
"HN-THANHTRI": hn_thanhtri,
|
||||
"HN-THANHXUAN": hn_thanhxuan,
|
||||
"HN-THUONGTIN": hn_thuongtin,
|
||||
"HN-UNGHOA": hn_unghoa,
|
||||
"HN": hn,
|
||||
"DH": dh,
|
||||
"LA": la,
|
||||
"DL": dl,
|
||||
"LD": ld,
|
||||
"TPHCM-BINHCHANH": tphcm_binhchanh,
|
||||
"TPHCM-BINHTAN": tphcm_binhtan,
|
||||
"TPHCM-BINHTHANH": tphcm_binhthanh,
|
||||
"TPHCM-CANGIO": tphcm_cangio,
|
||||
"TPHCM-GOVAP": tphcm_govap,
|
||||
"TPHCM-HOCMON": tphcm_hocmon,
|
||||
"TPHCM-NHABE": tphcm_nhabe,
|
||||
"TPHCM-PHUNHUAN": tphcm_phunhuan,
|
||||
"TPHCM-Q1": tphcm_q1,
|
||||
"TPHCM-Q10": tphcm_q10,
|
||||
"TPHCM-Q11": tphcm_q11,
|
||||
"TPHCM-Q12": tphcm_q12,
|
||||
"TPHCM-Q3": tphcm_q3,
|
||||
"TPHCM-Q4": tphcm_q4,
|
||||
"TPHCM-Q5": tphcm_q5,
|
||||
"TPHCM-Q6": tphcm_q6,
|
||||
"TPHCM-Q7": tphcm_q7,
|
||||
"TPHCM-Q8": tphcm_q8,
|
||||
"TPHCM-TANBINH": tphcm_tanbinh,
|
||||
"TPHCM-TANPHU": tphcm_tanphu,
|
||||
"TPHCM-THUDUC": tphcm_thuduc,
|
||||
"TPHCM": tphcm,
|
||||
};
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,542 @@
|
||||
{
|
||||
"type": "Feature",
|
||||
"properties": {
|
||||
"code": "TPHCM-BINHTAN",
|
||||
"name": "Binh Tan",
|
||||
"level": "district",
|
||||
"parent": "TPHCM",
|
||||
"basis": "pre-2025-merger extent, from OSM boundary/historic relations",
|
||||
"areaKm2": 52.3,
|
||||
"bbox": [
|
||||
106.55831,
|
||||
10.71096,
|
||||
106.62526,
|
||||
10.82978
|
||||
],
|
||||
"center": [
|
||||
10.76473,
|
||||
106.59754
|
||||
],
|
||||
"generatedAt": "2026-08-30T07:34:40.995Z",
|
||||
"osm": "relation/6909710"
|
||||
},
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [
|
||||
[
|
||||
[
|
||||
106.5583063,
|
||||
10.7389851
|
||||
],
|
||||
[
|
||||
106.5589135,
|
||||
10.7377293
|
||||
],
|
||||
[
|
||||
106.5587993,
|
||||
10.7364642
|
||||
],
|
||||
[
|
||||
106.5647473,
|
||||
10.7349651
|
||||
],
|
||||
[
|
||||
106.5684377,
|
||||
10.7342026
|
||||
],
|
||||
[
|
||||
106.5740733,
|
||||
10.7326896
|
||||
],
|
||||
[
|
||||
106.5740632,
|
||||
10.7316923
|
||||
],
|
||||
[
|
||||
106.5738048,
|
||||
10.7311645
|
||||
],
|
||||
[
|
||||
106.581323,
|
||||
10.7296064
|
||||
],
|
||||
[
|
||||
106.5902889,
|
||||
10.7269309
|
||||
],
|
||||
[
|
||||
106.5964821,
|
||||
10.7245498
|
||||
],
|
||||
[
|
||||
106.5997987,
|
||||
10.7226191
|
||||
],
|
||||
[
|
||||
106.6008731,
|
||||
10.7223375
|
||||
],
|
||||
[
|
||||
106.6001118,
|
||||
10.7179339
|
||||
],
|
||||
[
|
||||
106.6032775,
|
||||
10.7176517
|
||||
],
|
||||
[
|
||||
106.6034209,
|
||||
10.7174356
|
||||
],
|
||||
[
|
||||
106.6041352,
|
||||
10.7173439
|
||||
],
|
||||
[
|
||||
106.6038455,
|
||||
10.7162036
|
||||
],
|
||||
[
|
||||
106.6031142,
|
||||
10.7147539
|
||||
],
|
||||
[
|
||||
106.6037755,
|
||||
10.7134089
|
||||
],
|
||||
[
|
||||
106.6037719,
|
||||
10.7129428
|
||||
],
|
||||
[
|
||||
106.6033719,
|
||||
10.7117921
|
||||
],
|
||||
[
|
||||
106.6034657,
|
||||
10.7113586
|
||||
],
|
||||
[
|
||||
106.6038209,
|
||||
10.7109632
|
||||
],
|
||||
[
|
||||
106.6065404,
|
||||
10.7119137
|
||||
],
|
||||
[
|
||||
106.6160281,
|
||||
10.7142529
|
||||
],
|
||||
[
|
||||
106.615558,
|
||||
10.7150037
|
||||
],
|
||||
[
|
||||
106.6174368,
|
||||
10.7152573
|
||||
],
|
||||
[
|
||||
106.6182121,
|
||||
10.7160219
|
||||
],
|
||||
[
|
||||
106.6190341,
|
||||
10.7162048
|
||||
],
|
||||
[
|
||||
106.6210955,
|
||||
10.7342486
|
||||
],
|
||||
[
|
||||
106.6221717,
|
||||
10.7386272
|
||||
],
|
||||
[
|
||||
106.6220494,
|
||||
10.7387654
|
||||
],
|
||||
[
|
||||
106.6217503,
|
||||
10.7413933
|
||||
],
|
||||
[
|
||||
106.6231052,
|
||||
10.7447064
|
||||
],
|
||||
[
|
||||
106.6231201,
|
||||
10.7451195
|
||||
],
|
||||
[
|
||||
106.6237949,
|
||||
10.7455106
|
||||
],
|
||||
[
|
||||
106.6243672,
|
||||
10.7555135
|
||||
],
|
||||
[
|
||||
106.6252595,
|
||||
10.7575529
|
||||
],
|
||||
[
|
||||
106.6215893,
|
||||
10.7733635
|
||||
],
|
||||
[
|
||||
106.6214527,
|
||||
10.7762789
|
||||
],
|
||||
[
|
||||
106.6212335,
|
||||
10.7772689
|
||||
],
|
||||
[
|
||||
106.6184801,
|
||||
10.7853372
|
||||
],
|
||||
[
|
||||
106.6152161,
|
||||
10.7936116
|
||||
],
|
||||
[
|
||||
106.6136709,
|
||||
10.7967177
|
||||
],
|
||||
[
|
||||
106.6085415,
|
||||
10.8056515
|
||||
],
|
||||
[
|
||||
106.6074148,
|
||||
10.8080818
|
||||
],
|
||||
[
|
||||
106.6078887,
|
||||
10.8129234
|
||||
],
|
||||
[
|
||||
106.6076891,
|
||||
10.8130434
|
||||
],
|
||||
[
|
||||
106.6089918,
|
||||
10.8144877
|
||||
],
|
||||
[
|
||||
106.6097427,
|
||||
10.8148686
|
||||
],
|
||||
[
|
||||
106.6120436,
|
||||
10.8171105
|
||||
],
|
||||
[
|
||||
106.6131346,
|
||||
10.8178989
|
||||
],
|
||||
[
|
||||
106.6148834,
|
||||
10.8187404
|
||||
],
|
||||
[
|
||||
106.6160126,
|
||||
10.8196311
|
||||
],
|
||||
[
|
||||
106.6145173,
|
||||
10.8211242
|
||||
],
|
||||
[
|
||||
106.6116966,
|
||||
10.8217939
|
||||
],
|
||||
[
|
||||
106.6091561,
|
||||
10.8229681
|
||||
],
|
||||
[
|
||||
106.6076711,
|
||||
10.8232158
|
||||
],
|
||||
[
|
||||
106.6044301,
|
||||
10.8253075
|
||||
],
|
||||
[
|
||||
106.603879,
|
||||
10.8261239
|
||||
],
|
||||
[
|
||||
106.6030665,
|
||||
10.8262707
|
||||
],
|
||||
[
|
||||
106.6005353,
|
||||
10.8260964
|
||||
],
|
||||
[
|
||||
106.5974998,
|
||||
10.8270872
|
||||
],
|
||||
[
|
||||
106.5980789,
|
||||
10.8294632
|
||||
],
|
||||
[
|
||||
106.5952141,
|
||||
10.8297829
|
||||
],
|
||||
[
|
||||
106.5911133,
|
||||
10.8242549
|
||||
],
|
||||
[
|
||||
106.5872935,
|
||||
10.820401
|
||||
],
|
||||
[
|
||||
106.5819358,
|
||||
10.8156398
|
||||
],
|
||||
[
|
||||
106.5833678,
|
||||
10.8153151
|
||||
],
|
||||
[
|
||||
106.5837551,
|
||||
10.8137748
|
||||
],
|
||||
[
|
||||
106.5849332,
|
||||
10.8139978
|
||||
],
|
||||
[
|
||||
106.5860416,
|
||||
10.8131159
|
||||
],
|
||||
[
|
||||
106.5845228,
|
||||
10.806744
|
||||
],
|
||||
[
|
||||
106.5834754,
|
||||
10.8063554
|
||||
],
|
||||
[
|
||||
106.5834573,
|
||||
10.8041318
|
||||
],
|
||||
[
|
||||
106.5828647,
|
||||
10.7973024
|
||||
],
|
||||
[
|
||||
106.5847079,
|
||||
10.7972303
|
||||
],
|
||||
[
|
||||
106.584442,
|
||||
10.7957903
|
||||
],
|
||||
[
|
||||
106.5854817,
|
||||
10.7957245
|
||||
],
|
||||
[
|
||||
106.585422,
|
||||
10.7946679
|
||||
],
|
||||
[
|
||||
106.5846975,
|
||||
10.79472
|
||||
],
|
||||
[
|
||||
106.5844457,
|
||||
10.7907217
|
||||
],
|
||||
[
|
||||
106.5856057,
|
||||
10.7906255
|
||||
],
|
||||
[
|
||||
106.5856191,
|
||||
10.7894715
|
||||
],
|
||||
[
|
||||
106.586855,
|
||||
10.7892429
|
||||
],
|
||||
[
|
||||
106.5866657,
|
||||
10.7881643
|
||||
],
|
||||
[
|
||||
106.5890047,
|
||||
10.7873617
|
||||
],
|
||||
[
|
||||
106.5883195,
|
||||
10.7841637
|
||||
],
|
||||
[
|
||||
106.5880841,
|
||||
10.7818081
|
||||
],
|
||||
[
|
||||
106.588251,
|
||||
10.7807025
|
||||
],
|
||||
[
|
||||
106.5885763,
|
||||
10.7800362
|
||||
],
|
||||
[
|
||||
106.5888643,
|
||||
10.7799791
|
||||
],
|
||||
[
|
||||
106.5887643,
|
||||
10.7792498
|
||||
],
|
||||
[
|
||||
106.588447,
|
||||
10.7785341
|
||||
],
|
||||
[
|
||||
106.5868994,
|
||||
10.7762366
|
||||
],
|
||||
[
|
||||
106.5858582,
|
||||
10.7752865
|
||||
],
|
||||
[
|
||||
106.5846344,
|
||||
10.7749074
|
||||
],
|
||||
[
|
||||
106.5819769,
|
||||
10.7753388
|
||||
],
|
||||
[
|
||||
106.5808951,
|
||||
10.7750027
|
||||
],
|
||||
[
|
||||
106.5796579,
|
||||
10.773433
|
||||
],
|
||||
[
|
||||
106.5781174,
|
||||
10.7725154
|
||||
],
|
||||
[
|
||||
106.5773767,
|
||||
10.7718074
|
||||
],
|
||||
[
|
||||
106.5769184,
|
||||
10.7711749
|
||||
],
|
||||
[
|
||||
106.5767951,
|
||||
10.770119
|
||||
],
|
||||
[
|
||||
106.5758623,
|
||||
10.7692308
|
||||
],
|
||||
[
|
||||
106.5756274,
|
||||
10.7687664
|
||||
],
|
||||
[
|
||||
106.574981,
|
||||
10.768715
|
||||
],
|
||||
[
|
||||
106.5728974,
|
||||
10.7680499
|
||||
],
|
||||
[
|
||||
106.5722087,
|
||||
10.7681026
|
||||
],
|
||||
[
|
||||
106.5701648,
|
||||
10.7687251
|
||||
],
|
||||
[
|
||||
106.5677452,
|
||||
10.7629019
|
||||
],
|
||||
[
|
||||
106.5733723,
|
||||
10.7604104
|
||||
],
|
||||
[
|
||||
106.5731755,
|
||||
10.7599681
|
||||
],
|
||||
[
|
||||
106.5723128,
|
||||
10.7594051
|
||||
],
|
||||
[
|
||||
106.5699958,
|
||||
10.7572863
|
||||
],
|
||||
[
|
||||
106.5696015,
|
||||
10.7565962
|
||||
],
|
||||
[
|
||||
106.5675926,
|
||||
10.7545984
|
||||
],
|
||||
[
|
||||
106.5660459,
|
||||
10.7535329
|
||||
],
|
||||
[
|
||||
106.5642836,
|
||||
10.7526309
|
||||
],
|
||||
[
|
||||
106.5620899,
|
||||
10.750506
|
||||
],
|
||||
[
|
||||
106.5601611,
|
||||
10.749501
|
||||
],
|
||||
[
|
||||
106.5595326,
|
||||
10.7489622
|
||||
],
|
||||
[
|
||||
106.5591505,
|
||||
10.7479088
|
||||
],
|
||||
[
|
||||
106.5593662,
|
||||
10.745917
|
||||
],
|
||||
[
|
||||
106.5589041,
|
||||
10.7442158
|
||||
],
|
||||
[
|
||||
106.55928,
|
||||
10.7419274
|
||||
],
|
||||
[
|
||||
106.5583063,
|
||||
10.7389851
|
||||
]
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,682 @@
|
||||
{
|
||||
"type": "Feature",
|
||||
"properties": {
|
||||
"code": "TPHCM-BINHTHANH",
|
||||
"name": "Binh Thanh",
|
||||
"level": "district",
|
||||
"parent": "TPHCM",
|
||||
"basis": "pre-2025-merger extent, from OSM boundary/historic relations",
|
||||
"areaKm2": 20.9,
|
||||
"bbox": [
|
||||
106.68385,
|
||||
10.78534,
|
||||
106.75139,
|
||||
10.83917
|
||||
],
|
||||
"center": [
|
||||
10.81206,
|
||||
106.7162
|
||||
],
|
||||
"generatedAt": "2026-08-30T07:34:43.044Z",
|
||||
"osm": "relation/3797166"
|
||||
},
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [
|
||||
[
|
||||
[
|
||||
106.6838498,
|
||||
10.8071821
|
||||
],
|
||||
[
|
||||
106.6844495,
|
||||
10.806431
|
||||
],
|
||||
[
|
||||
106.6861537,
|
||||
10.8055045
|
||||
],
|
||||
[
|
||||
106.6870527,
|
||||
10.8052401
|
||||
],
|
||||
[
|
||||
106.6870269,
|
||||
10.8061811
|
||||
],
|
||||
[
|
||||
106.6885023,
|
||||
10.8056289
|
||||
],
|
||||
[
|
||||
106.6879147,
|
||||
10.8037448
|
||||
],
|
||||
[
|
||||
106.6909202,
|
||||
10.8033507
|
||||
],
|
||||
[
|
||||
106.6907951,
|
||||
10.8023955
|
||||
],
|
||||
[
|
||||
106.6921624,
|
||||
10.8022829
|
||||
],
|
||||
[
|
||||
106.6922298,
|
||||
10.8014996
|
||||
],
|
||||
[
|
||||
106.6920229,
|
||||
10.8013461
|
||||
],
|
||||
[
|
||||
106.6920237,
|
||||
10.8009754
|
||||
],
|
||||
[
|
||||
106.6923912,
|
||||
10.8010374
|
||||
],
|
||||
[
|
||||
106.692482,
|
||||
10.8004663
|
||||
],
|
||||
[
|
||||
106.6918172,
|
||||
10.8002909
|
||||
],
|
||||
[
|
||||
106.6918068,
|
||||
10.7989945
|
||||
],
|
||||
[
|
||||
106.6920921,
|
||||
10.7988295
|
||||
],
|
||||
[
|
||||
106.6919144,
|
||||
10.7987399
|
||||
],
|
||||
[
|
||||
106.6920432,
|
||||
10.7982558
|
||||
],
|
||||
[
|
||||
106.6918071,
|
||||
10.7982736
|
||||
],
|
||||
[
|
||||
106.6918366,
|
||||
10.7978257
|
||||
],
|
||||
[
|
||||
106.6920311,
|
||||
10.7977881
|
||||
],
|
||||
[
|
||||
106.691948,
|
||||
10.7960657
|
||||
],
|
||||
[
|
||||
106.6916087,
|
||||
10.7955247
|
||||
],
|
||||
[
|
||||
106.6926141,
|
||||
10.7948798
|
||||
],
|
||||
[
|
||||
106.6943564,
|
||||
10.7948981
|
||||
],
|
||||
[
|
||||
106.6945368,
|
||||
10.7945495
|
||||
],
|
||||
[
|
||||
106.6945432,
|
||||
10.7939072
|
||||
],
|
||||
[
|
||||
106.6948515,
|
||||
10.7935112
|
||||
],
|
||||
[
|
||||
106.6973197,
|
||||
10.7936608
|
||||
],
|
||||
[
|
||||
106.6994158,
|
||||
10.7935269
|
||||
],
|
||||
[
|
||||
106.7032122,
|
||||
10.7927802
|
||||
],
|
||||
[
|
||||
106.7055456,
|
||||
10.7921198
|
||||
],
|
||||
[
|
||||
106.7067238,
|
||||
10.7913608
|
||||
],
|
||||
[
|
||||
106.707618,
|
||||
10.7890035
|
||||
],
|
||||
[
|
||||
106.708727,
|
||||
10.7877518
|
||||
],
|
||||
[
|
||||
106.7094406,
|
||||
10.787511
|
||||
],
|
||||
[
|
||||
106.7118759,
|
||||
10.7877139
|
||||
],
|
||||
[
|
||||
106.7128377,
|
||||
10.7874826
|
||||
],
|
||||
[
|
||||
106.7140324,
|
||||
10.7866711
|
||||
],
|
||||
[
|
||||
106.7151954,
|
||||
10.7853396
|
||||
],
|
||||
[
|
||||
106.7192919,
|
||||
10.7862264
|
||||
],
|
||||
[
|
||||
106.7206793,
|
||||
10.7867917
|
||||
],
|
||||
[
|
||||
106.7218028,
|
||||
10.7875111
|
||||
],
|
||||
[
|
||||
106.723224,
|
||||
10.7886979
|
||||
],
|
||||
[
|
||||
106.7244302,
|
||||
10.7900456
|
||||
],
|
||||
[
|
||||
106.7261653,
|
||||
10.7927836
|
||||
],
|
||||
[
|
||||
106.7270983,
|
||||
10.7959549
|
||||
],
|
||||
[
|
||||
106.7272466,
|
||||
10.7983844
|
||||
],
|
||||
[
|
||||
106.7268654,
|
||||
10.8004651
|
||||
],
|
||||
[
|
||||
106.7259877,
|
||||
10.8021589
|
||||
],
|
||||
[
|
||||
106.723462,
|
||||
10.8058608
|
||||
],
|
||||
[
|
||||
106.7228123,
|
||||
10.8073519
|
||||
],
|
||||
[
|
||||
106.722333,
|
||||
10.8090333
|
||||
],
|
||||
[
|
||||
106.7224267,
|
||||
10.8107114
|
||||
],
|
||||
[
|
||||
106.7228741,
|
||||
10.8124258
|
||||
],
|
||||
[
|
||||
106.7243589,
|
||||
10.8153783
|
||||
],
|
||||
[
|
||||
106.7255858,
|
||||
10.8167173
|
||||
],
|
||||
[
|
||||
106.7280138,
|
||||
10.8185849
|
||||
],
|
||||
[
|
||||
106.7303724,
|
||||
10.8196141
|
||||
],
|
||||
[
|
||||
106.7313617,
|
||||
10.8197676
|
||||
],
|
||||
[
|
||||
106.7322408,
|
||||
10.8196769
|
||||
],
|
||||
[
|
||||
106.7334306,
|
||||
10.8190854
|
||||
],
|
||||
[
|
||||
106.7344711,
|
||||
10.8179926
|
||||
],
|
||||
[
|
||||
106.7352843,
|
||||
10.8162744
|
||||
],
|
||||
[
|
||||
106.7362051,
|
||||
10.8132775
|
||||
],
|
||||
[
|
||||
106.7366718,
|
||||
10.8122354
|
||||
],
|
||||
[
|
||||
106.7376293,
|
||||
10.8109063
|
||||
],
|
||||
[
|
||||
106.7388252,
|
||||
10.809827
|
||||
],
|
||||
[
|
||||
106.7413516,
|
||||
10.8087132
|
||||
],
|
||||
[
|
||||
106.7434736,
|
||||
10.8083111
|
||||
],
|
||||
[
|
||||
106.7457306,
|
||||
10.8085595
|
||||
],
|
||||
[
|
||||
106.7476562,
|
||||
10.8092622
|
||||
],
|
||||
[
|
||||
106.7490423,
|
||||
10.8101673
|
||||
],
|
||||
[
|
||||
106.7501148,
|
||||
10.81125
|
||||
],
|
||||
[
|
||||
106.7507739,
|
||||
10.8123065
|
||||
],
|
||||
[
|
||||
106.7511901,
|
||||
10.813658
|
||||
],
|
||||
[
|
||||
106.7513903,
|
||||
10.815905
|
||||
],
|
||||
[
|
||||
106.7513676,
|
||||
10.8254103
|
||||
],
|
||||
[
|
||||
106.7512491,
|
||||
10.8277419
|
||||
],
|
||||
[
|
||||
106.7510304,
|
||||
10.8287121
|
||||
],
|
||||
[
|
||||
106.7504861,
|
||||
10.8301155
|
||||
],
|
||||
[
|
||||
106.7496599,
|
||||
10.8314799
|
||||
],
|
||||
[
|
||||
106.7485551,
|
||||
10.8326695
|
||||
],
|
||||
[
|
||||
106.7474455,
|
||||
10.8334855
|
||||
],
|
||||
[
|
||||
106.7455807,
|
||||
10.8342562
|
||||
],
|
||||
[
|
||||
106.7421115,
|
||||
10.8353768
|
||||
],
|
||||
[
|
||||
106.7409936,
|
||||
10.8355447
|
||||
],
|
||||
[
|
||||
106.7397465,
|
||||
10.835484
|
||||
],
|
||||
[
|
||||
106.7386213,
|
||||
10.8350972
|
||||
],
|
||||
[
|
||||
106.7375377,
|
||||
10.8343068
|
||||
],
|
||||
[
|
||||
106.7361966,
|
||||
10.8326737
|
||||
],
|
||||
[
|
||||
106.7342118,
|
||||
10.8292277
|
||||
],
|
||||
[
|
||||
106.732656,
|
||||
10.8270463
|
||||
],
|
||||
[
|
||||
106.7303464,
|
||||
10.8247186
|
||||
],
|
||||
[
|
||||
106.7281085,
|
||||
10.8231766
|
||||
],
|
||||
[
|
||||
106.7247757,
|
||||
10.821672
|
||||
],
|
||||
[
|
||||
106.7228982,
|
||||
10.8210397
|
||||
],
|
||||
[
|
||||
106.7210385,
|
||||
10.8206229
|
||||
],
|
||||
[
|
||||
106.7182123,
|
||||
10.8204147
|
||||
],
|
||||
[
|
||||
106.7143435,
|
||||
10.820807
|
||||
],
|
||||
[
|
||||
106.7129396,
|
||||
10.821329
|
||||
],
|
||||
[
|
||||
106.7119364,
|
||||
10.821978
|
||||
],
|
||||
[
|
||||
106.7105547,
|
||||
10.8231684
|
||||
],
|
||||
[
|
||||
106.7098178,
|
||||
10.8241752
|
||||
],
|
||||
[
|
||||
106.7092919,
|
||||
10.8252431
|
||||
],
|
||||
[
|
||||
106.7090068,
|
||||
10.8262739
|
||||
],
|
||||
[
|
||||
106.7086986,
|
||||
10.831625
|
||||
],
|
||||
[
|
||||
106.708404,
|
||||
10.8331585
|
||||
],
|
||||
[
|
||||
106.7078823,
|
||||
10.8346562
|
||||
],
|
||||
[
|
||||
106.7071637,
|
||||
10.8358347
|
||||
],
|
||||
[
|
||||
106.7063053,
|
||||
10.8367562
|
||||
],
|
||||
[
|
||||
106.7027717,
|
||||
10.8391736
|
||||
],
|
||||
[
|
||||
106.7011125,
|
||||
10.8378423
|
||||
],
|
||||
[
|
||||
106.7007034,
|
||||
10.8359997
|
||||
],
|
||||
[
|
||||
106.7001075,
|
||||
10.8349207
|
||||
],
|
||||
[
|
||||
106.6991023,
|
||||
10.834231
|
||||
],
|
||||
[
|
||||
106.6962491,
|
||||
10.8329363
|
||||
],
|
||||
[
|
||||
106.6957335,
|
||||
10.8323163
|
||||
],
|
||||
[
|
||||
106.6953034,
|
||||
10.8311161
|
||||
],
|
||||
[
|
||||
106.6974301,
|
||||
10.8306409
|
||||
],
|
||||
[
|
||||
106.6997009,
|
||||
10.8303969
|
||||
],
|
||||
[
|
||||
106.6999564,
|
||||
10.8298916
|
||||
],
|
||||
[
|
||||
106.6995664,
|
||||
10.8286754
|
||||
],
|
||||
[
|
||||
106.7006982,
|
||||
10.8281913
|
||||
],
|
||||
[
|
||||
106.7010287,
|
||||
10.8270518
|
||||
],
|
||||
[
|
||||
106.7000631,
|
||||
10.826481
|
||||
],
|
||||
[
|
||||
106.6991888,
|
||||
10.8256756
|
||||
],
|
||||
[
|
||||
106.698626,
|
||||
10.8243173
|
||||
],
|
||||
[
|
||||
106.6986716,
|
||||
10.8232693
|
||||
],
|
||||
[
|
||||
106.6935055,
|
||||
10.8220061
|
||||
],
|
||||
[
|
||||
106.6940972,
|
||||
10.820544
|
||||
],
|
||||
[
|
||||
106.6934293,
|
||||
10.8203628
|
||||
],
|
||||
[
|
||||
106.693499,
|
||||
10.8196434
|
||||
],
|
||||
[
|
||||
106.6928673,
|
||||
10.8194525
|
||||
],
|
||||
[
|
||||
106.6929477,
|
||||
10.8189623
|
||||
],
|
||||
[
|
||||
106.6926758,
|
||||
10.8186794
|
||||
],
|
||||
[
|
||||
106.6927809,
|
||||
10.8183501
|
||||
],
|
||||
[
|
||||
106.6933041,
|
||||
10.8178443
|
||||
],
|
||||
[
|
||||
106.6920385,
|
||||
10.8175413
|
||||
],
|
||||
[
|
||||
106.6921748,
|
||||
10.8170096
|
||||
],
|
||||
[
|
||||
106.6914796,
|
||||
10.8168204
|
||||
],
|
||||
[
|
||||
106.6915419,
|
||||
10.8164331
|
||||
],
|
||||
[
|
||||
106.6912616,
|
||||
10.8162612
|
||||
],
|
||||
[
|
||||
106.6912771,
|
||||
10.8156763
|
||||
],
|
||||
[
|
||||
106.6906471,
|
||||
10.8155878
|
||||
],
|
||||
[
|
||||
106.6895474,
|
||||
10.8150506
|
||||
],
|
||||
[
|
||||
106.6895615,
|
||||
10.8148231
|
||||
],
|
||||
[
|
||||
106.6882735,
|
||||
10.8158347
|
||||
],
|
||||
[
|
||||
106.6877789,
|
||||
10.8150243
|
||||
],
|
||||
[
|
||||
106.6874156,
|
||||
10.8147335
|
||||
],
|
||||
[
|
||||
106.6852955,
|
||||
10.8140802
|
||||
],
|
||||
[
|
||||
106.6848203,
|
||||
10.8137187
|
||||
],
|
||||
[
|
||||
106.6847691,
|
||||
10.8134488
|
||||
],
|
||||
[
|
||||
106.6852775,
|
||||
10.8128091
|
||||
],
|
||||
[
|
||||
106.685224,
|
||||
10.8118182
|
||||
],
|
||||
[
|
||||
106.6851673,
|
||||
10.8115453
|
||||
],
|
||||
[
|
||||
106.6841974,
|
||||
10.8108206
|
||||
],
|
||||
[
|
||||
106.6840502,
|
||||
10.8103565
|
||||
],
|
||||
[
|
||||
106.6839355,
|
||||
10.8097139
|
||||
],
|
||||
[
|
||||
106.6842089,
|
||||
10.8087048
|
||||
],
|
||||
[
|
||||
106.6838498,
|
||||
10.8071821
|
||||
]
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,782 @@
|
||||
{
|
||||
"type": "Feature",
|
||||
"properties": {
|
||||
"code": "TPHCM-GOVAP",
|
||||
"name": "Go Vap",
|
||||
"level": "district",
|
||||
"parent": "TPHCM",
|
||||
"basis": "pre-2025-merger extent, from OSM boundary/historic relations",
|
||||
"areaKm2": 19.8,
|
||||
"bbox": [
|
||||
106.63194,
|
||||
10.80997,
|
||||
106.70106,
|
||||
10.86079
|
||||
],
|
||||
"center": [
|
||||
10.8383,
|
||||
106.66741
|
||||
],
|
||||
"generatedAt": "2026-08-30T07:34:44.646Z",
|
||||
"osm": "relation/6908316"
|
||||
},
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [
|
||||
[
|
||||
[
|
||||
106.6319429,
|
||||
10.8368091
|
||||
],
|
||||
[
|
||||
106.6320372,
|
||||
10.8364198
|
||||
],
|
||||
[
|
||||
106.632536,
|
||||
10.8357346
|
||||
],
|
||||
[
|
||||
106.6327724,
|
||||
10.8347746
|
||||
],
|
||||
[
|
||||
106.632358,
|
||||
10.8329517
|
||||
],
|
||||
[
|
||||
106.6339409,
|
||||
10.8323551
|
||||
],
|
||||
[
|
||||
106.6381996,
|
||||
10.8299334
|
||||
],
|
||||
[
|
||||
106.6407848,
|
||||
10.8292595
|
||||
],
|
||||
[
|
||||
106.6415057,
|
||||
10.828921
|
||||
],
|
||||
[
|
||||
106.6414136,
|
||||
10.8286759
|
||||
],
|
||||
[
|
||||
106.6425122,
|
||||
10.8281991
|
||||
],
|
||||
[
|
||||
106.646312,
|
||||
10.8372532
|
||||
],
|
||||
[
|
||||
106.6464825,
|
||||
10.8368769
|
||||
],
|
||||
[
|
||||
106.6475983,
|
||||
10.8366566
|
||||
],
|
||||
[
|
||||
106.6518955,
|
||||
10.8366144
|
||||
],
|
||||
[
|
||||
106.6519167,
|
||||
10.8368321
|
||||
],
|
||||
[
|
||||
106.6520627,
|
||||
10.8368183
|
||||
],
|
||||
[
|
||||
106.6520426,
|
||||
10.836613
|
||||
],
|
||||
[
|
||||
106.6555819,
|
||||
10.8360062
|
||||
],
|
||||
[
|
||||
106.655547,
|
||||
10.8358284
|
||||
],
|
||||
[
|
||||
106.6571005,
|
||||
10.8353982
|
||||
],
|
||||
[
|
||||
106.6592317,
|
||||
10.8332612
|
||||
],
|
||||
[
|
||||
106.6608741,
|
||||
10.8324051
|
||||
],
|
||||
[
|
||||
106.661986,
|
||||
10.8313787
|
||||
],
|
||||
[
|
||||
106.6627645,
|
||||
10.8312538
|
||||
],
|
||||
[
|
||||
106.6634298,
|
||||
10.8307011
|
||||
],
|
||||
[
|
||||
106.6639467,
|
||||
10.8306232
|
||||
],
|
||||
[
|
||||
106.6661223,
|
||||
10.8284392
|
||||
],
|
||||
[
|
||||
106.6680099,
|
||||
10.8283849
|
||||
],
|
||||
[
|
||||
106.670441,
|
||||
10.8271654
|
||||
],
|
||||
[
|
||||
106.6731419,
|
||||
10.8279436
|
||||
],
|
||||
[
|
||||
106.6734213,
|
||||
10.8277727
|
||||
],
|
||||
[
|
||||
106.6741447,
|
||||
10.8263647
|
||||
],
|
||||
[
|
||||
106.6744185,
|
||||
10.8261889
|
||||
],
|
||||
[
|
||||
106.6773501,
|
||||
10.8270259
|
||||
],
|
||||
[
|
||||
106.6778011,
|
||||
10.8269208
|
||||
],
|
||||
[
|
||||
106.6785859,
|
||||
10.826139
|
||||
],
|
||||
[
|
||||
106.678606,
|
||||
10.8256352
|
||||
],
|
||||
[
|
||||
106.6782975,
|
||||
10.825184
|
||||
],
|
||||
[
|
||||
106.6767593,
|
||||
10.8241335
|
||||
],
|
||||
[
|
||||
106.6753109,
|
||||
10.8236524
|
||||
],
|
||||
[
|
||||
106.6733203,
|
||||
10.8223634
|
||||
],
|
||||
[
|
||||
106.6748586,
|
||||
10.8183201
|
||||
],
|
||||
[
|
||||
106.6741726,
|
||||
10.8179961
|
||||
],
|
||||
[
|
||||
106.6756732,
|
||||
10.8163813
|
||||
],
|
||||
[
|
||||
106.6756901,
|
||||
10.8153569
|
||||
],
|
||||
[
|
||||
106.676116,
|
||||
10.8151054
|
||||
],
|
||||
[
|
||||
106.6760401,
|
||||
10.814857
|
||||
],
|
||||
[
|
||||
106.6749756,
|
||||
10.8151379
|
||||
],
|
||||
[
|
||||
106.6745478,
|
||||
10.8147342
|
||||
],
|
||||
[
|
||||
106.673668,
|
||||
10.8146291
|
||||
],
|
||||
[
|
||||
106.672783,
|
||||
10.8141411
|
||||
],
|
||||
[
|
||||
106.6760522,
|
||||
10.8112022
|
||||
],
|
||||
[
|
||||
106.6768594,
|
||||
10.8121494
|
||||
],
|
||||
[
|
||||
106.6782891,
|
||||
10.8112772
|
||||
],
|
||||
[
|
||||
106.6785299,
|
||||
10.8112773
|
||||
],
|
||||
[
|
||||
106.6785875,
|
||||
10.8120795
|
||||
],
|
||||
[
|
||||
106.6805932,
|
||||
10.8119445
|
||||
],
|
||||
[
|
||||
106.6805372,
|
||||
10.8118249
|
||||
],
|
||||
[
|
||||
106.6810199,
|
||||
10.8114975
|
||||
],
|
||||
[
|
||||
106.6807712,
|
||||
10.810994
|
||||
],
|
||||
[
|
||||
106.6809838,
|
||||
10.8108475
|
||||
],
|
||||
[
|
||||
106.6819604,
|
||||
10.8104847
|
||||
],
|
||||
[
|
||||
106.6821232,
|
||||
10.8108063
|
||||
],
|
||||
[
|
||||
106.6831105,
|
||||
10.8102266
|
||||
],
|
||||
[
|
||||
106.6839728,
|
||||
10.809969
|
||||
],
|
||||
[
|
||||
106.6841974,
|
||||
10.8108206
|
||||
],
|
||||
[
|
||||
106.6851918,
|
||||
10.8116365
|
||||
],
|
||||
[
|
||||
106.6852775,
|
||||
10.8128091
|
||||
],
|
||||
[
|
||||
106.6847691,
|
||||
10.8134488
|
||||
],
|
||||
[
|
||||
106.6848203,
|
||||
10.8137187
|
||||
],
|
||||
[
|
||||
106.6852955,
|
||||
10.8140802
|
||||
],
|
||||
[
|
||||
106.6874156,
|
||||
10.8147335
|
||||
],
|
||||
[
|
||||
106.6877789,
|
||||
10.8150243
|
||||
],
|
||||
[
|
||||
106.6882735,
|
||||
10.8158347
|
||||
],
|
||||
[
|
||||
106.6895615,
|
||||
10.8148231
|
||||
],
|
||||
[
|
||||
106.6895474,
|
||||
10.8150506
|
||||
],
|
||||
[
|
||||
106.6906471,
|
||||
10.8155878
|
||||
],
|
||||
[
|
||||
106.6912771,
|
||||
10.8156763
|
||||
],
|
||||
[
|
||||
106.6912616,
|
||||
10.8162612
|
||||
],
|
||||
[
|
||||
106.6915419,
|
||||
10.8164331
|
||||
],
|
||||
[
|
||||
106.6914796,
|
||||
10.8168204
|
||||
],
|
||||
[
|
||||
106.6921748,
|
||||
10.8170096
|
||||
],
|
||||
[
|
||||
106.6920385,
|
||||
10.8175413
|
||||
],
|
||||
[
|
||||
106.6933041,
|
||||
10.8178443
|
||||
],
|
||||
[
|
||||
106.6927809,
|
||||
10.8183501
|
||||
],
|
||||
[
|
||||
106.6926758,
|
||||
10.8186794
|
||||
],
|
||||
[
|
||||
106.6929477,
|
||||
10.8189623
|
||||
],
|
||||
[
|
||||
106.6928673,
|
||||
10.8194525
|
||||
],
|
||||
[
|
||||
106.693499,
|
||||
10.8196434
|
||||
],
|
||||
[
|
||||
106.6934293,
|
||||
10.8203628
|
||||
],
|
||||
[
|
||||
106.6940972,
|
||||
10.820544
|
||||
],
|
||||
[
|
||||
106.6935055,
|
||||
10.8220061
|
||||
],
|
||||
[
|
||||
106.6986716,
|
||||
10.8232693
|
||||
],
|
||||
[
|
||||
106.698626,
|
||||
10.8243173
|
||||
],
|
||||
[
|
||||
106.6991888,
|
||||
10.8256756
|
||||
],
|
||||
[
|
||||
106.7010554,
|
||||
10.8271337
|
||||
],
|
||||
[
|
||||
106.7006289,
|
||||
10.8282637
|
||||
],
|
||||
[
|
||||
106.6995664,
|
||||
10.8286754
|
||||
],
|
||||
[
|
||||
106.6999616,
|
||||
10.8300356
|
||||
],
|
||||
[
|
||||
106.699601,
|
||||
10.8304496
|
||||
],
|
||||
[
|
||||
106.6974301,
|
||||
10.8306409
|
||||
],
|
||||
[
|
||||
106.6953034,
|
||||
10.8311161
|
||||
],
|
||||
[
|
||||
106.6946272,
|
||||
10.8304278
|
||||
],
|
||||
[
|
||||
106.6938795,
|
||||
10.8301438
|
||||
],
|
||||
[
|
||||
106.6928652,
|
||||
10.8302484
|
||||
],
|
||||
[
|
||||
106.6922483,
|
||||
10.8306773
|
||||
],
|
||||
[
|
||||
106.6916976,
|
||||
10.8317902
|
||||
],
|
||||
[
|
||||
106.6906009,
|
||||
10.8363282
|
||||
],
|
||||
[
|
||||
106.6888654,
|
||||
10.8390805
|
||||
],
|
||||
[
|
||||
106.6887528,
|
||||
10.8406447
|
||||
],
|
||||
[
|
||||
106.6892805,
|
||||
10.8434082
|
||||
],
|
||||
[
|
||||
106.6888802,
|
||||
10.8448458
|
||||
],
|
||||
[
|
||||
106.6883069,
|
||||
10.845997
|
||||
],
|
||||
[
|
||||
106.6876423,
|
||||
10.8468103
|
||||
],
|
||||
[
|
||||
106.6868388,
|
||||
10.8472705
|
||||
],
|
||||
[
|
||||
106.6859324,
|
||||
10.8473958
|
||||
],
|
||||
[
|
||||
106.6842775,
|
||||
10.8470625
|
||||
],
|
||||
[
|
||||
106.6836123,
|
||||
10.8471514
|
||||
],
|
||||
[
|
||||
106.6831958,
|
||||
10.8473938
|
||||
],
|
||||
[
|
||||
106.6829689,
|
||||
10.847913
|
||||
],
|
||||
[
|
||||
106.6830417,
|
||||
10.8485442
|
||||
],
|
||||
[
|
||||
106.6844429,
|
||||
10.8506183
|
||||
],
|
||||
[
|
||||
106.6843408,
|
||||
10.8512314
|
||||
],
|
||||
[
|
||||
106.683898,
|
||||
10.8516093
|
||||
],
|
||||
[
|
||||
106.6830974,
|
||||
10.8517582
|
||||
],
|
||||
[
|
||||
106.6793057,
|
||||
10.8508052
|
||||
],
|
||||
[
|
||||
106.6758943,
|
||||
10.8520676
|
||||
],
|
||||
[
|
||||
106.6758078,
|
||||
10.8533005
|
||||
],
|
||||
[
|
||||
106.6750955,
|
||||
10.8548985
|
||||
],
|
||||
[
|
||||
106.675202,
|
||||
10.8568352
|
||||
],
|
||||
[
|
||||
106.6750963,
|
||||
10.8573703
|
||||
],
|
||||
[
|
||||
106.674247,
|
||||
10.8585881
|
||||
],
|
||||
[
|
||||
106.6734364,
|
||||
10.8602855
|
||||
],
|
||||
[
|
||||
106.6728044,
|
||||
10.8606907
|
||||
],
|
||||
[
|
||||
106.6708938,
|
||||
10.8597593
|
||||
],
|
||||
[
|
||||
106.6704229,
|
||||
10.8593775
|
||||
],
|
||||
[
|
||||
106.6700828,
|
||||
10.8587179
|
||||
],
|
||||
[
|
||||
106.6690449,
|
||||
10.858152
|
||||
],
|
||||
[
|
||||
106.6689809,
|
||||
10.858234
|
||||
],
|
||||
[
|
||||
106.667983,
|
||||
10.8575579
|
||||
],
|
||||
[
|
||||
106.667055,
|
||||
10.8560005
|
||||
],
|
||||
[
|
||||
106.6659111,
|
||||
10.8562877
|
||||
],
|
||||
[
|
||||
106.663415,
|
||||
10.8555675
|
||||
],
|
||||
[
|
||||
106.6626128,
|
||||
10.8555458
|
||||
],
|
||||
[
|
||||
106.6613851,
|
||||
10.8557929
|
||||
],
|
||||
[
|
||||
106.6588817,
|
||||
10.8578978
|
||||
],
|
||||
[
|
||||
106.6582527,
|
||||
10.8580289
|
||||
],
|
||||
[
|
||||
106.6573466,
|
||||
10.8577011
|
||||
],
|
||||
[
|
||||
106.6568062,
|
||||
10.8577868
|
||||
],
|
||||
[
|
||||
106.6555845,
|
||||
10.8588443
|
||||
],
|
||||
[
|
||||
106.6539414,
|
||||
10.8593362
|
||||
],
|
||||
[
|
||||
106.653078,
|
||||
10.860505
|
||||
],
|
||||
[
|
||||
106.6525657,
|
||||
10.8607922
|
||||
],
|
||||
[
|
||||
106.651062,
|
||||
10.8599934
|
||||
],
|
||||
[
|
||||
106.6500186,
|
||||
10.8591027
|
||||
],
|
||||
[
|
||||
106.648915,
|
||||
10.8589213
|
||||
],
|
||||
[
|
||||
106.6471422,
|
||||
10.8580699
|
||||
],
|
||||
[
|
||||
106.6465804,
|
||||
10.8563601
|
||||
],
|
||||
[
|
||||
106.6464955,
|
||||
10.8544578
|
||||
],
|
||||
[
|
||||
106.6463189,
|
||||
10.854034
|
||||
],
|
||||
[
|
||||
106.6459101,
|
||||
10.8540014
|
||||
],
|
||||
[
|
||||
106.6452943,
|
||||
10.8545083
|
||||
],
|
||||
[
|
||||
106.6448176,
|
||||
10.8544351
|
||||
],
|
||||
[
|
||||
106.6445246,
|
||||
10.8539282
|
||||
],
|
||||
[
|
||||
106.6444299,
|
||||
10.8529893
|
||||
],
|
||||
[
|
||||
106.6440861,
|
||||
10.8527104
|
||||
],
|
||||
[
|
||||
106.6433751,
|
||||
10.8525687
|
||||
],
|
||||
[
|
||||
106.6430296,
|
||||
10.8523306
|
||||
],
|
||||
[
|
||||
106.6428154,
|
||||
10.8509928
|
||||
],
|
||||
[
|
||||
106.6415378,
|
||||
10.8497153
|
||||
],
|
||||
[
|
||||
106.6404472,
|
||||
10.8493675
|
||||
],
|
||||
[
|
||||
106.6397425,
|
||||
10.8489265
|
||||
],
|
||||
[
|
||||
106.6361587,
|
||||
10.8461774
|
||||
],
|
||||
[
|
||||
106.6349054,
|
||||
10.8449022
|
||||
],
|
||||
[
|
||||
106.6339785,
|
||||
10.8452446
|
||||
],
|
||||
[
|
||||
106.6338082,
|
||||
10.8451471
|
||||
],
|
||||
[
|
||||
106.6336013,
|
||||
10.8431695
|
||||
],
|
||||
[
|
||||
106.6337206,
|
||||
10.8420353
|
||||
],
|
||||
[
|
||||
106.6335415,
|
||||
10.8411301
|
||||
],
|
||||
[
|
||||
106.6333839,
|
||||
10.8409378
|
||||
],
|
||||
[
|
||||
106.6322665,
|
||||
10.8405662
|
||||
],
|
||||
[
|
||||
106.6321173,
|
||||
10.8402495
|
||||
],
|
||||
[
|
||||
106.6322385,
|
||||
10.8392865
|
||||
],
|
||||
[
|
||||
106.6328525,
|
||||
10.8385697
|
||||
],
|
||||
[
|
||||
106.6330128,
|
||||
10.8380462
|
||||
],
|
||||
[
|
||||
106.6322279,
|
||||
10.8374089
|
||||
],
|
||||
[
|
||||
106.6319429,
|
||||
10.8368091
|
||||
]
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,706 @@
|
||||
{
|
||||
"type": "Feature",
|
||||
"properties": {
|
||||
"code": "TPHCM-HOCMON",
|
||||
"name": "Hoc Mon",
|
||||
"level": "district",
|
||||
"parent": "TPHCM",
|
||||
"basis": "pre-2025-merger extent, from OSM boundary/historic relations",
|
||||
"areaKm2": 109.6,
|
||||
"bbox": [
|
||||
106.49562,
|
||||
10.8261,
|
||||
106.69279,
|
||||
10.92934
|
||||
],
|
||||
"center": [
|
||||
10.88585,
|
||||
106.58983
|
||||
],
|
||||
"generatedAt": "2026-08-30T07:35:04.527Z",
|
||||
"osm": "relation/7157219"
|
||||
},
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [
|
||||
[
|
||||
[
|
||||
106.4956203,
|
||||
10.9208398
|
||||
],
|
||||
[
|
||||
106.5036803,
|
||||
10.9168867
|
||||
],
|
||||
[
|
||||
106.5315364,
|
||||
10.9026658
|
||||
],
|
||||
[
|
||||
106.5327461,
|
||||
10.9010902
|
||||
],
|
||||
[
|
||||
106.5328728,
|
||||
10.8999003
|
||||
],
|
||||
[
|
||||
106.5274982,
|
||||
10.8800642
|
||||
],
|
||||
[
|
||||
106.5265122,
|
||||
10.8769134
|
||||
],
|
||||
[
|
||||
106.5259106,
|
||||
10.8736439
|
||||
],
|
||||
[
|
||||
106.5348794,
|
||||
10.8718885
|
||||
],
|
||||
[
|
||||
106.5286784,
|
||||
10.8475074
|
||||
],
|
||||
[
|
||||
106.5448056,
|
||||
10.8430387
|
||||
],
|
||||
[
|
||||
106.5452237,
|
||||
10.8447865
|
||||
],
|
||||
[
|
||||
106.5559467,
|
||||
10.843056
|
||||
],
|
||||
[
|
||||
106.5574157,
|
||||
10.843335
|
||||
],
|
||||
[
|
||||
106.5575901,
|
||||
10.8438171
|
||||
],
|
||||
[
|
||||
106.5580595,
|
||||
10.8438105
|
||||
],
|
||||
[
|
||||
106.5580621,
|
||||
10.8440489
|
||||
],
|
||||
[
|
||||
106.5588051,
|
||||
10.8439067
|
||||
],
|
||||
[
|
||||
106.5596473,
|
||||
10.8442201
|
||||
],
|
||||
[
|
||||
106.5623711,
|
||||
10.8447312
|
||||
],
|
||||
[
|
||||
106.5626219,
|
||||
10.8429425
|
||||
],
|
||||
[
|
||||
106.56276,
|
||||
10.8426013
|
||||
],
|
||||
[
|
||||
106.5630779,
|
||||
10.8426027
|
||||
],
|
||||
[
|
||||
106.5632321,
|
||||
10.8413698
|
||||
],
|
||||
[
|
||||
106.5634996,
|
||||
10.8413988
|
||||
],
|
||||
[
|
||||
106.5645739,
|
||||
10.8407766
|
||||
],
|
||||
[
|
||||
106.5661785,
|
||||
10.8411024
|
||||
],
|
||||
[
|
||||
106.5692604,
|
||||
10.8428002
|
||||
],
|
||||
[
|
||||
106.5726882,
|
||||
10.8425934
|
||||
],
|
||||
[
|
||||
106.5727124,
|
||||
10.8427225
|
||||
],
|
||||
[
|
||||
106.5736277,
|
||||
10.8427469
|
||||
],
|
||||
[
|
||||
106.5757593,
|
||||
10.8426079
|
||||
],
|
||||
[
|
||||
106.5764735,
|
||||
10.8401142
|
||||
],
|
||||
[
|
||||
106.5765801,
|
||||
10.8384193
|
||||
],
|
||||
[
|
||||
106.5780835,
|
||||
10.8373047
|
||||
],
|
||||
[
|
||||
106.5790652,
|
||||
10.8355202
|
||||
],
|
||||
[
|
||||
106.5807844,
|
||||
10.8342176
|
||||
],
|
||||
[
|
||||
106.5819271,
|
||||
10.8328397
|
||||
],
|
||||
[
|
||||
106.5842106,
|
||||
10.8325466
|
||||
],
|
||||
[
|
||||
106.5851672,
|
||||
10.8327389
|
||||
],
|
||||
[
|
||||
106.5860186,
|
||||
10.8331682
|
||||
],
|
||||
[
|
||||
106.5878803,
|
||||
10.8327949
|
||||
],
|
||||
[
|
||||
106.5909468,
|
||||
10.8318376
|
||||
],
|
||||
[
|
||||
106.5923381,
|
||||
10.8303607
|
||||
],
|
||||
[
|
||||
106.5930231,
|
||||
10.8293998
|
||||
],
|
||||
[
|
||||
106.5945571,
|
||||
10.8298413
|
||||
],
|
||||
[
|
||||
106.5980789,
|
||||
10.8294632
|
||||
],
|
||||
[
|
||||
106.5974998,
|
||||
10.8270872
|
||||
],
|
||||
[
|
||||
106.6005353,
|
||||
10.8260964
|
||||
],
|
||||
[
|
||||
106.6032773,
|
||||
10.8262326
|
||||
],
|
||||
[
|
||||
106.603739,
|
||||
10.8282162
|
||||
],
|
||||
[
|
||||
106.6043611,
|
||||
10.8298733
|
||||
],
|
||||
[
|
||||
106.608031,
|
||||
10.8359612
|
||||
],
|
||||
[
|
||||
106.6088228,
|
||||
10.8368728
|
||||
],
|
||||
[
|
||||
106.6153254,
|
||||
10.8426844
|
||||
],
|
||||
[
|
||||
106.6072559,
|
||||
10.8552972
|
||||
],
|
||||
[
|
||||
106.612047,
|
||||
10.8625043
|
||||
],
|
||||
[
|
||||
106.6155117,
|
||||
10.8673445
|
||||
],
|
||||
[
|
||||
106.6151201,
|
||||
10.8698825
|
||||
],
|
||||
[
|
||||
106.6149189,
|
||||
10.8703369
|
||||
],
|
||||
[
|
||||
106.6194002,
|
||||
10.8711719
|
||||
],
|
||||
[
|
||||
106.6255941,
|
||||
10.8780112
|
||||
],
|
||||
[
|
||||
106.6279236,
|
||||
10.881226
|
||||
],
|
||||
[
|
||||
106.6253325,
|
||||
10.8829677
|
||||
],
|
||||
[
|
||||
106.6252145,
|
||||
10.8831995
|
||||
],
|
||||
[
|
||||
106.6286584,
|
||||
10.8858124
|
||||
],
|
||||
[
|
||||
106.6290661,
|
||||
10.8863076
|
||||
],
|
||||
[
|
||||
106.6294524,
|
||||
10.8868976
|
||||
],
|
||||
[
|
||||
106.6314908,
|
||||
10.8915122
|
||||
],
|
||||
[
|
||||
106.6321024,
|
||||
10.8917756
|
||||
],
|
||||
[
|
||||
106.6390225,
|
||||
10.8918177
|
||||
],
|
||||
[
|
||||
106.6409537,
|
||||
10.89206
|
||||
],
|
||||
[
|
||||
106.6425094,
|
||||
10.8924077
|
||||
],
|
||||
[
|
||||
106.6427776,
|
||||
10.8927764
|
||||
],
|
||||
[
|
||||
106.6428527,
|
||||
10.8934086
|
||||
],
|
||||
[
|
||||
106.6431209,
|
||||
10.8937457
|
||||
],
|
||||
[
|
||||
106.6435393,
|
||||
10.8939248
|
||||
],
|
||||
[
|
||||
106.6442903,
|
||||
10.8939459
|
||||
],
|
||||
[
|
||||
106.6451379,
|
||||
10.8942198
|
||||
],
|
||||
[
|
||||
106.6458782,
|
||||
10.8940828
|
||||
],
|
||||
[
|
||||
106.6468924,
|
||||
10.8945782
|
||||
],
|
||||
[
|
||||
106.6468517,
|
||||
10.8948581
|
||||
],
|
||||
[
|
||||
106.6485068,
|
||||
10.8950416
|
||||
],
|
||||
[
|
||||
106.6496333,
|
||||
10.8953576
|
||||
],
|
||||
[
|
||||
106.6514572,
|
||||
10.8949046
|
||||
],
|
||||
[
|
||||
106.6522703,
|
||||
10.8948819
|
||||
],
|
||||
[
|
||||
106.6524979,
|
||||
10.8943778
|
||||
],
|
||||
[
|
||||
106.6525039,
|
||||
10.8930746
|
||||
],
|
||||
[
|
||||
106.6531126,
|
||||
10.8923518
|
||||
],
|
||||
[
|
||||
106.6532259,
|
||||
10.8917401
|
||||
],
|
||||
[
|
||||
106.6544929,
|
||||
10.8920529
|
||||
],
|
||||
[
|
||||
106.6561492,
|
||||
10.8920807
|
||||
],
|
||||
[
|
||||
106.6575649,
|
||||
10.8924977
|
||||
],
|
||||
[
|
||||
106.6606086,
|
||||
10.8925672
|
||||
],
|
||||
[
|
||||
106.6623357,
|
||||
10.8929426
|
||||
],
|
||||
[
|
||||
106.6634046,
|
||||
10.8929634
|
||||
],
|
||||
[
|
||||
106.6650114,
|
||||
10.8924491
|
||||
],
|
||||
[
|
||||
106.6655352,
|
||||
10.8921015
|
||||
],
|
||||
[
|
||||
106.6672198,
|
||||
10.8917331
|
||||
],
|
||||
[
|
||||
106.6688833,
|
||||
10.8909129
|
||||
],
|
||||
[
|
||||
106.6692378,
|
||||
10.8939553
|
||||
],
|
||||
[
|
||||
106.6693551,
|
||||
10.8943597
|
||||
],
|
||||
[
|
||||
106.6696736,
|
||||
10.8941627
|
||||
],
|
||||
[
|
||||
106.6703254,
|
||||
10.8961665
|
||||
],
|
||||
[
|
||||
106.6707075,
|
||||
10.8990249
|
||||
],
|
||||
[
|
||||
106.6706343,
|
||||
10.9011647
|
||||
],
|
||||
[
|
||||
106.6718621,
|
||||
10.9015639
|
||||
],
|
||||
[
|
||||
106.6734395,
|
||||
10.9017156
|
||||
],
|
||||
[
|
||||
106.6773017,
|
||||
10.903033
|
||||
],
|
||||
[
|
||||
106.6796678,
|
||||
10.9036398
|
||||
],
|
||||
[
|
||||
106.6815461,
|
||||
10.8983942
|
||||
],
|
||||
[
|
||||
106.6857091,
|
||||
10.8999511
|
||||
],
|
||||
[
|
||||
106.6890428,
|
||||
10.9018194
|
||||
],
|
||||
[
|
||||
106.6905714,
|
||||
10.9032166
|
||||
],
|
||||
[
|
||||
106.6913926,
|
||||
10.9036957
|
||||
],
|
||||
[
|
||||
106.692787,
|
||||
10.9041992
|
||||
],
|
||||
[
|
||||
106.6909434,
|
||||
10.9090033
|
||||
],
|
||||
[
|
||||
106.6874412,
|
||||
10.9167793
|
||||
],
|
||||
[
|
||||
106.6850149,
|
||||
10.9208556
|
||||
],
|
||||
[
|
||||
106.6839973,
|
||||
10.9218784
|
||||
],
|
||||
[
|
||||
106.6826373,
|
||||
10.9227834
|
||||
],
|
||||
[
|
||||
106.6808778,
|
||||
10.9234567
|
||||
],
|
||||
[
|
||||
106.6788554,
|
||||
10.9234945
|
||||
],
|
||||
[
|
||||
106.6776795,
|
||||
10.9232493
|
||||
],
|
||||
[
|
||||
106.6705742,
|
||||
10.9210022
|
||||
],
|
||||
[
|
||||
106.6693926,
|
||||
10.9208134
|
||||
],
|
||||
[
|
||||
106.6682663,
|
||||
10.9208418
|
||||
],
|
||||
[
|
||||
106.6671637,
|
||||
10.9210689
|
||||
],
|
||||
[
|
||||
106.6628122,
|
||||
10.9226306
|
||||
],
|
||||
[
|
||||
106.6605079,
|
||||
10.9238589
|
||||
],
|
||||
[
|
||||
106.6572929,
|
||||
10.9258852
|
||||
],
|
||||
[
|
||||
106.6562291,
|
||||
10.9245722
|
||||
],
|
||||
[
|
||||
106.6561019,
|
||||
10.9229197
|
||||
],
|
||||
[
|
||||
106.6557168,
|
||||
10.9219588
|
||||
],
|
||||
[
|
||||
106.6540643,
|
||||
10.9199869
|
||||
],
|
||||
[
|
||||
106.653504,
|
||||
10.9196179
|
||||
],
|
||||
[
|
||||
106.6530076,
|
||||
10.9195807
|
||||
],
|
||||
[
|
||||
106.6512767,
|
||||
10.919991
|
||||
],
|
||||
[
|
||||
106.6487932,
|
||||
10.9196151
|
||||
],
|
||||
[
|
||||
106.6458819,
|
||||
10.919811
|
||||
],
|
||||
[
|
||||
106.6449013,
|
||||
10.9194176
|
||||
],
|
||||
[
|
||||
106.6437346,
|
||||
10.9183194
|
||||
],
|
||||
[
|
||||
106.6428957,
|
||||
10.9178005
|
||||
],
|
||||
[
|
||||
106.6385195,
|
||||
10.9161742
|
||||
],
|
||||
[
|
||||
106.6372313,
|
||||
10.9160528
|
||||
],
|
||||
[
|
||||
106.634626,
|
||||
10.9162123
|
||||
],
|
||||
[
|
||||
106.6302376,
|
||||
10.9162121
|
||||
],
|
||||
[
|
||||
106.625037,
|
||||
10.915281
|
||||
],
|
||||
[
|
||||
106.6220677,
|
||||
10.9144584
|
||||
],
|
||||
[
|
||||
106.6190913,
|
||||
10.9150295
|
||||
],
|
||||
[
|
||||
106.6004518,
|
||||
10.9177979
|
||||
],
|
||||
[
|
||||
106.6005741,
|
||||
10.9205142
|
||||
],
|
||||
[
|
||||
106.6011921,
|
||||
10.9230665
|
||||
],
|
||||
[
|
||||
106.5814577,
|
||||
10.929341
|
||||
],
|
||||
[
|
||||
106.581576,
|
||||
10.9261048
|
||||
],
|
||||
[
|
||||
106.5825949,
|
||||
10.9250367
|
||||
],
|
||||
[
|
||||
106.5830574,
|
||||
10.9232359
|
||||
],
|
||||
[
|
||||
106.5834653,
|
||||
10.9226478
|
||||
],
|
||||
[
|
||||
106.5836357,
|
||||
10.9217839
|
||||
],
|
||||
[
|
||||
106.5840904,
|
||||
10.9207526
|
||||
],
|
||||
[
|
||||
106.5841641,
|
||||
10.9196599
|
||||
],
|
||||
[
|
||||
106.5818751,
|
||||
10.9199554
|
||||
],
|
||||
[
|
||||
106.5795667,
|
||||
10.9200252
|
||||
],
|
||||
[
|
||||
106.5650412,
|
||||
10.9191267
|
||||
],
|
||||
[
|
||||
106.5637793,
|
||||
10.9186986
|
||||
],
|
||||
[
|
||||
106.5613747,
|
||||
10.9170763
|
||||
],
|
||||
[
|
||||
106.5370906,
|
||||
10.9159834
|
||||
],
|
||||
[
|
||||
106.5012395,
|
||||
10.9203104
|
||||
],
|
||||
[
|
||||
106.4956203,
|
||||
10.9208398
|
||||
]
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,486 @@
|
||||
{
|
||||
"type": "Feature",
|
||||
"properties": {
|
||||
"code": "TPHCM-PHUNHUAN",
|
||||
"name": "Phu Nhuan",
|
||||
"level": "district",
|
||||
"parent": "TPHCM",
|
||||
"basis": "pre-2025-merger extent, from OSM boundary/historic relations",
|
||||
"areaKm2": 4.9,
|
||||
"bbox": [
|
||||
106.66596,
|
||||
10.78791,
|
||||
106.69248,
|
||||
10.81414
|
||||
],
|
||||
"center": [
|
||||
10.80033,
|
||||
106.67817
|
||||
],
|
||||
"generatedAt": "2026-08-30T07:34:46.089Z",
|
||||
"osm": "relation/3851694"
|
||||
},
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [
|
||||
[
|
||||
[
|
||||
106.6659623,
|
||||
10.790869
|
||||
],
|
||||
[
|
||||
106.6667297,
|
||||
10.7903408
|
||||
],
|
||||
[
|
||||
106.6667098,
|
||||
10.7896271
|
||||
],
|
||||
[
|
||||
106.6669163,
|
||||
10.7894428
|
||||
],
|
||||
[
|
||||
106.6683212,
|
||||
10.7887499
|
||||
],
|
||||
[
|
||||
106.6707794,
|
||||
10.7879103
|
||||
],
|
||||
[
|
||||
106.6710336,
|
||||
10.7892987
|
||||
],
|
||||
[
|
||||
106.6719005,
|
||||
10.7893747
|
||||
],
|
||||
[
|
||||
106.672193,
|
||||
10.7895631
|
||||
],
|
||||
[
|
||||
106.6721772,
|
||||
10.7897854
|
||||
],
|
||||
[
|
||||
106.6737911,
|
||||
10.7899497
|
||||
],
|
||||
[
|
||||
106.6740013,
|
||||
10.7903561
|
||||
],
|
||||
[
|
||||
106.6746612,
|
||||
10.7900096
|
||||
],
|
||||
[
|
||||
106.6749739,
|
||||
10.7902903
|
||||
],
|
||||
[
|
||||
106.6754926,
|
||||
10.7902741
|
||||
],
|
||||
[
|
||||
106.6764704,
|
||||
10.7905835
|
||||
],
|
||||
[
|
||||
106.6767569,
|
||||
10.7904543
|
||||
],
|
||||
[
|
||||
106.6768404,
|
||||
10.7906094
|
||||
],
|
||||
[
|
||||
106.6777765,
|
||||
10.790904
|
||||
],
|
||||
[
|
||||
106.6778833,
|
||||
10.7907561
|
||||
],
|
||||
[
|
||||
106.6792834,
|
||||
10.79104
|
||||
],
|
||||
[
|
||||
106.6796614,
|
||||
10.7909694
|
||||
],
|
||||
[
|
||||
106.6801247,
|
||||
10.7912414
|
||||
],
|
||||
[
|
||||
106.6810562,
|
||||
10.7913798
|
||||
],
|
||||
[
|
||||
106.681669,
|
||||
10.7912391
|
||||
],
|
||||
[
|
||||
106.6822586,
|
||||
10.791826
|
||||
],
|
||||
[
|
||||
106.6835234,
|
||||
10.7924087
|
||||
],
|
||||
[
|
||||
106.6858743,
|
||||
10.79227
|
||||
],
|
||||
[
|
||||
106.6862779,
|
||||
10.792689
|
||||
],
|
||||
[
|
||||
106.6863946,
|
||||
10.7930891
|
||||
],
|
||||
[
|
||||
106.6851211,
|
||||
10.7944282
|
||||
],
|
||||
[
|
||||
106.6848646,
|
||||
10.7961678
|
||||
],
|
||||
[
|
||||
106.6849599,
|
||||
10.7965626
|
||||
],
|
||||
[
|
||||
106.6852732,
|
||||
10.7968688
|
||||
],
|
||||
[
|
||||
106.6857755,
|
||||
10.7969297
|
||||
],
|
||||
[
|
||||
106.6861233,
|
||||
10.7967384
|
||||
],
|
||||
[
|
||||
106.6869427,
|
||||
10.7951322
|
||||
],
|
||||
[
|
||||
106.6873877,
|
||||
10.7948627
|
||||
],
|
||||
[
|
||||
106.6894209,
|
||||
10.7956353
|
||||
],
|
||||
[
|
||||
106.6907597,
|
||||
10.7957902
|
||||
],
|
||||
[
|
||||
106.6916087,
|
||||
10.7955247
|
||||
],
|
||||
[
|
||||
106.6919171,
|
||||
10.7959194
|
||||
],
|
||||
[
|
||||
106.6920311,
|
||||
10.7977881
|
||||
],
|
||||
[
|
||||
106.6918366,
|
||||
10.7978257
|
||||
],
|
||||
[
|
||||
106.6918071,
|
||||
10.7982736
|
||||
],
|
||||
[
|
||||
106.6920432,
|
||||
10.7982558
|
||||
],
|
||||
[
|
||||
106.6919144,
|
||||
10.7987399
|
||||
],
|
||||
[
|
||||
106.6920921,
|
||||
10.7988295
|
||||
],
|
||||
[
|
||||
106.6918068,
|
||||
10.7989945
|
||||
],
|
||||
[
|
||||
106.6918172,
|
||||
10.8002909
|
||||
],
|
||||
[
|
||||
106.692482,
|
||||
10.8004663
|
||||
],
|
||||
[
|
||||
106.6923912,
|
||||
10.8010374
|
||||
],
|
||||
[
|
||||
106.6920237,
|
||||
10.8009754
|
||||
],
|
||||
[
|
||||
106.6920229,
|
||||
10.8013461
|
||||
],
|
||||
[
|
||||
106.6922298,
|
||||
10.8014996
|
||||
],
|
||||
[
|
||||
106.6921624,
|
||||
10.8022829
|
||||
],
|
||||
[
|
||||
106.6907951,
|
||||
10.8023955
|
||||
],
|
||||
[
|
||||
106.6909202,
|
||||
10.8033507
|
||||
],
|
||||
[
|
||||
106.6879147,
|
||||
10.8037448
|
||||
],
|
||||
[
|
||||
106.6885023,
|
||||
10.8056289
|
||||
],
|
||||
[
|
||||
106.6870269,
|
||||
10.8061811
|
||||
],
|
||||
[
|
||||
106.6870527,
|
||||
10.8052401
|
||||
],
|
||||
[
|
||||
106.6860504,
|
||||
10.805546
|
||||
],
|
||||
[
|
||||
106.6843367,
|
||||
10.8065188
|
||||
],
|
||||
[
|
||||
106.6838498,
|
||||
10.8071821
|
||||
],
|
||||
[
|
||||
106.6842089,
|
||||
10.8087048
|
||||
],
|
||||
[
|
||||
106.6839728,
|
||||
10.809969
|
||||
],
|
||||
[
|
||||
106.6831105,
|
||||
10.8102266
|
||||
],
|
||||
[
|
||||
106.6821232,
|
||||
10.8108063
|
||||
],
|
||||
[
|
||||
106.6819604,
|
||||
10.8104847
|
||||
],
|
||||
[
|
||||
106.6809838,
|
||||
10.8108475
|
||||
],
|
||||
[
|
||||
106.6807712,
|
||||
10.810994
|
||||
],
|
||||
[
|
||||
106.6810199,
|
||||
10.8114975
|
||||
],
|
||||
[
|
||||
106.6805372,
|
||||
10.8118249
|
||||
],
|
||||
[
|
||||
106.6805932,
|
||||
10.8119445
|
||||
],
|
||||
[
|
||||
106.6785875,
|
||||
10.8120795
|
||||
],
|
||||
[
|
||||
106.6785299,
|
||||
10.8112773
|
||||
],
|
||||
[
|
||||
106.6782891,
|
||||
10.8112772
|
||||
],
|
||||
[
|
||||
106.6768594,
|
||||
10.8121494
|
||||
],
|
||||
[
|
||||
106.6760522,
|
||||
10.8112022
|
||||
],
|
||||
[
|
||||
106.672783,
|
||||
10.8141411
|
||||
],
|
||||
[
|
||||
106.6723028,
|
||||
10.8135789
|
||||
],
|
||||
[
|
||||
106.6720607,
|
||||
10.8125837
|
||||
],
|
||||
[
|
||||
106.6721512,
|
||||
10.8109242
|
||||
],
|
||||
[
|
||||
106.672404,
|
||||
10.8106675
|
||||
],
|
||||
[
|
||||
106.671651,
|
||||
10.8094077
|
||||
],
|
||||
[
|
||||
106.6707236,
|
||||
10.8098496
|
||||
],
|
||||
[
|
||||
106.6706718,
|
||||
10.8087065
|
||||
],
|
||||
[
|
||||
106.6710368,
|
||||
10.8086713
|
||||
],
|
||||
[
|
||||
106.6710395,
|
||||
10.8078723
|
||||
],
|
||||
[
|
||||
106.6706965,
|
||||
10.8078137
|
||||
],
|
||||
[
|
||||
106.6706774,
|
||||
10.807653
|
||||
],
|
||||
[
|
||||
106.6699978,
|
||||
10.8077159
|
||||
],
|
||||
[
|
||||
106.6699015,
|
||||
10.8068995
|
||||
],
|
||||
[
|
||||
106.6694258,
|
||||
10.8066736
|
||||
],
|
||||
[
|
||||
106.6690519,
|
||||
10.803317
|
||||
],
|
||||
[
|
||||
106.6686228,
|
||||
10.8033104
|
||||
],
|
||||
[
|
||||
106.668137,
|
||||
10.8027002
|
||||
],
|
||||
[
|
||||
106.6673729,
|
||||
10.8005322
|
||||
],
|
||||
[
|
||||
106.6675849,
|
||||
10.8001992
|
||||
],
|
||||
[
|
||||
106.6698792,
|
||||
10.7989467
|
||||
],
|
||||
[
|
||||
106.669816,
|
||||
10.7986179
|
||||
],
|
||||
[
|
||||
106.6691093,
|
||||
10.7988861
|
||||
],
|
||||
[
|
||||
106.6688635,
|
||||
10.7986978
|
||||
],
|
||||
[
|
||||
106.6688541,
|
||||
10.798431
|
||||
],
|
||||
[
|
||||
106.669058,
|
||||
10.7984185
|
||||
],
|
||||
[
|
||||
106.6690275,
|
||||
10.7981784
|
||||
],
|
||||
[
|
||||
106.6684739,
|
||||
10.798224
|
||||
],
|
||||
[
|
||||
106.6683834,
|
||||
10.797225
|
||||
],
|
||||
[
|
||||
106.668547,
|
||||
10.7972171
|
||||
],
|
||||
[
|
||||
106.66854,
|
||||
10.7970376
|
||||
],
|
||||
[
|
||||
106.6682731,
|
||||
10.7942846
|
||||
],
|
||||
[
|
||||
106.6684519,
|
||||
10.7941512
|
||||
],
|
||||
[
|
||||
106.6659623,
|
||||
10.790869
|
||||
]
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
{
|
||||
"type": "Feature",
|
||||
"properties": {
|
||||
"code": "TPHCM-Q1",
|
||||
"name": "District 1",
|
||||
"level": "district",
|
||||
"parent": "TPHCM",
|
||||
"basis": "pre-2025-merger extent, from OSM boundary/historic relations",
|
||||
"areaKm2": 7.7,
|
||||
"bbox": [
|
||||
106.68169,
|
||||
10.75352,
|
||||
106.7152,
|
||||
10.79693
|
||||
],
|
||||
"center": [
|
||||
10.77622,
|
||||
106.69715
|
||||
],
|
||||
"generatedAt": "2026-08-30T07:34:23.479Z",
|
||||
"osm": "relation/2587287"
|
||||
},
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [
|
||||
[
|
||||
[
|
||||
106.6816889,
|
||||
10.7654323
|
||||
],
|
||||
[
|
||||
106.6854624,
|
||||
10.7559828
|
||||
],
|
||||
[
|
||||
106.6872747,
|
||||
10.753519
|
||||
],
|
||||
[
|
||||
106.6916093,
|
||||
10.7575954
|
||||
],
|
||||
[
|
||||
106.694486,
|
||||
10.7592226
|
||||
],
|
||||
[
|
||||
106.6959129,
|
||||
10.7603701
|
||||
],
|
||||
[
|
||||
106.6982152,
|
||||
10.7633005
|
||||
],
|
||||
[
|
||||
106.7012074,
|
||||
10.7653499
|
||||
],
|
||||
[
|
||||
106.7035693,
|
||||
10.7680314
|
||||
],
|
||||
[
|
||||
106.7056291,
|
||||
10.7689242
|
||||
],
|
||||
[
|
||||
106.7082991,
|
||||
10.7697601
|
||||
],
|
||||
[
|
||||
106.7080073,
|
||||
10.7722411
|
||||
],
|
||||
[
|
||||
106.708136,
|
||||
10.7751802
|
||||
],
|
||||
[
|
||||
106.7085436,
|
||||
10.7772914
|
||||
],
|
||||
[
|
||||
106.7092598,
|
||||
10.7793384
|
||||
],
|
||||
[
|
||||
106.7103796,
|
||||
10.7815073
|
||||
],
|
||||
[
|
||||
106.7113623,
|
||||
10.7827581
|
||||
],
|
||||
[
|
||||
106.7131932,
|
||||
10.784281
|
||||
],
|
||||
[
|
||||
106.7151954,
|
||||
10.7853396
|
||||
],
|
||||
[
|
||||
106.7140324,
|
||||
10.7866711
|
||||
],
|
||||
[
|
||||
106.7128377,
|
||||
10.7874826
|
||||
],
|
||||
[
|
||||
106.7118759,
|
||||
10.7877139
|
||||
],
|
||||
[
|
||||
106.7094406,
|
||||
10.787511
|
||||
],
|
||||
[
|
||||
106.708727,
|
||||
10.7877518
|
||||
],
|
||||
[
|
||||
106.707618,
|
||||
10.7890035
|
||||
],
|
||||
[
|
||||
106.7067238,
|
||||
10.7913608
|
||||
],
|
||||
[
|
||||
106.7055456,
|
||||
10.7921198
|
||||
],
|
||||
[
|
||||
106.7032122,
|
||||
10.7927802
|
||||
],
|
||||
[
|
||||
106.6994158,
|
||||
10.7935269
|
||||
],
|
||||
[
|
||||
106.6973197,
|
||||
10.7936608
|
||||
],
|
||||
[
|
||||
106.6950206,
|
||||
10.7934546
|
||||
],
|
||||
[
|
||||
106.6946043,
|
||||
10.7937444
|
||||
],
|
||||
[
|
||||
106.6945368,
|
||||
10.7945495
|
||||
],
|
||||
[
|
||||
106.6943564,
|
||||
10.7948981
|
||||
],
|
||||
[
|
||||
106.6926141,
|
||||
10.7948798
|
||||
],
|
||||
[
|
||||
106.6909561,
|
||||
10.7957797
|
||||
],
|
||||
[
|
||||
106.689155,
|
||||
10.7955932
|
||||
],
|
||||
[
|
||||
106.6873877,
|
||||
10.7948627
|
||||
],
|
||||
[
|
||||
106.6869427,
|
||||
10.7951322
|
||||
],
|
||||
[
|
||||
106.6861233,
|
||||
10.7967384
|
||||
],
|
||||
[
|
||||
106.6857755,
|
||||
10.7969297
|
||||
],
|
||||
[
|
||||
106.6852732,
|
||||
10.7968688
|
||||
],
|
||||
[
|
||||
106.6849599,
|
||||
10.7965626
|
||||
],
|
||||
[
|
||||
106.6848646,
|
||||
10.7961678
|
||||
],
|
||||
[
|
||||
106.6850667,
|
||||
10.7946235
|
||||
],
|
||||
[
|
||||
106.685342,
|
||||
10.7941097
|
||||
],
|
||||
[
|
||||
106.6863497,
|
||||
10.7932013
|
||||
],
|
||||
[
|
||||
106.6863825,
|
||||
10.7928942
|
||||
],
|
||||
[
|
||||
106.6861498,
|
||||
10.7925061
|
||||
],
|
||||
[
|
||||
106.6981274,
|
||||
10.7828849
|
||||
],
|
||||
[
|
||||
106.6816889,
|
||||
10.7654323
|
||||
]
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
{
|
||||
"type": "Feature",
|
||||
"properties": {
|
||||
"code": "TPHCM-Q10",
|
||||
"name": "District 10",
|
||||
"level": "district",
|
||||
"parent": "TPHCM",
|
||||
"basis": "pre-2025-merger extent, from OSM boundary/historic relations",
|
||||
"areaKm2": 5.7,
|
||||
"bbox": [
|
||||
106.65613,
|
||||
10.75843,
|
||||
106.6828,
|
||||
10.78693
|
||||
],
|
||||
"center": [
|
||||
10.77237,
|
||||
106.66826
|
||||
],
|
||||
"generatedAt": "2026-08-30T07:34:36.038Z",
|
||||
"osm": "relation/6228121"
|
||||
},
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [
|
||||
[
|
||||
[
|
||||
106.6561318,
|
||||
10.7777457
|
||||
],
|
||||
[
|
||||
106.661562,
|
||||
10.7584316
|
||||
],
|
||||
[
|
||||
106.6747019,
|
||||
10.7610637
|
||||
],
|
||||
[
|
||||
106.6812069,
|
||||
10.7652911
|
||||
],
|
||||
[
|
||||
106.6816889,
|
||||
10.7654323
|
||||
],
|
||||
[
|
||||
106.6744365,
|
||||
10.7676889
|
||||
],
|
||||
[
|
||||
106.6756419,
|
||||
10.7693963
|
||||
],
|
||||
[
|
||||
106.6776153,
|
||||
10.7715083
|
||||
],
|
||||
[
|
||||
106.6827966,
|
||||
10.7758944
|
||||
],
|
||||
[
|
||||
106.6816674,
|
||||
10.7778253
|
||||
],
|
||||
[
|
||||
106.6646464,
|
||||
10.7869335
|
||||
],
|
||||
[
|
||||
106.6561318,
|
||||
10.7777457
|
||||
]
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
{
|
||||
"type": "Feature",
|
||||
"properties": {
|
||||
"code": "TPHCM-Q11",
|
||||
"name": "District 11",
|
||||
"level": "district",
|
||||
"parent": "TPHCM",
|
||||
"basis": "pre-2025-merger extent, from OSM boundary/historic relations",
|
||||
"areaKm2": 5.2,
|
||||
"bbox": [
|
||||
106.6342,
|
||||
10.7534,
|
||||
106.66156,
|
||||
10.77845
|
||||
],
|
||||
"center": [
|
||||
10.76412,
|
||||
106.64773
|
||||
],
|
||||
"generatedAt": "2026-08-30T07:34:37.523Z",
|
||||
"osm": "relation/6846181"
|
||||
},
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [
|
||||
[
|
||||
[
|
||||
106.6342046,
|
||||
10.758974
|
||||
],
|
||||
[
|
||||
106.6374635,
|
||||
10.7553954
|
||||
],
|
||||
[
|
||||
106.6376157,
|
||||
10.7546647
|
||||
],
|
||||
[
|
||||
106.6384948,
|
||||
10.7546983
|
||||
],
|
||||
[
|
||||
106.6507413,
|
||||
10.7534041
|
||||
],
|
||||
[
|
||||
106.6510904,
|
||||
10.7563386
|
||||
],
|
||||
[
|
||||
106.661562,
|
||||
10.7584316
|
||||
],
|
||||
[
|
||||
106.6559493,
|
||||
10.7784506
|
||||
],
|
||||
[
|
||||
106.6533108,
|
||||
10.7756279
|
||||
],
|
||||
[
|
||||
106.6525131,
|
||||
10.7693077
|
||||
],
|
||||
[
|
||||
106.6523283,
|
||||
10.7689114
|
||||
],
|
||||
[
|
||||
106.6518883,
|
||||
10.7691722
|
||||
],
|
||||
[
|
||||
106.6476803,
|
||||
10.7752257
|
||||
],
|
||||
[
|
||||
106.6453628,
|
||||
10.7752275
|
||||
],
|
||||
[
|
||||
106.6442952,
|
||||
10.7749853
|
||||
],
|
||||
[
|
||||
106.6438671,
|
||||
10.774622
|
||||
],
|
||||
[
|
||||
106.6421239,
|
||||
10.7721479
|
||||
],
|
||||
[
|
||||
106.6415908,
|
||||
10.7725649
|
||||
],
|
||||
[
|
||||
106.6392536,
|
||||
10.7704387
|
||||
],
|
||||
[
|
||||
106.6377246,
|
||||
10.7693731
|
||||
],
|
||||
[
|
||||
106.6376048,
|
||||
10.7689823
|
||||
],
|
||||
[
|
||||
106.6356122,
|
||||
10.7695551
|
||||
],
|
||||
[
|
||||
106.6357269,
|
||||
10.7627551
|
||||
],
|
||||
[
|
||||
106.6350891,
|
||||
10.7600777
|
||||
],
|
||||
[
|
||||
106.6342046,
|
||||
10.758974
|
||||
]
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,914 @@
|
||||
{
|
||||
"type": "Feature",
|
||||
"properties": {
|
||||
"code": "TPHCM-Q12",
|
||||
"name": "District 12",
|
||||
"level": "district",
|
||||
"parent": "TPHCM",
|
||||
"basis": "pre-2025-merger extent, from OSM boundary/historic relations",
|
||||
"areaKm2": 52.9,
|
||||
"bbox": [
|
||||
106.60328,
|
||||
10.81861,
|
||||
106.7182,
|
||||
10.9042
|
||||
],
|
||||
"center": [
|
||||
10.86453,
|
||||
106.65697
|
||||
],
|
||||
"generatedAt": "2026-08-30T07:34:39.203Z",
|
||||
"osm": "relation/6923167"
|
||||
},
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [
|
||||
[
|
||||
[
|
||||
106.6032773,
|
||||
10.8262326
|
||||
],
|
||||
[
|
||||
106.603879,
|
||||
10.8261239
|
||||
],
|
||||
[
|
||||
106.6044301,
|
||||
10.8253075
|
||||
],
|
||||
[
|
||||
106.6076711,
|
||||
10.8232158
|
||||
],
|
||||
[
|
||||
106.6091561,
|
||||
10.8229681
|
||||
],
|
||||
[
|
||||
106.6116966,
|
||||
10.8217939
|
||||
],
|
||||
[
|
||||
106.6145173,
|
||||
10.8211242
|
||||
],
|
||||
[
|
||||
106.6161843,
|
||||
10.8195547
|
||||
],
|
||||
[
|
||||
106.6177833,
|
||||
10.8196421
|
||||
],
|
||||
[
|
||||
106.6191787,
|
||||
10.8187331
|
||||
],
|
||||
[
|
||||
106.6196485,
|
||||
10.8186053
|
||||
],
|
||||
[
|
||||
106.6214038,
|
||||
10.8188256
|
||||
],
|
||||
[
|
||||
106.623437,
|
||||
10.8195106
|
||||
],
|
||||
[
|
||||
106.6247915,
|
||||
10.8208015
|
||||
],
|
||||
[
|
||||
106.6291063,
|
||||
10.826186
|
||||
],
|
||||
[
|
||||
106.6301022,
|
||||
10.8275906
|
||||
],
|
||||
[
|
||||
106.6313754,
|
||||
10.8302412
|
||||
],
|
||||
[
|
||||
106.6320898,
|
||||
10.8320138
|
||||
],
|
||||
[
|
||||
106.6327845,
|
||||
10.8346429
|
||||
],
|
||||
[
|
||||
106.632536,
|
||||
10.8357346
|
||||
],
|
||||
[
|
||||
106.6320372,
|
||||
10.8364198
|
||||
],
|
||||
[
|
||||
106.6319429,
|
||||
10.8368091
|
||||
],
|
||||
[
|
||||
106.6322279,
|
||||
10.8374089
|
||||
],
|
||||
[
|
||||
106.6328817,
|
||||
10.837849
|
||||
],
|
||||
[
|
||||
106.6330171,
|
||||
10.8381117
|
||||
],
|
||||
[
|
||||
106.6328525,
|
||||
10.8385697
|
||||
],
|
||||
[
|
||||
106.6322385,
|
||||
10.8392865
|
||||
],
|
||||
[
|
||||
106.6321392,
|
||||
10.840406
|
||||
],
|
||||
[
|
||||
106.6335415,
|
||||
10.8411301
|
||||
],
|
||||
[
|
||||
106.6337206,
|
||||
10.8420353
|
||||
],
|
||||
[
|
||||
106.6336013,
|
||||
10.8431695
|
||||
],
|
||||
[
|
||||
106.6337534,
|
||||
10.8450704
|
||||
],
|
||||
[
|
||||
106.6339785,
|
||||
10.8452446
|
||||
],
|
||||
[
|
||||
106.6349054,
|
||||
10.8449022
|
||||
],
|
||||
[
|
||||
106.6361587,
|
||||
10.8461774
|
||||
],
|
||||
[
|
||||
106.6397425,
|
||||
10.8489265
|
||||
],
|
||||
[
|
||||
106.6404472,
|
||||
10.8493675
|
||||
],
|
||||
[
|
||||
106.6415378,
|
||||
10.8497153
|
||||
],
|
||||
[
|
||||
106.6428154,
|
||||
10.8509928
|
||||
],
|
||||
[
|
||||
106.6430296,
|
||||
10.8523306
|
||||
],
|
||||
[
|
||||
106.6433751,
|
||||
10.8525687
|
||||
],
|
||||
[
|
||||
106.6440861,
|
||||
10.8527104
|
||||
],
|
||||
[
|
||||
106.6444299,
|
||||
10.8529893
|
||||
],
|
||||
[
|
||||
106.6445125,
|
||||
10.8538749
|
||||
],
|
||||
[
|
||||
106.6447211,
|
||||
10.8543505
|
||||
],
|
||||
[
|
||||
106.6451741,
|
||||
10.8545293
|
||||
],
|
||||
[
|
||||
106.6460279,
|
||||
10.8539584
|
||||
],
|
||||
[
|
||||
106.6463712,
|
||||
10.8541026
|
||||
],
|
||||
[
|
||||
106.6465804,
|
||||
10.8563601
|
||||
],
|
||||
[
|
||||
106.6470451,
|
||||
10.8579867
|
||||
],
|
||||
[
|
||||
106.648915,
|
||||
10.8589213
|
||||
],
|
||||
[
|
||||
106.6500186,
|
||||
10.8591027
|
||||
],
|
||||
[
|
||||
106.651062,
|
||||
10.8599934
|
||||
],
|
||||
[
|
||||
106.6525657,
|
||||
10.8607922
|
||||
],
|
||||
[
|
||||
106.653078,
|
||||
10.860505
|
||||
],
|
||||
[
|
||||
106.6539414,
|
||||
10.8593362
|
||||
],
|
||||
[
|
||||
106.6555845,
|
||||
10.8588443
|
||||
],
|
||||
[
|
||||
106.6568062,
|
||||
10.8577868
|
||||
],
|
||||
[
|
||||
106.6573466,
|
||||
10.8577011
|
||||
],
|
||||
[
|
||||
106.6582527,
|
||||
10.8580289
|
||||
],
|
||||
[
|
||||
106.6588817,
|
||||
10.8578978
|
||||
],
|
||||
[
|
||||
106.6612083,
|
||||
10.8558868
|
||||
],
|
||||
[
|
||||
106.6627642,
|
||||
10.8555363
|
||||
],
|
||||
[
|
||||
106.6637371,
|
||||
10.8556238
|
||||
],
|
||||
[
|
||||
106.6659111,
|
||||
10.8562877
|
||||
],
|
||||
[
|
||||
106.667055,
|
||||
10.8560005
|
||||
],
|
||||
[
|
||||
106.667983,
|
||||
10.8575579
|
||||
],
|
||||
[
|
||||
106.6689809,
|
||||
10.858234
|
||||
],
|
||||
[
|
||||
106.6690449,
|
||||
10.858152
|
||||
],
|
||||
[
|
||||
106.6700828,
|
||||
10.8587179
|
||||
],
|
||||
[
|
||||
106.670645,
|
||||
10.8595831
|
||||
],
|
||||
[
|
||||
106.6724463,
|
||||
10.8606255
|
||||
],
|
||||
[
|
||||
106.6728044,
|
||||
10.8606907
|
||||
],
|
||||
[
|
||||
106.6732013,
|
||||
10.8605208
|
||||
],
|
||||
[
|
||||
106.6734788,
|
||||
10.8602221
|
||||
],
|
||||
[
|
||||
106.674247,
|
||||
10.8585881
|
||||
],
|
||||
[
|
||||
106.6750963,
|
||||
10.8573703
|
||||
],
|
||||
[
|
||||
106.675202,
|
||||
10.8568352
|
||||
],
|
||||
[
|
||||
106.6750955,
|
||||
10.8548985
|
||||
],
|
||||
[
|
||||
106.6758078,
|
||||
10.8533005
|
||||
],
|
||||
[
|
||||
106.6758943,
|
||||
10.8520676
|
||||
],
|
||||
[
|
||||
106.6793057,
|
||||
10.8508052
|
||||
],
|
||||
[
|
||||
106.6830974,
|
||||
10.8517582
|
||||
],
|
||||
[
|
||||
106.683898,
|
||||
10.8516093
|
||||
],
|
||||
[
|
||||
106.6843408,
|
||||
10.8512314
|
||||
],
|
||||
[
|
||||
106.6844429,
|
||||
10.8506183
|
||||
],
|
||||
[
|
||||
106.6830417,
|
||||
10.8485442
|
||||
],
|
||||
[
|
||||
106.6829689,
|
||||
10.847913
|
||||
],
|
||||
[
|
||||
106.6831958,
|
||||
10.8473938
|
||||
],
|
||||
[
|
||||
106.6836123,
|
||||
10.8471514
|
||||
],
|
||||
[
|
||||
106.6844431,
|
||||
10.8470612
|
||||
],
|
||||
[
|
||||
106.6859324,
|
||||
10.8473958
|
||||
],
|
||||
[
|
||||
106.6864596,
|
||||
10.8473719
|
||||
],
|
||||
[
|
||||
106.6872836,
|
||||
10.8470764
|
||||
],
|
||||
[
|
||||
106.6880694,
|
||||
10.8463487
|
||||
],
|
||||
[
|
||||
106.6888066,
|
||||
10.8450172
|
||||
],
|
||||
[
|
||||
106.689262,
|
||||
10.8435822
|
||||
],
|
||||
[
|
||||
106.6887362,
|
||||
10.8404574
|
||||
],
|
||||
[
|
||||
106.6889271,
|
||||
10.8388882
|
||||
],
|
||||
[
|
||||
106.6906009,
|
||||
10.8363282
|
||||
],
|
||||
[
|
||||
106.6916976,
|
||||
10.8317902
|
||||
],
|
||||
[
|
||||
106.6921324,
|
||||
10.8308375
|
||||
],
|
||||
[
|
||||
106.6928652,
|
||||
10.8302484
|
||||
],
|
||||
[
|
||||
106.6941372,
|
||||
10.8301909
|
||||
],
|
||||
[
|
||||
106.6951539,
|
||||
10.8309115
|
||||
],
|
||||
[
|
||||
106.6957335,
|
||||
10.8323163
|
||||
],
|
||||
[
|
||||
106.6962491,
|
||||
10.8329363
|
||||
],
|
||||
[
|
||||
106.6991023,
|
||||
10.834231
|
||||
],
|
||||
[
|
||||
106.7001075,
|
||||
10.8349207
|
||||
],
|
||||
[
|
||||
106.7007034,
|
||||
10.8359997
|
||||
],
|
||||
[
|
||||
106.7011125,
|
||||
10.8378423
|
||||
],
|
||||
[
|
||||
106.7027717,
|
||||
10.8391736
|
||||
],
|
||||
[
|
||||
106.6994467,
|
||||
10.8413013
|
||||
],
|
||||
[
|
||||
106.698748,
|
||||
10.8421061
|
||||
],
|
||||
[
|
||||
106.6982049,
|
||||
10.8430531
|
||||
],
|
||||
[
|
||||
106.697867,
|
||||
10.8446041
|
||||
],
|
||||
[
|
||||
106.697885,
|
||||
10.8460493
|
||||
],
|
||||
[
|
||||
106.6983048,
|
||||
10.8487142
|
||||
],
|
||||
[
|
||||
106.6988781,
|
||||
10.849789
|
||||
],
|
||||
[
|
||||
106.6997415,
|
||||
10.850528
|
||||
],
|
||||
[
|
||||
106.7006584,
|
||||
10.8509931
|
||||
],
|
||||
[
|
||||
106.7041691,
|
||||
10.8517419
|
||||
],
|
||||
[
|
||||
106.7060335,
|
||||
10.8517621
|
||||
],
|
||||
[
|
||||
106.7115481,
|
||||
10.851082
|
||||
],
|
||||
[
|
||||
106.7136919,
|
||||
10.8514038
|
||||
],
|
||||
[
|
||||
106.71487,
|
||||
10.85198
|
||||
],
|
||||
[
|
||||
106.7155427,
|
||||
10.8524259
|
||||
],
|
||||
[
|
||||
106.7171969,
|
||||
10.8541553
|
||||
],
|
||||
[
|
||||
106.7181037,
|
||||
10.855809
|
||||
],
|
||||
[
|
||||
106.7182007,
|
||||
10.8570173
|
||||
],
|
||||
[
|
||||
106.7174084,
|
||||
10.8606822
|
||||
],
|
||||
[
|
||||
106.7168419,
|
||||
10.862077
|
||||
],
|
||||
[
|
||||
106.7155158,
|
||||
10.8639953
|
||||
],
|
||||
[
|
||||
106.7142809,
|
||||
10.865054
|
||||
],
|
||||
[
|
||||
106.7131142,
|
||||
10.8657067
|
||||
],
|
||||
[
|
||||
106.7118781,
|
||||
10.8661331
|
||||
],
|
||||
[
|
||||
106.7101779,
|
||||
10.8664698
|
||||
],
|
||||
[
|
||||
106.7027211,
|
||||
10.8671672
|
||||
],
|
||||
[
|
||||
106.701602,
|
||||
10.8675029
|
||||
],
|
||||
[
|
||||
106.7005135,
|
||||
10.8680845
|
||||
],
|
||||
[
|
||||
106.6978828,
|
||||
10.8702404
|
||||
],
|
||||
[
|
||||
106.6956213,
|
||||
10.8726752
|
||||
],
|
||||
[
|
||||
106.6921642,
|
||||
10.8770668
|
||||
],
|
||||
[
|
||||
106.6897724,
|
||||
10.8804197
|
||||
],
|
||||
[
|
||||
106.689155,
|
||||
10.8819699
|
||||
],
|
||||
[
|
||||
106.6889612,
|
||||
10.8831161
|
||||
],
|
||||
[
|
||||
106.6889896,
|
||||
10.8842815
|
||||
],
|
||||
[
|
||||
106.6892418,
|
||||
10.8854263
|
||||
],
|
||||
[
|
||||
106.6897093,
|
||||
10.886504
|
||||
],
|
||||
[
|
||||
106.6936895,
|
||||
10.8929262
|
||||
],
|
||||
[
|
||||
106.6944572,
|
||||
10.8951658
|
||||
],
|
||||
[
|
||||
106.6945,
|
||||
10.89666
|
||||
],
|
||||
[
|
||||
106.6943267,
|
||||
10.8981028
|
||||
],
|
||||
[
|
||||
106.6933795,
|
||||
10.9022668
|
||||
],
|
||||
[
|
||||
106.692787,
|
||||
10.9041992
|
||||
],
|
||||
[
|
||||
106.6913926,
|
||||
10.9036957
|
||||
],
|
||||
[
|
||||
106.6905714,
|
||||
10.9032166
|
||||
],
|
||||
[
|
||||
106.6890428,
|
||||
10.9018194
|
||||
],
|
||||
[
|
||||
106.6857091,
|
||||
10.8999511
|
||||
],
|
||||
[
|
||||
106.6815461,
|
||||
10.8983942
|
||||
],
|
||||
[
|
||||
106.6796678,
|
||||
10.9036398
|
||||
],
|
||||
[
|
||||
106.6773017,
|
||||
10.903033
|
||||
],
|
||||
[
|
||||
106.6734395,
|
||||
10.9017156
|
||||
],
|
||||
[
|
||||
106.6718621,
|
||||
10.9015639
|
||||
],
|
||||
[
|
||||
106.6706343,
|
||||
10.9011647
|
||||
],
|
||||
[
|
||||
106.6707075,
|
||||
10.8990249
|
||||
],
|
||||
[
|
||||
106.6703254,
|
||||
10.8961665
|
||||
],
|
||||
[
|
||||
106.6696736,
|
||||
10.8941627
|
||||
],
|
||||
[
|
||||
106.6693551,
|
||||
10.8943597
|
||||
],
|
||||
[
|
||||
106.6692378,
|
||||
10.8939553
|
||||
],
|
||||
[
|
||||
106.6688833,
|
||||
10.8909129
|
||||
],
|
||||
[
|
||||
106.6672198,
|
||||
10.8917331
|
||||
],
|
||||
[
|
||||
106.6655352,
|
||||
10.8921015
|
||||
],
|
||||
[
|
||||
106.6650114,
|
||||
10.8924491
|
||||
],
|
||||
[
|
||||
106.6634046,
|
||||
10.8929634
|
||||
],
|
||||
[
|
||||
106.6623357,
|
||||
10.8929426
|
||||
],
|
||||
[
|
||||
106.6606086,
|
||||
10.8925672
|
||||
],
|
||||
[
|
||||
106.6575649,
|
||||
10.8924977
|
||||
],
|
||||
[
|
||||
106.6561492,
|
||||
10.8920807
|
||||
],
|
||||
[
|
||||
106.6544929,
|
||||
10.8920529
|
||||
],
|
||||
[
|
||||
106.6532259,
|
||||
10.8917401
|
||||
],
|
||||
[
|
||||
106.6531126,
|
||||
10.8923518
|
||||
],
|
||||
[
|
||||
106.6525039,
|
||||
10.8930746
|
||||
],
|
||||
[
|
||||
106.6524979,
|
||||
10.8943778
|
||||
],
|
||||
[
|
||||
106.6522703,
|
||||
10.8948819
|
||||
],
|
||||
[
|
||||
106.6514572,
|
||||
10.8949046
|
||||
],
|
||||
[
|
||||
106.6496333,
|
||||
10.8953576
|
||||
],
|
||||
[
|
||||
106.6485068,
|
||||
10.8950416
|
||||
],
|
||||
[
|
||||
106.6468517,
|
||||
10.8948581
|
||||
],
|
||||
[
|
||||
106.6468924,
|
||||
10.8945782
|
||||
],
|
||||
[
|
||||
106.6458782,
|
||||
10.8940828
|
||||
],
|
||||
[
|
||||
106.6451379,
|
||||
10.8942198
|
||||
],
|
||||
[
|
||||
106.6442903,
|
||||
10.8939459
|
||||
],
|
||||
[
|
||||
106.6435393,
|
||||
10.8939248
|
||||
],
|
||||
[
|
||||
106.6431209,
|
||||
10.8937457
|
||||
],
|
||||
[
|
||||
106.6428527,
|
||||
10.8934086
|
||||
],
|
||||
[
|
||||
106.6427776,
|
||||
10.8927764
|
||||
],
|
||||
[
|
||||
106.6425094,
|
||||
10.8924077
|
||||
],
|
||||
[
|
||||
106.6409537,
|
||||
10.89206
|
||||
],
|
||||
[
|
||||
106.6396555,
|
||||
10.8918388
|
||||
],
|
||||
[
|
||||
106.6318127,
|
||||
10.8917124
|
||||
],
|
||||
[
|
||||
106.6312763,
|
||||
10.8912172
|
||||
],
|
||||
[
|
||||
106.6294524,
|
||||
10.8868976
|
||||
],
|
||||
[
|
||||
106.6290661,
|
||||
10.8863076
|
||||
],
|
||||
[
|
||||
106.6286584,
|
||||
10.8858124
|
||||
],
|
||||
[
|
||||
106.6252145,
|
||||
10.8831995
|
||||
],
|
||||
[
|
||||
106.6253325,
|
||||
10.8829677
|
||||
],
|
||||
[
|
||||
106.6279236,
|
||||
10.881226
|
||||
],
|
||||
[
|
||||
106.6255941,
|
||||
10.8780112
|
||||
],
|
||||
[
|
||||
106.6194002,
|
||||
10.8711719
|
||||
],
|
||||
[
|
||||
106.6149189,
|
||||
10.8703369
|
||||
],
|
||||
[
|
||||
106.6151201,
|
||||
10.8698825
|
||||
],
|
||||
[
|
||||
106.6155117,
|
||||
10.8673445
|
||||
],
|
||||
[
|
||||
106.612047,
|
||||
10.8625043
|
||||
],
|
||||
[
|
||||
106.6072559,
|
||||
10.8552972
|
||||
],
|
||||
[
|
||||
106.6153254,
|
||||
10.8426844
|
||||
],
|
||||
[
|
||||
106.6088228,
|
||||
10.8368728
|
||||
],
|
||||
[
|
||||
106.608031,
|
||||
10.8359612
|
||||
],
|
||||
[
|
||||
106.6043611,
|
||||
10.8298733
|
||||
],
|
||||
[
|
||||
106.603739,
|
||||
10.8282162
|
||||
],
|
||||
[
|
||||
106.6032773,
|
||||
10.8262326
|
||||
]
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
{
|
||||
"type": "Feature",
|
||||
"properties": {
|
||||
"code": "TPHCM-Q3",
|
||||
"name": "District 3",
|
||||
"level": "district",
|
||||
"parent": "TPHCM",
|
||||
"basis": "pre-2025-merger extent, from OSM boundary/historic relations",
|
||||
"areaKm2": 4.9,
|
||||
"bbox": [
|
||||
106.66429,
|
||||
10.76543,
|
||||
106.69813,
|
||||
10.79251
|
||||
],
|
||||
"center": [
|
||||
10.78146,
|
||||
106.68284
|
||||
],
|
||||
"generatedAt": "2026-08-30T07:34:25.565Z",
|
||||
"osm": "relation/3819816"
|
||||
},
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [
|
||||
[
|
||||
[
|
||||
106.6642925,
|
||||
10.7871257
|
||||
],
|
||||
[
|
||||
106.6816674,
|
||||
10.7778253
|
||||
],
|
||||
[
|
||||
106.6827966,
|
||||
10.7758944
|
||||
],
|
||||
[
|
||||
106.6776153,
|
||||
10.7715083
|
||||
],
|
||||
[
|
||||
106.6756419,
|
||||
10.7693963
|
||||
],
|
||||
[
|
||||
106.6744365,
|
||||
10.7676889
|
||||
],
|
||||
[
|
||||
106.6816889,
|
||||
10.7654323
|
||||
],
|
||||
[
|
||||
106.6981274,
|
||||
10.7828849
|
||||
],
|
||||
[
|
||||
106.6861498,
|
||||
10.7925061
|
||||
],
|
||||
[
|
||||
106.6857326,
|
||||
10.7922396
|
||||
],
|
||||
[
|
||||
106.6835234,
|
||||
10.7924087
|
||||
],
|
||||
[
|
||||
106.6822586,
|
||||
10.791826
|
||||
],
|
||||
[
|
||||
106.681669,
|
||||
10.7912391
|
||||
],
|
||||
[
|
||||
106.6810562,
|
||||
10.7913798
|
||||
],
|
||||
[
|
||||
106.6801247,
|
||||
10.7912414
|
||||
],
|
||||
[
|
||||
106.6796614,
|
||||
10.7909694
|
||||
],
|
||||
[
|
||||
106.6792834,
|
||||
10.79104
|
||||
],
|
||||
[
|
||||
106.6778833,
|
||||
10.7907561
|
||||
],
|
||||
[
|
||||
106.6777765,
|
||||
10.790904
|
||||
],
|
||||
[
|
||||
106.6768404,
|
||||
10.7906094
|
||||
],
|
||||
[
|
||||
106.6767569,
|
||||
10.7904543
|
||||
],
|
||||
[
|
||||
106.6764704,
|
||||
10.7905835
|
||||
],
|
||||
[
|
||||
106.6754926,
|
||||
10.7902741
|
||||
],
|
||||
[
|
||||
106.6749739,
|
||||
10.7902903
|
||||
],
|
||||
[
|
||||
106.6746612,
|
||||
10.7900096
|
||||
],
|
||||
[
|
||||
106.6740013,
|
||||
10.7903561
|
||||
],
|
||||
[
|
||||
106.6737911,
|
||||
10.7899497
|
||||
],
|
||||
[
|
||||
106.6721772,
|
||||
10.7897854
|
||||
],
|
||||
[
|
||||
106.672193,
|
||||
10.7895631
|
||||
],
|
||||
[
|
||||
106.6719005,
|
||||
10.7893747
|
||||
],
|
||||
[
|
||||
106.6710336,
|
||||
10.7892987
|
||||
],
|
||||
[
|
||||
106.6707794,
|
||||
10.7879103
|
||||
],
|
||||
[
|
||||
106.6681063,
|
||||
10.7888395
|
||||
],
|
||||
[
|
||||
106.6667209,
|
||||
10.789603
|
||||
],
|
||||
[
|
||||
106.6661193,
|
||||
10.7893948
|
||||
],
|
||||
[
|
||||
106.6650608,
|
||||
10.7901541
|
||||
],
|
||||
[
|
||||
106.6647497,
|
||||
10.7901109
|
||||
],
|
||||
[
|
||||
106.6651983,
|
||||
10.7886751
|
||||
],
|
||||
[
|
||||
106.6642925,
|
||||
10.7871257
|
||||
]
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
{
|
||||
"type": "Feature",
|
||||
"properties": {
|
||||
"code": "TPHCM-Q4",
|
||||
"name": "District 4",
|
||||
"level": "district",
|
||||
"parent": "TPHCM",
|
||||
"basis": "pre-2025-merger extent, from OSM boundary/historic relations",
|
||||
"areaKm2": 4.2,
|
||||
"bbox": [
|
||||
106.68642,
|
||||
10.75171,
|
||||
106.72228,
|
||||
10.76976
|
||||
],
|
||||
"center": [
|
||||
10.75846,
|
||||
106.70554
|
||||
],
|
||||
"generatedAt": "2026-08-30T07:34:27.069Z",
|
||||
"osm": "relation/2778323"
|
||||
},
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [
|
||||
[
|
||||
[
|
||||
106.6864219,
|
||||
10.7517073
|
||||
],
|
||||
[
|
||||
106.6917998,
|
||||
10.7523424
|
||||
],
|
||||
[
|
||||
106.7155179,
|
||||
10.7524726
|
||||
],
|
||||
[
|
||||
106.7170231,
|
||||
10.7526249
|
||||
],
|
||||
[
|
||||
106.7186608,
|
||||
10.7532452
|
||||
],
|
||||
[
|
||||
106.7195276,
|
||||
10.7540192
|
||||
],
|
||||
[
|
||||
106.7203028,
|
||||
10.7551379
|
||||
],
|
||||
[
|
||||
106.7222803,
|
||||
10.7592738
|
||||
],
|
||||
[
|
||||
106.7177225,
|
||||
10.7608338
|
||||
],
|
||||
[
|
||||
106.7143725,
|
||||
10.7624818
|
||||
],
|
||||
[
|
||||
106.7127219,
|
||||
10.7635176
|
||||
],
|
||||
[
|
||||
106.7111806,
|
||||
10.7648337
|
||||
],
|
||||
[
|
||||
106.7102524,
|
||||
10.7659441
|
||||
],
|
||||
[
|
||||
106.7091195,
|
||||
10.7677888
|
||||
],
|
||||
[
|
||||
106.7082991,
|
||||
10.7697601
|
||||
],
|
||||
[
|
||||
106.7056291,
|
||||
10.7689242
|
||||
],
|
||||
[
|
||||
106.7035693,
|
||||
10.7680314
|
||||
],
|
||||
[
|
||||
106.7012074,
|
||||
10.7653499
|
||||
],
|
||||
[
|
||||
106.6982152,
|
||||
10.7633005
|
||||
],
|
||||
[
|
||||
106.6959129,
|
||||
10.7603701
|
||||
],
|
||||
[
|
||||
106.694486,
|
||||
10.7592226
|
||||
],
|
||||
[
|
||||
106.6916093,
|
||||
10.7575954
|
||||
],
|
||||
[
|
||||
106.6897117,
|
||||
10.7559422
|
||||
],
|
||||
[
|
||||
106.6869795,
|
||||
10.753218
|
||||
],
|
||||
[
|
||||
106.6866062,
|
||||
10.7526228
|
||||
],
|
||||
[
|
||||
106.6864219,
|
||||
10.7517073
|
||||
]
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
{
|
||||
"type": "Feature",
|
||||
"properties": {
|
||||
"code": "TPHCM-Q5",
|
||||
"name": "District 5",
|
||||
"level": "district",
|
||||
"parent": "TPHCM",
|
||||
"basis": "pre-2025-merger extent, from OSM boundary/historic relations",
|
||||
"areaKm2": 4.3,
|
||||
"bbox": [
|
||||
106.65051,
|
||||
10.74614,
|
||||
106.68727,
|
||||
10.76543
|
||||
],
|
||||
"center": [
|
||||
10.75494,
|
||||
106.66946
|
||||
],
|
||||
"generatedAt": "2026-08-30T07:34:28.965Z",
|
||||
"osm": "relation/3820432"
|
||||
},
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [
|
||||
[
|
||||
[
|
||||
106.6505141,
|
||||
10.7514823
|
||||
],
|
||||
[
|
||||
106.6532758,
|
||||
10.7516666
|
||||
],
|
||||
[
|
||||
106.6533937,
|
||||
10.750473
|
||||
],
|
||||
[
|
||||
106.6538595,
|
||||
10.7485698
|
||||
],
|
||||
[
|
||||
106.6550498,
|
||||
10.7461405
|
||||
],
|
||||
[
|
||||
106.6577973,
|
||||
10.7476151
|
||||
],
|
||||
[
|
||||
106.6654229,
|
||||
10.7501579
|
||||
],
|
||||
[
|
||||
106.6666192,
|
||||
10.7501579
|
||||
],
|
||||
[
|
||||
106.671196,
|
||||
10.7493132
|
||||
],
|
||||
[
|
||||
106.673426,
|
||||
10.7487462
|
||||
],
|
||||
[
|
||||
106.6752972,
|
||||
10.7487828
|
||||
],
|
||||
[
|
||||
106.675957,
|
||||
10.7490121
|
||||
],
|
||||
[
|
||||
106.6764033,
|
||||
10.7494064
|
||||
],
|
||||
[
|
||||
106.6766246,
|
||||
10.7500716
|
||||
],
|
||||
[
|
||||
106.6767259,
|
||||
10.7513448
|
||||
],
|
||||
[
|
||||
106.6770963,
|
||||
10.7516981
|
||||
],
|
||||
[
|
||||
106.679133,
|
||||
10.7517139
|
||||
],
|
||||
[
|
||||
106.6812255,
|
||||
10.75198
|
||||
],
|
||||
[
|
||||
106.6837322,
|
||||
10.7512804
|
||||
],
|
||||
[
|
||||
106.6850814,
|
||||
10.7511236
|
||||
],
|
||||
[
|
||||
106.6864219,
|
||||
10.7517073
|
||||
],
|
||||
[
|
||||
106.6866062,
|
||||
10.7526228
|
||||
],
|
||||
[
|
||||
106.6872747,
|
||||
10.753519
|
||||
],
|
||||
[
|
||||
106.6854624,
|
||||
10.7559828
|
||||
],
|
||||
[
|
||||
106.6816889,
|
||||
10.7654323
|
||||
],
|
||||
[
|
||||
106.6812069,
|
||||
10.7652911
|
||||
],
|
||||
[
|
||||
106.6747019,
|
||||
10.7610637
|
||||
],
|
||||
[
|
||||
106.6510904,
|
||||
10.7563386
|
||||
],
|
||||
[
|
||||
106.6505141,
|
||||
10.7514823
|
||||
]
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
{
|
||||
"type": "Feature",
|
||||
"properties": {
|
||||
"code": "TPHCM-Q6",
|
||||
"name": "District 6",
|
||||
"level": "district",
|
||||
"parent": "TPHCM",
|
||||
"basis": "pre-2025-merger extent, from OSM boundary/historic relations",
|
||||
"areaKm2": 7.2,
|
||||
"bbox": [
|
||||
106.62108,
|
||||
10.72973,
|
||||
106.65505,
|
||||
10.76202
|
||||
],
|
||||
"center": [
|
||||
10.74587,
|
||||
106.63524
|
||||
],
|
||||
"generatedAt": "2026-08-30T07:34:30.860Z",
|
||||
"osm": "relation/6228792"
|
||||
},
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [
|
||||
[
|
||||
[
|
||||
106.6210796,
|
||||
10.7340559
|
||||
],
|
||||
[
|
||||
106.6215381,
|
||||
10.7340412
|
||||
],
|
||||
[
|
||||
106.622237,
|
||||
10.7347042
|
||||
],
|
||||
[
|
||||
106.6236549,
|
||||
10.7353804
|
||||
],
|
||||
[
|
||||
106.6240796,
|
||||
10.7357855
|
||||
],
|
||||
[
|
||||
106.6244153,
|
||||
10.7357988
|
||||
],
|
||||
[
|
||||
106.6245587,
|
||||
10.7347465
|
||||
],
|
||||
[
|
||||
106.6239795,
|
||||
10.7319717
|
||||
],
|
||||
[
|
||||
106.6241397,
|
||||
10.7314621
|
||||
],
|
||||
[
|
||||
106.6260492,
|
||||
10.731423
|
||||
],
|
||||
[
|
||||
106.6271275,
|
||||
10.7317319
|
||||
],
|
||||
[
|
||||
106.6276212,
|
||||
10.7316954
|
||||
],
|
||||
[
|
||||
106.6278287,
|
||||
10.7311138
|
||||
],
|
||||
[
|
||||
106.6274048,
|
||||
10.7305413
|
||||
],
|
||||
[
|
||||
106.6272047,
|
||||
10.7297295
|
||||
],
|
||||
[
|
||||
106.6300523,
|
||||
10.7312836
|
||||
],
|
||||
[
|
||||
106.6329789,
|
||||
10.732892
|
||||
],
|
||||
[
|
||||
106.6494595,
|
||||
10.7429234
|
||||
],
|
||||
[
|
||||
106.6550498,
|
||||
10.7461405
|
||||
],
|
||||
[
|
||||
106.6538595,
|
||||
10.7485698
|
||||
],
|
||||
[
|
||||
106.6533937,
|
||||
10.750473
|
||||
],
|
||||
[
|
||||
106.6532758,
|
||||
10.7516666
|
||||
],
|
||||
[
|
||||
106.6505141,
|
||||
10.7514823
|
||||
],
|
||||
[
|
||||
106.6507413,
|
||||
10.7534041
|
||||
],
|
||||
[
|
||||
106.6384948,
|
||||
10.7546983
|
||||
],
|
||||
[
|
||||
106.6376157,
|
||||
10.7546647
|
||||
],
|
||||
[
|
||||
106.6374635,
|
||||
10.7553954
|
||||
],
|
||||
[
|
||||
106.6342046,
|
||||
10.758974
|
||||
],
|
||||
[
|
||||
106.6337331,
|
||||
10.7581715
|
||||
],
|
||||
[
|
||||
106.632997,
|
||||
10.758465
|
||||
],
|
||||
[
|
||||
106.6325973,
|
||||
10.7588418
|
||||
],
|
||||
[
|
||||
106.6318678,
|
||||
10.7588932
|
||||
],
|
||||
[
|
||||
106.6310014,
|
||||
10.7593728
|
||||
],
|
||||
[
|
||||
106.6306313,
|
||||
10.7597575
|
||||
],
|
||||
[
|
||||
106.6298119,
|
||||
10.7597483
|
||||
],
|
||||
[
|
||||
106.6297542,
|
||||
10.7601607
|
||||
],
|
||||
[
|
||||
106.6296,
|
||||
10.760212
|
||||
],
|
||||
[
|
||||
106.6289643,
|
||||
10.7603333
|
||||
],
|
||||
[
|
||||
106.6285043,
|
||||
10.7602239
|
||||
],
|
||||
[
|
||||
106.6281194,
|
||||
10.7604044
|
||||
],
|
||||
[
|
||||
106.6262646,
|
||||
10.7605981
|
||||
],
|
||||
[
|
||||
106.6258409,
|
||||
10.7604321
|
||||
],
|
||||
[
|
||||
106.6253151,
|
||||
10.7605546
|
||||
],
|
||||
[
|
||||
106.6251395,
|
||||
10.7607852
|
||||
],
|
||||
[
|
||||
106.6247143,
|
||||
10.7607509
|
||||
],
|
||||
[
|
||||
106.6244093,
|
||||
10.7617951
|
||||
],
|
||||
[
|
||||
106.6241619,
|
||||
10.7620157
|
||||
],
|
||||
[
|
||||
106.6252577,
|
||||
10.7574684
|
||||
],
|
||||
[
|
||||
106.6243798,
|
||||
10.7556165
|
||||
],
|
||||
[
|
||||
106.6237949,
|
||||
10.7455106
|
||||
],
|
||||
[
|
||||
106.6231201,
|
||||
10.7451195
|
||||
],
|
||||
[
|
||||
106.6231052,
|
||||
10.7447064
|
||||
],
|
||||
[
|
||||
106.6217503,
|
||||
10.7413933
|
||||
],
|
||||
[
|
||||
106.6220494,
|
||||
10.7387654
|
||||
],
|
||||
[
|
||||
106.6221717,
|
||||
10.7386272
|
||||
],
|
||||
[
|
||||
106.6210796,
|
||||
10.7340559
|
||||
]
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,546 @@
|
||||
{
|
||||
"type": "Feature",
|
||||
"properties": {
|
||||
"code": "TPHCM-Q7",
|
||||
"name": "District 7",
|
||||
"level": "district",
|
||||
"parent": "TPHCM",
|
||||
"basis": "pre-2025-merger extent, from OSM boundary/historic relations",
|
||||
"areaKm2": 35.7,
|
||||
"bbox": [
|
||||
106.69009,
|
||||
10.69795,
|
||||
106.76866,
|
||||
10.77763
|
||||
],
|
||||
"center": [
|
||||
10.73646,
|
||||
106.72962
|
||||
],
|
||||
"generatedAt": "2026-08-30T07:34:32.649Z",
|
||||
"osm": "relation/2764875"
|
||||
},
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [
|
||||
[
|
||||
[
|
||||
106.6900942,
|
||||
10.7252801
|
||||
],
|
||||
[
|
||||
106.6927747,
|
||||
10.725626
|
||||
],
|
||||
[
|
||||
106.6952228,
|
||||
10.7249052
|
||||
],
|
||||
[
|
||||
106.6984219,
|
||||
10.7243384
|
||||
],
|
||||
[
|
||||
106.7001917,
|
||||
10.7244822
|
||||
],
|
||||
[
|
||||
106.7022997,
|
||||
10.7251939
|
||||
],
|
||||
[
|
||||
106.7034075,
|
||||
10.7253829
|
||||
],
|
||||
[
|
||||
106.7045232,
|
||||
10.7253227
|
||||
],
|
||||
[
|
||||
106.7061496,
|
||||
10.7248806
|
||||
],
|
||||
[
|
||||
106.7068232,
|
||||
10.7245022
|
||||
],
|
||||
[
|
||||
106.7072977,
|
||||
10.7238093
|
||||
],
|
||||
[
|
||||
106.7074288,
|
||||
10.7228109
|
||||
],
|
||||
[
|
||||
106.7073417,
|
||||
10.7215558
|
||||
],
|
||||
[
|
||||
106.70756,
|
||||
10.7205352
|
||||
],
|
||||
[
|
||||
106.7080444,
|
||||
10.7199277
|
||||
],
|
||||
[
|
||||
106.7088177,
|
||||
10.7195863
|
||||
],
|
||||
[
|
||||
106.7092937,
|
||||
10.7197001
|
||||
],
|
||||
[
|
||||
106.7106391,
|
||||
10.7207056
|
||||
],
|
||||
[
|
||||
106.7112144,
|
||||
10.7208069
|
||||
],
|
||||
[
|
||||
106.717675,
|
||||
10.7190261
|
||||
],
|
||||
[
|
||||
106.7184432,
|
||||
10.7190011
|
||||
],
|
||||
[
|
||||
106.7190232,
|
||||
10.7193951
|
||||
],
|
||||
[
|
||||
106.7199684,
|
||||
10.721549
|
||||
],
|
||||
[
|
||||
106.7205028,
|
||||
10.7221066
|
||||
],
|
||||
[
|
||||
106.7210606,
|
||||
10.7223467
|
||||
],
|
||||
[
|
||||
106.7244476,
|
||||
10.7211278
|
||||
],
|
||||
[
|
||||
106.7250856,
|
||||
10.720479
|
||||
],
|
||||
[
|
||||
106.725275,
|
||||
10.7197654
|
||||
],
|
||||
[
|
||||
106.7252165,
|
||||
10.719299
|
||||
],
|
||||
[
|
||||
106.7234096,
|
||||
10.7170245
|
||||
],
|
||||
[
|
||||
106.7230856,
|
||||
10.7162545
|
||||
],
|
||||
[
|
||||
106.7230504,
|
||||
10.7155521
|
||||
],
|
||||
[
|
||||
106.7237023,
|
||||
10.7139434
|
||||
],
|
||||
[
|
||||
106.7253004,
|
||||
10.7120346
|
||||
],
|
||||
[
|
||||
106.7257464,
|
||||
10.7106827
|
||||
],
|
||||
[
|
||||
106.7257304,
|
||||
10.7086976
|
||||
],
|
||||
[
|
||||
106.7247136,
|
||||
10.7066492
|
||||
],
|
||||
[
|
||||
106.7245972,
|
||||
10.7058075
|
||||
],
|
||||
[
|
||||
106.7249089,
|
||||
10.7048314
|
||||
],
|
||||
[
|
||||
106.7260703,
|
||||
10.7025583
|
||||
],
|
||||
[
|
||||
106.7263609,
|
||||
10.6999459
|
||||
],
|
||||
[
|
||||
106.7268836,
|
||||
10.6992594
|
||||
],
|
||||
[
|
||||
106.7286655,
|
||||
10.698636
|
||||
],
|
||||
[
|
||||
106.731293,
|
||||
10.6980354
|
||||
],
|
||||
[
|
||||
106.7329744,
|
||||
10.6979464
|
||||
],
|
||||
[
|
||||
106.7336579,
|
||||
10.6981902
|
||||
],
|
||||
[
|
||||
106.7341867,
|
||||
10.6988316
|
||||
],
|
||||
[
|
||||
106.7346024,
|
||||
10.7014355
|
||||
],
|
||||
[
|
||||
106.7349768,
|
||||
10.7023049
|
||||
],
|
||||
[
|
||||
106.7356646,
|
||||
10.7030169
|
||||
],
|
||||
[
|
||||
106.7363214,
|
||||
10.7033525
|
||||
],
|
||||
[
|
||||
106.7370473,
|
||||
10.7034952
|
||||
],
|
||||
[
|
||||
106.7453833,
|
||||
10.7032515
|
||||
],
|
||||
[
|
||||
106.7495477,
|
||||
10.7046114
|
||||
],
|
||||
[
|
||||
106.751906,
|
||||
10.7051207
|
||||
],
|
||||
[
|
||||
106.7512155,
|
||||
10.7092477
|
||||
],
|
||||
[
|
||||
106.7512531,
|
||||
10.713473
|
||||
],
|
||||
[
|
||||
106.7519171,
|
||||
10.7185809
|
||||
],
|
||||
[
|
||||
106.752304,
|
||||
10.7204199
|
||||
],
|
||||
[
|
||||
106.7528834,
|
||||
10.7219417
|
||||
],
|
||||
[
|
||||
106.7536556,
|
||||
10.7234634
|
||||
],
|
||||
[
|
||||
106.764866,
|
||||
10.7377245
|
||||
],
|
||||
[
|
||||
106.7686561,
|
||||
10.7431427
|
||||
],
|
||||
[
|
||||
106.7605662,
|
||||
10.7440812
|
||||
],
|
||||
[
|
||||
106.758578,
|
||||
10.7440515
|
||||
],
|
||||
[
|
||||
106.75522,
|
||||
10.7435471
|
||||
],
|
||||
[
|
||||
106.7512499,
|
||||
10.7422959
|
||||
],
|
||||
[
|
||||
106.7491947,
|
||||
10.742096
|
||||
],
|
||||
[
|
||||
106.7473831,
|
||||
10.742354
|
||||
],
|
||||
[
|
||||
106.7463773,
|
||||
10.7429059
|
||||
],
|
||||
[
|
||||
106.7455949,
|
||||
10.7436167
|
||||
],
|
||||
[
|
||||
106.7447776,
|
||||
10.7449103
|
||||
],
|
||||
[
|
||||
106.744245,
|
||||
10.746242
|
||||
],
|
||||
[
|
||||
106.7440823,
|
||||
10.7476196
|
||||
],
|
||||
[
|
||||
106.7443435,
|
||||
10.7493188
|
||||
],
|
||||
[
|
||||
106.745346,
|
||||
10.751807
|
||||
],
|
||||
[
|
||||
106.7464144,
|
||||
10.7536636
|
||||
],
|
||||
[
|
||||
106.7480829,
|
||||
10.756012
|
||||
],
|
||||
[
|
||||
106.7504998,
|
||||
10.7589585
|
||||
],
|
||||
[
|
||||
106.7520333,
|
||||
10.7613851
|
||||
],
|
||||
[
|
||||
106.7524697,
|
||||
10.7626517
|
||||
],
|
||||
[
|
||||
106.7526877,
|
||||
10.7639233
|
||||
],
|
||||
[
|
||||
106.7527261,
|
||||
10.7651092
|
||||
],
|
||||
[
|
||||
106.7525521,
|
||||
10.7662674
|
||||
],
|
||||
[
|
||||
106.7518294,
|
||||
10.7684425
|
||||
],
|
||||
[
|
||||
106.7508683,
|
||||
10.770106
|
||||
],
|
||||
[
|
||||
106.7489684,
|
||||
10.7722975
|
||||
],
|
||||
[
|
||||
106.7467441,
|
||||
10.7743204
|
||||
],
|
||||
[
|
||||
106.7440514,
|
||||
10.7763208
|
||||
],
|
||||
[
|
||||
106.7427218,
|
||||
10.7770556
|
||||
],
|
||||
[
|
||||
106.740673,
|
||||
10.7776336
|
||||
],
|
||||
[
|
||||
106.7389863,
|
||||
10.7776019
|
||||
],
|
||||
[
|
||||
106.7378086,
|
||||
10.7772026
|
||||
],
|
||||
[
|
||||
106.7365916,
|
||||
10.776362
|
||||
],
|
||||
[
|
||||
106.7357404,
|
||||
10.7753754
|
||||
],
|
||||
[
|
||||
106.7351494,
|
||||
10.7742426
|
||||
],
|
||||
[
|
||||
106.7346271,
|
||||
10.7722869
|
||||
],
|
||||
[
|
||||
106.7343574,
|
||||
10.7671217
|
||||
],
|
||||
[
|
||||
106.7339688,
|
||||
10.764798
|
||||
],
|
||||
[
|
||||
106.733272,
|
||||
10.7627184
|
||||
],
|
||||
[
|
||||
106.7321193,
|
||||
10.7610314
|
||||
],
|
||||
[
|
||||
106.7308536,
|
||||
10.760207
|
||||
],
|
||||
[
|
||||
106.7292256,
|
||||
10.7596409
|
||||
],
|
||||
[
|
||||
106.7250908,
|
||||
10.75915
|
||||
],
|
||||
[
|
||||
106.7222803,
|
||||
10.7592738
|
||||
],
|
||||
[
|
||||
106.7200864,
|
||||
10.7547764
|
||||
],
|
||||
[
|
||||
106.7192636,
|
||||
10.7537245
|
||||
],
|
||||
[
|
||||
106.7184667,
|
||||
10.7531384
|
||||
],
|
||||
[
|
||||
106.7174356,
|
||||
10.7527259
|
||||
],
|
||||
[
|
||||
106.7158543,
|
||||
10.7524833
|
||||
],
|
||||
[
|
||||
106.6917998,
|
||||
10.7523424
|
||||
],
|
||||
[
|
||||
106.6902025,
|
||||
10.7521092
|
||||
],
|
||||
[
|
||||
106.6903889,
|
||||
10.7500103
|
||||
],
|
||||
[
|
||||
106.6919585,
|
||||
10.7479882
|
||||
],
|
||||
[
|
||||
106.6925424,
|
||||
10.7469369
|
||||
],
|
||||
[
|
||||
106.692855,
|
||||
10.7458472
|
||||
],
|
||||
[
|
||||
106.6927935,
|
||||
10.7440551
|
||||
],
|
||||
[
|
||||
106.6919158,
|
||||
10.7427513
|
||||
],
|
||||
[
|
||||
106.6918898,
|
||||
10.7420137
|
||||
],
|
||||
[
|
||||
106.6943747,
|
||||
10.7379754
|
||||
],
|
||||
[
|
||||
106.6948785,
|
||||
10.7368653
|
||||
],
|
||||
[
|
||||
106.6949594,
|
||||
10.7361939
|
||||
],
|
||||
[
|
||||
106.6947688,
|
||||
10.7353862
|
||||
],
|
||||
[
|
||||
106.6937685,
|
||||
10.7335271
|
||||
],
|
||||
[
|
||||
106.6926419,
|
||||
10.732463
|
||||
],
|
||||
[
|
||||
106.6908453,
|
||||
10.7312186
|
||||
],
|
||||
[
|
||||
106.6903276,
|
||||
10.7304666
|
||||
],
|
||||
[
|
||||
106.690195,
|
||||
10.7297562
|
||||
],
|
||||
[
|
||||
106.6900942,
|
||||
10.7252801
|
||||
]
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,438 @@
|
||||
{
|
||||
"type": "Feature",
|
||||
"properties": {
|
||||
"code": "TPHCM-Q8",
|
||||
"name": "District 8",
|
||||
"level": "district",
|
||||
"parent": "TPHCM",
|
||||
"basis": "pre-2025-merger extent, from OSM boundary/historic relations",
|
||||
"areaKm2": 19.8,
|
||||
"bbox": [
|
||||
106.59456,
|
||||
10.68985,
|
||||
106.69285,
|
||||
10.75211
|
||||
],
|
||||
"center": [
|
||||
10.7245,
|
||||
106.64055
|
||||
],
|
||||
"generatedAt": "2026-08-30T07:34:34.548Z",
|
||||
"osm": "relation/6888445"
|
||||
},
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [
|
||||
[
|
||||
[
|
||||
106.5945559,
|
||||
10.6898539
|
||||
],
|
||||
[
|
||||
106.627758,
|
||||
10.6990013
|
||||
],
|
||||
[
|
||||
106.6422442,
|
||||
10.7278381
|
||||
],
|
||||
[
|
||||
106.642375,
|
||||
10.7275872
|
||||
],
|
||||
[
|
||||
106.6431959,
|
||||
10.7276667
|
||||
],
|
||||
[
|
||||
106.6437051,
|
||||
10.7280418
|
||||
],
|
||||
[
|
||||
106.6453784,
|
||||
10.7286487
|
||||
],
|
||||
[
|
||||
106.6453111,
|
||||
10.7290667
|
||||
],
|
||||
[
|
||||
106.6455175,
|
||||
10.7291327
|
||||
],
|
||||
[
|
||||
106.6499642,
|
||||
10.7286981
|
||||
],
|
||||
[
|
||||
106.6500848,
|
||||
10.7287391
|
||||
],
|
||||
[
|
||||
106.6494424,
|
||||
10.7302755
|
||||
],
|
||||
[
|
||||
106.6497356,
|
||||
10.7304894
|
||||
],
|
||||
[
|
||||
106.6548687,
|
||||
10.7318066
|
||||
],
|
||||
[
|
||||
106.6561147,
|
||||
10.7323143
|
||||
],
|
||||
[
|
||||
106.657065,
|
||||
10.732269
|
||||
],
|
||||
[
|
||||
106.6575447,
|
||||
10.7323554
|
||||
],
|
||||
[
|
||||
106.6576721,
|
||||
10.7325205
|
||||
],
|
||||
[
|
||||
106.6593625,
|
||||
10.7326767
|
||||
],
|
||||
[
|
||||
106.6595859,
|
||||
10.7329354
|
||||
],
|
||||
[
|
||||
106.6611839,
|
||||
10.7331928
|
||||
],
|
||||
[
|
||||
106.6634511,
|
||||
10.7339882
|
||||
],
|
||||
[
|
||||
106.6641899,
|
||||
10.7339573
|
||||
],
|
||||
[
|
||||
106.6657348,
|
||||
10.7342221
|
||||
],
|
||||
[
|
||||
106.6659431,
|
||||
10.7323951
|
||||
],
|
||||
[
|
||||
106.6701997,
|
||||
10.735145
|
||||
],
|
||||
[
|
||||
106.6701103,
|
||||
10.7352704
|
||||
],
|
||||
[
|
||||
106.6717283,
|
||||
10.7362667
|
||||
],
|
||||
[
|
||||
106.6718026,
|
||||
10.7361635
|
||||
],
|
||||
[
|
||||
106.6736405,
|
||||
10.7373836
|
||||
],
|
||||
[
|
||||
106.6744609,
|
||||
10.7362303
|
||||
],
|
||||
[
|
||||
106.6767759,
|
||||
10.7350865
|
||||
],
|
||||
[
|
||||
106.6781027,
|
||||
10.7354312
|
||||
],
|
||||
[
|
||||
106.678108,
|
||||
10.7350182
|
||||
],
|
||||
[
|
||||
106.6798082,
|
||||
10.7346882
|
||||
],
|
||||
[
|
||||
106.6799127,
|
||||
10.7351943
|
||||
],
|
||||
[
|
||||
106.6810735,
|
||||
10.7349618
|
||||
],
|
||||
[
|
||||
106.6815584,
|
||||
10.7345719
|
||||
],
|
||||
[
|
||||
106.6822111,
|
||||
10.7347314
|
||||
],
|
||||
[
|
||||
106.6820757,
|
||||
10.736278
|
||||
],
|
||||
[
|
||||
106.6822815,
|
||||
10.7370079
|
||||
],
|
||||
[
|
||||
106.6830457,
|
||||
10.7385115
|
||||
],
|
||||
[
|
||||
106.6829835,
|
||||
10.7395195
|
||||
],
|
||||
[
|
||||
106.686892,
|
||||
10.7400493
|
||||
],
|
||||
[
|
||||
106.6921478,
|
||||
10.7415213
|
||||
],
|
||||
[
|
||||
106.6918898,
|
||||
10.7420137
|
||||
],
|
||||
[
|
||||
106.6918471,
|
||||
10.7425352
|
||||
],
|
||||
[
|
||||
106.6927935,
|
||||
10.7440551
|
||||
],
|
||||
[
|
||||
106.692855,
|
||||
10.7458472
|
||||
],
|
||||
[
|
||||
106.6925424,
|
||||
10.7469369
|
||||
],
|
||||
[
|
||||
106.6919585,
|
||||
10.7479882
|
||||
],
|
||||
[
|
||||
106.6903889,
|
||||
10.7500103
|
||||
],
|
||||
[
|
||||
106.6902025,
|
||||
10.7521092
|
||||
],
|
||||
[
|
||||
106.6864219,
|
||||
10.7517073
|
||||
],
|
||||
[
|
||||
106.6848442,
|
||||
10.7510971
|
||||
],
|
||||
[
|
||||
106.6812255,
|
||||
10.75198
|
||||
],
|
||||
[
|
||||
106.679133,
|
||||
10.7517139
|
||||
],
|
||||
[
|
||||
106.6771808,
|
||||
10.7517228
|
||||
],
|
||||
[
|
||||
106.6767459,
|
||||
10.7514071
|
||||
],
|
||||
[
|
||||
106.6766246,
|
||||
10.7500716
|
||||
],
|
||||
[
|
||||
106.67631,
|
||||
10.7492777
|
||||
],
|
||||
[
|
||||
106.6757212,
|
||||
10.7489065
|
||||
],
|
||||
[
|
||||
106.6749964,
|
||||
10.7487357
|
||||
],
|
||||
[
|
||||
106.673426,
|
||||
10.7487462
|
||||
],
|
||||
[
|
||||
106.671196,
|
||||
10.7493132
|
||||
],
|
||||
[
|
||||
106.6658361,
|
||||
10.7501969
|
||||
],
|
||||
[
|
||||
106.6647248,
|
||||
10.7500024
|
||||
],
|
||||
[
|
||||
106.6587243,
|
||||
10.7479588
|
||||
],
|
||||
[
|
||||
106.6558803,
|
||||
10.7466435
|
||||
],
|
||||
[
|
||||
106.6494595,
|
||||
10.7429234
|
||||
],
|
||||
[
|
||||
106.6329789,
|
||||
10.732892
|
||||
],
|
||||
[
|
||||
106.6272047,
|
||||
10.7297295
|
||||
],
|
||||
[
|
||||
106.6274048,
|
||||
10.7305413
|
||||
],
|
||||
[
|
||||
106.6278287,
|
||||
10.7311138
|
||||
],
|
||||
[
|
||||
106.6276878,
|
||||
10.731656
|
||||
],
|
||||
[
|
||||
106.6272354,
|
||||
10.7317441
|
||||
],
|
||||
[
|
||||
106.6262132,
|
||||
10.7314364
|
||||
],
|
||||
[
|
||||
106.6244427,
|
||||
10.7313794
|
||||
],
|
||||
[
|
||||
106.6240144,
|
||||
10.7316488
|
||||
],
|
||||
[
|
||||
106.6245587,
|
||||
10.7347465
|
||||
],
|
||||
[
|
||||
106.6244153,
|
||||
10.7357988
|
||||
],
|
||||
[
|
||||
106.6240796,
|
||||
10.7357855
|
||||
],
|
||||
[
|
||||
106.6236549,
|
||||
10.7353804
|
||||
],
|
||||
[
|
||||
106.622237,
|
||||
10.7347042
|
||||
],
|
||||
[
|
||||
106.6215381,
|
||||
10.7340412
|
||||
],
|
||||
[
|
||||
106.6210796,
|
||||
10.7340559
|
||||
],
|
||||
[
|
||||
106.6190341,
|
||||
10.7162048
|
||||
],
|
||||
[
|
||||
106.6182121,
|
||||
10.7160219
|
||||
],
|
||||
[
|
||||
106.6174368,
|
||||
10.7152573
|
||||
],
|
||||
[
|
||||
106.615558,
|
||||
10.7150037
|
||||
],
|
||||
[
|
||||
106.6160281,
|
||||
10.7142529
|
||||
],
|
||||
[
|
||||
106.6065404,
|
||||
10.7119137
|
||||
],
|
||||
[
|
||||
106.6038209,
|
||||
10.7109632
|
||||
],
|
||||
[
|
||||
106.6072782,
|
||||
10.70834
|
||||
],
|
||||
[
|
||||
106.6086473,
|
||||
10.7062665
|
||||
],
|
||||
[
|
||||
106.6058878,
|
||||
10.7047797
|
||||
],
|
||||
[
|
||||
106.6012667,
|
||||
10.7034806
|
||||
],
|
||||
[
|
||||
106.5989167,
|
||||
10.7022033
|
||||
],
|
||||
[
|
||||
106.5973069,
|
||||
10.7016164
|
||||
],
|
||||
[
|
||||
106.5973805,
|
||||
10.6997062
|
||||
],
|
||||
[
|
||||
106.5970397,
|
||||
10.6975914
|
||||
],
|
||||
[
|
||||
106.5945559,
|
||||
10.6898539
|
||||
]
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,462 @@
|
||||
{
|
||||
"type": "Feature",
|
||||
"properties": {
|
||||
"code": "TPHCM-TANBINH",
|
||||
"name": "Tan Binh",
|
||||
"level": "district",
|
||||
"parent": "TPHCM",
|
||||
"basis": "pre-2025-merger extent, from OSM boundary/historic relations",
|
||||
"areaKm2": 22.5,
|
||||
"bbox": [
|
||||
106.62789,
|
||||
10.76891,
|
||||
106.67861,
|
||||
10.83725
|
||||
],
|
||||
"center": [
|
||||
10.80882,
|
||||
106.65239
|
||||
],
|
||||
"generatedAt": "2026-08-30T07:34:48.592Z",
|
||||
"osm": "relation/6846177"
|
||||
},
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [
|
||||
[
|
||||
[
|
||||
106.6278908,
|
||||
10.8246854
|
||||
],
|
||||
[
|
||||
106.6295221,
|
||||
10.8232091
|
||||
],
|
||||
[
|
||||
106.6302464,
|
||||
10.8219341
|
||||
],
|
||||
[
|
||||
106.6357729,
|
||||
10.8038388
|
||||
],
|
||||
[
|
||||
106.6362173,
|
||||
10.8026501
|
||||
],
|
||||
[
|
||||
106.6367349,
|
||||
10.8018264
|
||||
],
|
||||
[
|
||||
106.6382682,
|
||||
10.7959026
|
||||
],
|
||||
[
|
||||
106.6393945,
|
||||
10.7923729
|
||||
],
|
||||
[
|
||||
106.6429519,
|
||||
10.7822253
|
||||
],
|
||||
[
|
||||
106.6434517,
|
||||
10.7811899
|
||||
],
|
||||
[
|
||||
106.6504264,
|
||||
10.7711933
|
||||
],
|
||||
[
|
||||
106.6518883,
|
||||
10.7691722
|
||||
],
|
||||
[
|
||||
106.6523283,
|
||||
10.7689114
|
||||
],
|
||||
[
|
||||
106.6525131,
|
||||
10.7693077
|
||||
],
|
||||
[
|
||||
106.6532259,
|
||||
10.7753215
|
||||
],
|
||||
[
|
||||
106.6534406,
|
||||
10.7758836
|
||||
],
|
||||
[
|
||||
106.6559493,
|
||||
10.7784506
|
||||
],
|
||||
[
|
||||
106.6561318,
|
||||
10.7777457
|
||||
],
|
||||
[
|
||||
106.6646464,
|
||||
10.7869335
|
||||
],
|
||||
[
|
||||
106.6642925,
|
||||
10.7871257
|
||||
],
|
||||
[
|
||||
106.6651983,
|
||||
10.7886751
|
||||
],
|
||||
[
|
||||
106.6647497,
|
||||
10.7901109
|
||||
],
|
||||
[
|
||||
106.6650608,
|
||||
10.7901541
|
||||
],
|
||||
[
|
||||
106.6661193,
|
||||
10.7893948
|
||||
],
|
||||
[
|
||||
106.6667209,
|
||||
10.789603
|
||||
],
|
||||
[
|
||||
106.6667297,
|
||||
10.7903408
|
||||
],
|
||||
[
|
||||
106.6659623,
|
||||
10.790869
|
||||
],
|
||||
[
|
||||
106.6684519,
|
||||
10.7941512
|
||||
],
|
||||
[
|
||||
106.6682731,
|
||||
10.7942846
|
||||
],
|
||||
[
|
||||
106.66854,
|
||||
10.7970376
|
||||
],
|
||||
[
|
||||
106.668547,
|
||||
10.7972171
|
||||
],
|
||||
[
|
||||
106.6683834,
|
||||
10.797225
|
||||
],
|
||||
[
|
||||
106.6684739,
|
||||
10.798224
|
||||
],
|
||||
[
|
||||
106.6690275,
|
||||
10.7981784
|
||||
],
|
||||
[
|
||||
106.669058,
|
||||
10.7984185
|
||||
],
|
||||
[
|
||||
106.6688541,
|
||||
10.798431
|
||||
],
|
||||
[
|
||||
106.6688635,
|
||||
10.7986978
|
||||
],
|
||||
[
|
||||
106.6691093,
|
||||
10.7988861
|
||||
],
|
||||
[
|
||||
106.669816,
|
||||
10.7986179
|
||||
],
|
||||
[
|
||||
106.6698792,
|
||||
10.7989467
|
||||
],
|
||||
[
|
||||
106.6673501,
|
||||
10.800385
|
||||
],
|
||||
[
|
||||
106.668137,
|
||||
10.8027002
|
||||
],
|
||||
[
|
||||
106.6686228,
|
||||
10.8033104
|
||||
],
|
||||
[
|
||||
106.6690519,
|
||||
10.803317
|
||||
],
|
||||
[
|
||||
106.6694258,
|
||||
10.8066736
|
||||
],
|
||||
[
|
||||
106.6699015,
|
||||
10.8068995
|
||||
],
|
||||
[
|
||||
106.6699978,
|
||||
10.8077159
|
||||
],
|
||||
[
|
||||
106.6706774,
|
||||
10.807653
|
||||
],
|
||||
[
|
||||
106.6706965,
|
||||
10.8078137
|
||||
],
|
||||
[
|
||||
106.6710395,
|
||||
10.8078723
|
||||
],
|
||||
[
|
||||
106.6710368,
|
||||
10.8086713
|
||||
],
|
||||
[
|
||||
106.6706718,
|
||||
10.8087065
|
||||
],
|
||||
[
|
||||
106.6707236,
|
||||
10.8098496
|
||||
],
|
||||
[
|
||||
106.671651,
|
||||
10.8094077
|
||||
],
|
||||
[
|
||||
106.672404,
|
||||
10.8106675
|
||||
],
|
||||
[
|
||||
106.6721512,
|
||||
10.8109242
|
||||
],
|
||||
[
|
||||
106.6720607,
|
||||
10.8125837
|
||||
],
|
||||
[
|
||||
106.6725375,
|
||||
10.8139935
|
||||
],
|
||||
[
|
||||
106.673668,
|
||||
10.8146291
|
||||
],
|
||||
[
|
||||
106.6745478,
|
||||
10.8147342
|
||||
],
|
||||
[
|
||||
106.6749756,
|
||||
10.8151379
|
||||
],
|
||||
[
|
||||
106.6760401,
|
||||
10.814857
|
||||
],
|
||||
[
|
||||
106.676116,
|
||||
10.8151054
|
||||
],
|
||||
[
|
||||
106.6756901,
|
||||
10.8153569
|
||||
],
|
||||
[
|
||||
106.6757402,
|
||||
10.8160225
|
||||
],
|
||||
[
|
||||
106.6756024,
|
||||
10.8164998
|
||||
],
|
||||
[
|
||||
106.6741726,
|
||||
10.8179961
|
||||
],
|
||||
[
|
||||
106.6748586,
|
||||
10.8183201
|
||||
],
|
||||
[
|
||||
106.6733203,
|
||||
10.8223634
|
||||
],
|
||||
[
|
||||
106.6750903,
|
||||
10.8235374
|
||||
],
|
||||
[
|
||||
106.6767593,
|
||||
10.8241335
|
||||
],
|
||||
[
|
||||
106.6781916,
|
||||
10.8250878
|
||||
],
|
||||
[
|
||||
106.678606,
|
||||
10.8256352
|
||||
],
|
||||
[
|
||||
106.6785859,
|
||||
10.826139
|
||||
],
|
||||
[
|
||||
106.6779257,
|
||||
10.8268289
|
||||
],
|
||||
[
|
||||
106.6774204,
|
||||
10.8270321
|
||||
],
|
||||
[
|
||||
106.6743134,
|
||||
10.8262207
|
||||
],
|
||||
[
|
||||
106.6734213,
|
||||
10.8277727
|
||||
],
|
||||
[
|
||||
106.6731419,
|
||||
10.8279436
|
||||
],
|
||||
[
|
||||
106.670441,
|
||||
10.8271654
|
||||
],
|
||||
[
|
||||
106.6680099,
|
||||
10.8283849
|
||||
],
|
||||
[
|
||||
106.6661223,
|
||||
10.8284392
|
||||
],
|
||||
[
|
||||
106.6639467,
|
||||
10.8306232
|
||||
],
|
||||
[
|
||||
106.6634298,
|
||||
10.8307011
|
||||
],
|
||||
[
|
||||
106.6627645,
|
||||
10.8312538
|
||||
],
|
||||
[
|
||||
106.661986,
|
||||
10.8313787
|
||||
],
|
||||
[
|
||||
106.6608741,
|
||||
10.8324051
|
||||
],
|
||||
[
|
||||
106.6592317,
|
||||
10.8332612
|
||||
],
|
||||
[
|
||||
106.6571005,
|
||||
10.8353982
|
||||
],
|
||||
[
|
||||
106.655547,
|
||||
10.8358284
|
||||
],
|
||||
[
|
||||
106.6555819,
|
||||
10.8360062
|
||||
],
|
||||
[
|
||||
106.6520426,
|
||||
10.836613
|
||||
],
|
||||
[
|
||||
106.6520627,
|
||||
10.8368183
|
||||
],
|
||||
[
|
||||
106.6519167,
|
||||
10.8368321
|
||||
],
|
||||
[
|
||||
106.6518955,
|
||||
10.8366144
|
||||
],
|
||||
[
|
||||
106.6475983,
|
||||
10.8366566
|
||||
],
|
||||
[
|
||||
106.6464825,
|
||||
10.8368769
|
||||
],
|
||||
[
|
||||
106.646312,
|
||||
10.8372532
|
||||
],
|
||||
[
|
||||
106.6425122,
|
||||
10.8281991
|
||||
],
|
||||
[
|
||||
106.6414136,
|
||||
10.8286759
|
||||
],
|
||||
[
|
||||
106.6415057,
|
||||
10.828921
|
||||
],
|
||||
[
|
||||
106.6407848,
|
||||
10.8292595
|
||||
],
|
||||
[
|
||||
106.6381996,
|
||||
10.8299334
|
||||
],
|
||||
[
|
||||
106.6339409,
|
||||
10.8323551
|
||||
],
|
||||
[
|
||||
106.632358,
|
||||
10.8329517
|
||||
],
|
||||
[
|
||||
106.6313754,
|
||||
10.8302412
|
||||
],
|
||||
[
|
||||
106.6301022,
|
||||
10.8275906
|
||||
],
|
||||
[
|
||||
106.6278908,
|
||||
10.8246854
|
||||
]
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,298 @@
|
||||
{
|
||||
"type": "Feature",
|
||||
"properties": {
|
||||
"code": "TPHCM-TANPHU",
|
||||
"name": "Tan Phu",
|
||||
"level": "district",
|
||||
"parent": "TPHCM",
|
||||
"basis": "pre-2025-merger extent, from OSM boundary/historic relations",
|
||||
"areaKm2": 16,
|
||||
"bbox": [
|
||||
106.6074,
|
||||
10.75817,
|
||||
106.64768,
|
||||
10.82469
|
||||
],
|
||||
"center": [
|
||||
10.79255,
|
||||
106.62689
|
||||
],
|
||||
"generatedAt": "2026-08-30T07:34:50.584Z",
|
||||
"osm": "relation/6846128"
|
||||
},
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [
|
||||
[
|
||||
[
|
||||
106.6074036,
|
||||
10.8082441
|
||||
],
|
||||
[
|
||||
106.6075792,
|
||||
10.8075692
|
||||
],
|
||||
[
|
||||
106.6085415,
|
||||
10.8056515
|
||||
],
|
||||
[
|
||||
106.6136709,
|
||||
10.7967177
|
||||
],
|
||||
[
|
||||
106.6152161,
|
||||
10.7936116
|
||||
],
|
||||
[
|
||||
106.6184801,
|
||||
10.7853372
|
||||
],
|
||||
[
|
||||
106.6212335,
|
||||
10.7772689
|
||||
],
|
||||
[
|
||||
106.6214527,
|
||||
10.7762789
|
||||
],
|
||||
[
|
||||
106.6215893,
|
||||
10.7733635
|
||||
],
|
||||
[
|
||||
106.6241619,
|
||||
10.7620157
|
||||
],
|
||||
[
|
||||
106.6244093,
|
||||
10.7617951
|
||||
],
|
||||
[
|
||||
106.6247143,
|
||||
10.7607509
|
||||
],
|
||||
[
|
||||
106.6251395,
|
||||
10.7607852
|
||||
],
|
||||
[
|
||||
106.6253151,
|
||||
10.7605546
|
||||
],
|
||||
[
|
||||
106.6258409,
|
||||
10.7604321
|
||||
],
|
||||
[
|
||||
106.6262646,
|
||||
10.7605981
|
||||
],
|
||||
[
|
||||
106.6281194,
|
||||
10.7604044
|
||||
],
|
||||
[
|
||||
106.6285043,
|
||||
10.7602239
|
||||
],
|
||||
[
|
||||
106.6289643,
|
||||
10.7603333
|
||||
],
|
||||
[
|
||||
106.6296,
|
||||
10.760212
|
||||
],
|
||||
[
|
||||
106.6297542,
|
||||
10.7601607
|
||||
],
|
||||
[
|
||||
106.6298119,
|
||||
10.7597483
|
||||
],
|
||||
[
|
||||
106.6306313,
|
||||
10.7597575
|
||||
],
|
||||
[
|
||||
106.6310014,
|
||||
10.7593728
|
||||
],
|
||||
[
|
||||
106.6318678,
|
||||
10.7588932
|
||||
],
|
||||
[
|
||||
106.6325973,
|
||||
10.7588418
|
||||
],
|
||||
[
|
||||
106.632997,
|
||||
10.758465
|
||||
],
|
||||
[
|
||||
106.6337331,
|
||||
10.7581715
|
||||
],
|
||||
[
|
||||
106.6340344,
|
||||
10.7587757
|
||||
],
|
||||
[
|
||||
106.6350891,
|
||||
10.7600777
|
||||
],
|
||||
[
|
||||
106.6357269,
|
||||
10.7627551
|
||||
],
|
||||
[
|
||||
106.6356122,
|
||||
10.7695551
|
||||
],
|
||||
[
|
||||
106.6376048,
|
||||
10.7689823
|
||||
],
|
||||
[
|
||||
106.6377246,
|
||||
10.7693731
|
||||
],
|
||||
[
|
||||
106.6392536,
|
||||
10.7704387
|
||||
],
|
||||
[
|
||||
106.6415908,
|
||||
10.7725649
|
||||
],
|
||||
[
|
||||
106.6421239,
|
||||
10.7721479
|
||||
],
|
||||
[
|
||||
106.6438671,
|
||||
10.774622
|
||||
],
|
||||
[
|
||||
106.6442952,
|
||||
10.7749853
|
||||
],
|
||||
[
|
||||
106.6453628,
|
||||
10.7752275
|
||||
],
|
||||
[
|
||||
106.6476803,
|
||||
10.7752257
|
||||
],
|
||||
[
|
||||
106.6434517,
|
||||
10.7811899
|
||||
],
|
||||
[
|
||||
106.6427251,
|
||||
10.7828118
|
||||
],
|
||||
[
|
||||
106.6393945,
|
||||
10.7923729
|
||||
],
|
||||
[
|
||||
106.6382682,
|
||||
10.7959026
|
||||
],
|
||||
[
|
||||
106.6367349,
|
||||
10.8018264
|
||||
],
|
||||
[
|
||||
106.6362173,
|
||||
10.8026501
|
||||
],
|
||||
[
|
||||
106.6357729,
|
||||
10.8038388
|
||||
],
|
||||
[
|
||||
106.6302464,
|
||||
10.8219341
|
||||
],
|
||||
[
|
||||
106.6299024,
|
||||
10.8226839
|
||||
],
|
||||
[
|
||||
106.6295221,
|
||||
10.8232091
|
||||
],
|
||||
[
|
||||
106.6278908,
|
||||
10.8246854
|
||||
],
|
||||
[
|
||||
106.6247915,
|
||||
10.8208015
|
||||
],
|
||||
[
|
||||
106.623437,
|
||||
10.8195106
|
||||
],
|
||||
[
|
||||
106.6214038,
|
||||
10.8188256
|
||||
],
|
||||
[
|
||||
106.6196485,
|
||||
10.8186053
|
||||
],
|
||||
[
|
||||
106.6191787,
|
||||
10.8187331
|
||||
],
|
||||
[
|
||||
106.6177833,
|
||||
10.8196421
|
||||
],
|
||||
[
|
||||
106.6160126,
|
||||
10.8196311
|
||||
],
|
||||
[
|
||||
106.6148834,
|
||||
10.8187404
|
||||
],
|
||||
[
|
||||
106.6131346,
|
||||
10.8178989
|
||||
],
|
||||
[
|
||||
106.6108974,
|
||||
10.8160869
|
||||
],
|
||||
[
|
||||
106.6097427,
|
||||
10.8148686
|
||||
],
|
||||
[
|
||||
106.6089918,
|
||||
10.8144877
|
||||
],
|
||||
[
|
||||
106.6076891,
|
||||
10.8130434
|
||||
],
|
||||
[
|
||||
106.6078887,
|
||||
10.8129234
|
||||
],
|
||||
[
|
||||
106.6074036,
|
||||
10.8082441
|
||||
]
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -1,12 +1,13 @@
|
||||
{
|
||||
"code": "DN",
|
||||
"name": "Da Nang",
|
||||
"center": [16.07868,108.08095],
|
||||
"center": [16.0544,108.2022],
|
||||
"bbox": [107.81854,15.91799,108.33864,16.2255],
|
||||
"count": 1521,
|
||||
"source": "mapillary vector tiles z14",
|
||||
"grid": 0.0003,
|
||||
"generatedAt": "2026-08-30T04:26:55.880Z",
|
||||
"reclippedAt": "2026-08-30T07:38:26.488Z",
|
||||
"panos": [
|
||||
{"id":"809174308506012","lat":15.965507,"lng":108.285922},
|
||||
{"id":"2371280393367178","lat":15.965579,"lng":108.285869},
|
||||
|
||||
+3
-21
@@ -1,12 +1,13 @@
|
||||
{
|
||||
"code": "HN",
|
||||
"name": "Ha Noi",
|
||||
"center": [20.99862,105.70059],
|
||||
"center": [21.0285,105.8542],
|
||||
"bbox": [105.28896,20.56452,106.02004,21.38542],
|
||||
"count": 225985,
|
||||
"count": 225966,
|
||||
"source": "mapillary vector tiles z14",
|
||||
"grid": 0.0003,
|
||||
"generatedAt": "2026-08-30T04:33:17.835Z",
|
||||
"reclippedAt": "2026-08-30T07:38:27.243Z",
|
||||
"panos": [
|
||||
{"id":"424902840263020","lat":20.616432,"lng":105.788088},
|
||||
{"id":"314933181439578","lat":20.616583,"lng":105.788287},
|
||||
@@ -32659,7 +32660,6 @@
|
||||
{"id":"1756108371477987","lat":20.960989,"lng":105.769469},
|
||||
{"id":"1525702838285974","lat":20.960989,"lng":105.799048},
|
||||
{"id":"1018756006031412","lat":20.960989,"lng":105.808189},
|
||||
{"id":"255573487475211","lat":20.960989,"lng":105.817035},
|
||||
{"id":"331655722730872","lat":20.960989,"lng":105.837876},
|
||||
{"id":"837865837883479","lat":20.960989,"lng":105.84662},
|
||||
{"id":"1067662564266261","lat":20.960994,"lng":105.735592},
|
||||
@@ -32741,7 +32741,6 @@
|
||||
{"id":"1817497318850395","lat":20.961019,"lng":105.849066},
|
||||
{"id":"881307916939022","lat":20.961024,"lng":105.737008},
|
||||
{"id":"870799727833286","lat":20.961024,"lng":105.756916},
|
||||
{"id":"1599085597166612","lat":20.961024,"lng":105.817174},
|
||||
{"id":"1472721080220323","lat":20.961024,"lng":105.81851},
|
||||
{"id":"651084133827252","lat":20.961024,"lng":105.824288},
|
||||
{"id":"3647810492102486","lat":20.961034,"lng":105.735603},
|
||||
@@ -32761,7 +32760,6 @@
|
||||
{"id":"214146748343408","lat":20.961049,"lng":105.83912},
|
||||
{"id":"3718500998475571","lat":20.961059,"lng":105.797696},
|
||||
{"id":"1071497937604489","lat":20.961064,"lng":105.736493},
|
||||
{"id":"1050409029637964","lat":20.961064,"lng":105.817475},
|
||||
{"id":"1485172482352137","lat":20.961064,"lng":105.874488},
|
||||
{"id":"337956295280143","lat":20.961069,"lng":105.811193},
|
||||
{"id":"765954685529447","lat":20.961074,"lng":105.731703},
|
||||
@@ -32779,9 +32777,7 @@
|
||||
{"id":"728227722473306","lat":20.961099,"lng":105.814809},
|
||||
{"id":"1615758002287490","lat":20.961104,"lng":105.768583},
|
||||
{"id":"1778229482582463","lat":20.961104,"lng":105.794676},
|
||||
{"id":"1361578074780751","lat":20.961104,"lng":105.817775},
|
||||
{"id":"406142145463934","lat":20.961104,"lng":105.873887},
|
||||
{"id":"852446289619271","lat":20.961119,"lng":105.818076},
|
||||
{"id":"3650118995312542","lat":20.961119,"lng":105.843304},
|
||||
{"id":"644370867836133","lat":20.961119,"lng":105.846915},
|
||||
{"id":"354080880640478","lat":20.961124,"lng":105.728731},
|
||||
@@ -54525,7 +54521,6 @@
|
||||
{"id":"2310796399104641","lat":20.985643,"lng":105.858861},
|
||||
{"id":"1469152277856443","lat":20.985643,"lng":105.863619},
|
||||
{"id":"289624557162053","lat":20.985643,"lng":105.878012},
|
||||
{"id":"1181642889600599","lat":20.985648,"lng":105.786415},
|
||||
{"id":"1011635089941562","lat":20.985648,"lng":105.823756},
|
||||
{"id":"721798236441039","lat":20.985648,"lng":105.876907},
|
||||
{"id":"1771029003354524","lat":20.985653,"lng":105.797846},
|
||||
@@ -65092,7 +65087,6 @@
|
||||
{"id":"358456693412662","lat":20.994458,"lng":105.764705},
|
||||
{"id":"865820771677852","lat":20.994458,"lng":105.822233},
|
||||
{"id":"873532194255643","lat":20.994458,"lng":105.825865},
|
||||
{"id":"710547200409739","lat":20.994458,"lng":105.842296},
|
||||
{"id":"857189366007883","lat":20.994458,"lng":105.842838},
|
||||
{"id":"264531656560250","lat":20.994458,"lng":105.884401},
|
||||
{"id":"1044065650563065","lat":20.994458,"lng":105.942889},
|
||||
@@ -65375,7 +65369,6 @@
|
||||
{"id":"24026934410283870","lat":20.994698,"lng":105.934805},
|
||||
{"id":"362808969508446","lat":20.994698,"lng":105.945014},
|
||||
{"id":"1027138218500075","lat":20.994703,"lng":105.729268},
|
||||
{"id":"1272219003472611","lat":20.994703,"lng":105.842441},
|
||||
{"id":"505302308526898","lat":20.994708,"lng":105.940218},
|
||||
{"id":"1174014103367159","lat":20.994713,"lng":105.763798},
|
||||
{"id":"962235428952288","lat":20.994718,"lng":105.584085},
|
||||
@@ -67154,7 +67147,6 @@
|
||||
{"id":"649650463951112","lat":20.996221,"lng":105.792058},
|
||||
{"id":"6745275752186533","lat":20.996221,"lng":105.793485},
|
||||
{"id":"939882110817178","lat":20.996221,"lng":105.820978},
|
||||
{"id":"1573923316680479","lat":20.996221,"lng":105.8428},
|
||||
{"id":"836581378009408","lat":20.996221,"lng":105.846759},
|
||||
{"id":"840398907573298","lat":20.996221,"lng":105.84964},
|
||||
{"id":"902905651965823","lat":20.996221,"lng":105.869681},
|
||||
@@ -68920,7 +68912,6 @@
|
||||
{"id":"1264508530907545","lat":20.997814,"lng":105.817915},
|
||||
{"id":"649861117314748","lat":20.997819,"lng":105.711023},
|
||||
{"id":"223873520523656","lat":20.997819,"lng":105.747598},
|
||||
{"id":"1757365531390374","lat":20.997819,"lng":105.842366},
|
||||
{"id":"851394176287033","lat":20.997819,"lng":105.865953},
|
||||
{"id":"320545740583825","lat":20.997824,"lng":105.785744},
|
||||
{"id":"1229421864395626","lat":20.997824,"lng":105.816547},
|
||||
@@ -69730,7 +69721,6 @@
|
||||
{"id":"2764838380339608","lat":20.99861,"lng":105.738881},
|
||||
{"id":"1497717184395643","lat":20.99861,"lng":105.78959},
|
||||
{"id":"1360184188184793","lat":20.99861,"lng":105.823102},
|
||||
{"id":"995293234924997","lat":20.99861,"lng":105.842022},
|
||||
{"id":"1544590642791740","lat":20.99861,"lng":105.852225},
|
||||
{"id":"1292780402176382","lat":20.99861,"lng":105.871022},
|
||||
{"id":"1845215089227125","lat":20.99861,"lng":105.877524},
|
||||
@@ -70162,7 +70152,6 @@
|
||||
{"id":"1197267131286064","lat":20.998985,"lng":105.870459},
|
||||
{"id":"393644930165108","lat":20.99899,"lng":105.534008},
|
||||
{"id":"863779478289875","lat":20.99899,"lng":105.745205},
|
||||
{"id":"1711061542649589","lat":20.99899,"lng":105.841926},
|
||||
{"id":"1258928978138104","lat":20.99899,"lng":105.853094},
|
||||
{"id":"883997353285925","lat":20.998995,"lng":105.75477},
|
||||
{"id":"482727270971252","lat":20.998995,"lng":105.845879},
|
||||
@@ -70362,7 +70351,6 @@
|
||||
{"id":"1346003583882963","lat":20.999121,"lng":105.873098},
|
||||
{"id":"1646834856182631","lat":20.999121,"lng":105.929988},
|
||||
{"id":"732936798667563","lat":20.999126,"lng":105.739803},
|
||||
{"id":"697288592241199","lat":20.999126,"lng":105.841888},
|
||||
{"id":"790502743696387","lat":20.999126,"lng":105.869574},
|
||||
{"id":"892232661766987","lat":20.999126,"lng":105.915402},
|
||||
{"id":"1083964249714706","lat":20.999126,"lng":105.952502},
|
||||
@@ -70475,7 +70463,6 @@
|
||||
{"id":"1114794193016832","lat":20.999291,"lng":105.78104},
|
||||
{"id":"1727339577695231","lat":20.999291,"lng":105.809691},
|
||||
{"id":"1008882690306677","lat":20.999291,"lng":105.82867},
|
||||
{"id":"999654984589383","lat":20.999291,"lng":105.841781},
|
||||
{"id":"1476209156766631","lat":20.999291,"lng":105.872996},
|
||||
{"id":"1115856346489310","lat":20.999291,"lng":105.906894},
|
||||
{"id":"397369276455457","lat":20.999296,"lng":105.5322},
|
||||
@@ -70749,7 +70736,6 @@
|
||||
{"id":"994624302138007","lat":20.999601,"lng":105.809541},
|
||||
{"id":"354113820598469","lat":20.999601,"lng":105.822158},
|
||||
{"id":"1815372358877220","lat":20.999601,"lng":105.828751},
|
||||
{"id":"1783557778756264","lat":20.999601,"lng":105.841658},
|
||||
{"id":"1644915576049446","lat":20.999601,"lng":105.845606},
|
||||
{"id":"1371645427653389","lat":20.999601,"lng":105.872771},
|
||||
{"id":"1396215391723347","lat":20.999606,"lng":105.412493},
|
||||
@@ -95738,7 +95724,6 @@
|
||||
{"id":"438724371992764","lat":21.018992,"lng":105.553819},
|
||||
{"id":"1199447267708202","lat":21.018992,"lng":105.554409},
|
||||
{"id":"2107501759628519","lat":21.018992,"lng":105.795583},
|
||||
{"id":"214707721614923","lat":21.018992,"lng":105.848122},
|
||||
{"id":"1366613580712524","lat":21.018997,"lng":105.548996},
|
||||
{"id":"420280857185657","lat":21.018997,"lng":105.69633},
|
||||
{"id":"324751107343727","lat":21.018997,"lng":105.800748},
|
||||
@@ -136815,7 +136800,6 @@
|
||||
{"id":"932732428275695","lat":21.053254,"lng":105.731703},
|
||||
{"id":"1032248977926373","lat":21.053254,"lng":105.781292},
|
||||
{"id":"314928354566671","lat":21.053254,"lng":105.797428},
|
||||
{"id":"831210518680284","lat":21.053254,"lng":105.805512},
|
||||
{"id":"230259606574456","lat":21.053254,"lng":105.868785},
|
||||
{"id":"1115961486121818","lat":21.053254,"lng":105.973198},
|
||||
{"id":"935953824681529","lat":21.053259,"lng":105.621598},
|
||||
@@ -217880,7 +217864,6 @@
|
||||
{"id":"852529756307974","lat":21.194485,"lng":105.762489},
|
||||
{"id":"403135945408847","lat":21.19449,"lng":105.7809},
|
||||
{"id":"372564369148914","lat":21.194515,"lng":105.874209},
|
||||
{"id":"3408803739417552","lat":21.194525,"lng":105.90705},
|
||||
{"id":"975148443993316","lat":21.194535,"lng":105.860401},
|
||||
{"id":"1660756007662661","lat":21.194595,"lng":105.8955},
|
||||
{"id":"824803602853287","lat":21.194625,"lng":105.903305},
|
||||
@@ -223162,7 +223145,6 @@
|
||||
{"id":"3635633666540739","lat":21.236602,"lng":105.814825},
|
||||
{"id":"930934717727193","lat":21.236662,"lng":105.814787},
|
||||
{"id":"1212854850524878","lat":21.236687,"lng":105.769184},
|
||||
{"id":"1052924258837031","lat":21.236697,"lng":105.732658},
|
||||
{"id":"910465434181407","lat":21.236702,"lng":105.744942},
|
||||
{"id":"1746835829530446","lat":21.236712,"lng":105.769163},
|
||||
{"id":"1123003582119917","lat":21.236752,"lng":105.744535},
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
// Generated by scripts/build-pano-index.mjs. Do not edit by hand.
|
||||
//
|
||||
// Lists only the city indexes that have actually been built, so a partial
|
||||
// build still compiles.
|
||||
// Lists only the province indexes that have actually been built, so a
|
||||
// partial build still compiles.
|
||||
|
||||
import dh from './dh.json';
|
||||
import dl from './dl.json';
|
||||
import dn from './dn.json';
|
||||
import hn from './hn.json';
|
||||
import la from './la.json';
|
||||
import ld from './ld.json';
|
||||
import tphcm from './tphcm.json';
|
||||
|
||||
export const PANO_INDEXES = {
|
||||
DH: dh,
|
||||
DL: dl,
|
||||
DN: dn,
|
||||
HN: hn,
|
||||
TPHCM: tphcm,
|
||||
"DN": dn,
|
||||
"HN": hn,
|
||||
"LA": la,
|
||||
"LD": ld,
|
||||
"TPHCM": tphcm,
|
||||
};
|
||||
@@ -1,12 +1,13 @@
|
||||
{
|
||||
"code": "DH",
|
||||
"name": "Duc Hoa",
|
||||
"center": [10.88904,106.40896],
|
||||
"code": "LA",
|
||||
"name": "Long An",
|
||||
"center": [10.88908,106.40895],
|
||||
"bbox": [106.27082,10.7409,106.53287,11.02578],
|
||||
"count": 11718,
|
||||
"count": 11717,
|
||||
"source": "mapillary vector tiles z14",
|
||||
"grid": 0.0003,
|
||||
"generatedAt": "2026-08-30T04:26:15.527Z",
|
||||
"reclippedAt": "2026-08-30T07:38:27.347Z",
|
||||
"panos": [
|
||||
{"id":"564462039872493","lat":10.745689,"lng":106.471751},
|
||||
{"id":"566528822988202","lat":10.745731,"lng":106.471789},
|
||||
@@ -2323,7 +2324,6 @@
|
||||
{"id":"502282576179543","lat":10.793429,"lng":106.465802},
|
||||
{"id":"1068891564930333","lat":10.793455,"lng":106.465797},
|
||||
{"id":"1079670583895683","lat":10.793619,"lng":106.473597},
|
||||
{"id":"582738327454394","lat":10.793619,"lng":106.505703},
|
||||
{"id":"990905126134429","lat":10.793634,"lng":106.505681},
|
||||
{"id":"920013016306613","lat":10.793671,"lng":106.481461},
|
||||
{"id":"1628238277769221","lat":10.793677,"lng":106.489073},
|
||||
@@ -1,12 +1,13 @@
|
||||
{
|
||||
"code": "DL",
|
||||
"name": "Da Lat",
|
||||
"center": [11.92057,108.46436],
|
||||
"bbox": [108.31521,11.80798,108.5944,12.00855],
|
||||
"code": "LD",
|
||||
"name": "Lam Dong",
|
||||
"center": [11.92055,108.4644],
|
||||
"bbox": [108.31521,11.80798,108.59461,12.00855],
|
||||
"count": 475,
|
||||
"source": "mapillary vector tiles z14",
|
||||
"grid": 0.0003,
|
||||
"generatedAt": "2026-08-30T04:25:42.884Z",
|
||||
"reclippedAt": "2026-08-30T07:38:27.372Z",
|
||||
"panos": [
|
||||
{"id":"500652592608650","lat":11.933637,"lng":108.460255},
|
||||
{"id":"1032715811583639","lat":11.933716,"lng":108.460277},
|
||||
@@ -1,12 +1,13 @@
|
||||
{
|
||||
"code": "TPHCM",
|
||||
"name": "Ho Chi Minh",
|
||||
"center": [10.66748,106.76447],
|
||||
"center": [10.8231,106.6297],
|
||||
"bbox": [106.46356,10.35828,107.02758,10.92934],
|
||||
"count": 184992,
|
||||
"count": 184938,
|
||||
"source": "mapillary vector tiles z14",
|
||||
"grid": 0.0003,
|
||||
"generatedAt": "2026-08-30T04:29:47.789Z",
|
||||
"reclippedAt": "2026-08-30T07:38:28.062Z",
|
||||
"panos": [
|
||||
{"id":"389711540263441","lat":10.532117,"lng":106.764107},
|
||||
{"id":"840654070870055","lat":10.53218,"lng":106.764321},
|
||||
@@ -22242,7 +22243,6 @@
|
||||
{"id":"869202661716111","lat":10.718983,"lng":106.540775},
|
||||
{"id":"517922627331567","lat":10.718983,"lng":106.553763},
|
||||
{"id":"1181234199577251","lat":10.718983,"lng":106.557582},
|
||||
{"id":"2075583632806742","lat":10.718983,"lng":106.619418},
|
||||
{"id":"1618295305659730","lat":10.718983,"lng":106.663256},
|
||||
{"id":"776183794442651","lat":10.718983,"lng":106.680534},
|
||||
{"id":"960717108852720","lat":10.718983,"lng":106.682986},
|
||||
@@ -22336,7 +22336,6 @@
|
||||
{"id":"943583787158161","lat":10.719056,"lng":106.62358},
|
||||
{"id":"2227491320920066","lat":10.719056,"lng":106.681194},
|
||||
{"id":"1130549901448306","lat":10.719062,"lng":106.604703},
|
||||
{"id":"1032935964703098","lat":10.719062,"lng":106.619407},
|
||||
{"id":"381457391533235","lat":10.719062,"lng":106.664532},
|
||||
{"id":"1058986241825666","lat":10.719072,"lng":106.605615},
|
||||
{"id":"406357928685999","lat":10.719072,"lng":106.6851},
|
||||
@@ -26110,7 +26109,6 @@
|
||||
{"id":"1114492743318790","lat":10.725487,"lng":106.583707},
|
||||
{"id":"1073743133924702","lat":10.725487,"lng":106.679386},
|
||||
{"id":"883989240022840","lat":10.725487,"lng":106.729496},
|
||||
{"id":"1039674343812402","lat":10.725497,"lng":106.62033},
|
||||
{"id":"315815764112576","lat":10.725503,"lng":106.647881},
|
||||
{"id":"2355742077948227","lat":10.725503,"lng":106.696526},
|
||||
{"id":"1021967396166952","lat":10.725508,"lng":106.573799},
|
||||
@@ -26326,7 +26324,6 @@
|
||||
{"id":"736810904698548","lat":10.725819,"lng":106.645483},
|
||||
{"id":"306614628466194","lat":10.725819,"lng":106.650601},
|
||||
{"id":"1031743457891462","lat":10.725829,"lng":106.598089},
|
||||
{"id":"272029405703611","lat":10.725829,"lng":106.620324},
|
||||
{"id":"1611245392746731","lat":10.725829,"lng":106.644593},
|
||||
{"id":"1312930156084801","lat":10.725829,"lng":106.648788},
|
||||
{"id":"3813972848886791","lat":10.725829,"lng":106.678523},
|
||||
@@ -26794,7 +26791,6 @@
|
||||
{"id":"1521260568417458","lat":10.726478,"lng":106.728895},
|
||||
{"id":"709173674608094","lat":10.726478,"lng":106.743293},
|
||||
{"id":"385487190738512","lat":10.726483,"lng":106.613163},
|
||||
{"id":"1134041084642579","lat":10.726483,"lng":106.620228},
|
||||
{"id":"7980254198668260","lat":10.726483,"lng":106.622985},
|
||||
{"id":"395530419625352","lat":10.726483,"lng":106.623328},
|
||||
{"id":"221546550948433","lat":10.726483,"lng":106.624525},
|
||||
@@ -27032,7 +27028,6 @@
|
||||
{"id":"1845786965943474","lat":10.726789,"lng":106.734645},
|
||||
{"id":"1036738807796598","lat":10.726794,"lng":106.558655},
|
||||
{"id":"350607421116486","lat":10.726794,"lng":106.599462},
|
||||
{"id":"758808772388407","lat":10.726794,"lng":106.620271},
|
||||
{"id":"747574060284568","lat":10.726794,"lng":106.62792},
|
||||
{"id":"1094718035099785","lat":10.726794,"lng":106.674473},
|
||||
{"id":"955628525916882","lat":10.726794,"lng":106.685094},
|
||||
@@ -27051,7 +27046,6 @@
|
||||
{"id":"354319877453083","lat":10.726799,"lng":106.614273},
|
||||
{"id":"754231672916538","lat":10.726799,"lng":106.614965},
|
||||
{"id":"1482705808973043","lat":10.726799,"lng":106.61709},
|
||||
{"id":"916949929648024","lat":10.726799,"lng":106.620319},
|
||||
{"id":"382859117608025","lat":10.726799,"lng":106.631809},
|
||||
{"id":"6649220141769289","lat":10.726799,"lng":106.642308},
|
||||
{"id":"339840468818696","lat":10.726799,"lng":106.649464},
|
||||
@@ -27155,7 +27149,6 @@
|
||||
{"id":"896114821487638","lat":10.72691,"lng":106.729807},
|
||||
{"id":"403611372231464","lat":10.726915,"lng":106.612203},
|
||||
{"id":"915321413469274","lat":10.726915,"lng":106.617009},
|
||||
{"id":"355450387375361","lat":10.726915,"lng":106.620292},
|
||||
{"id":"824273729378958","lat":10.72692,"lng":106.590289},
|
||||
{"id":"1520867045425273","lat":10.726926,"lng":106.646996},
|
||||
{"id":"2056737348052515","lat":10.726931,"lng":106.596002},
|
||||
@@ -27218,7 +27211,6 @@
|
||||
{"id":"1108925570126174","lat":10.727073,"lng":106.600208},
|
||||
{"id":"720513350224086","lat":10.727073,"lng":106.610872},
|
||||
{"id":"7533078850070284","lat":10.727073,"lng":106.613452},
|
||||
{"id":"366912966060195","lat":10.727073,"lng":106.620319},
|
||||
{"id":"7735747439798137","lat":10.727073,"lng":106.681484},
|
||||
{"id":"946711180379501","lat":10.727073,"lng":106.693152},
|
||||
{"id":"1160167102009718","lat":10.727073,"lng":106.694938},
|
||||
@@ -27437,7 +27429,6 @@
|
||||
{"id":"698957615676242","lat":10.727374,"lng":106.581346},
|
||||
{"id":"1194306288441725","lat":10.727374,"lng":106.601801},
|
||||
{"id":"1682655502263739","lat":10.727374,"lng":106.610813},
|
||||
{"id":"2768136263334337","lat":10.727374,"lng":106.620351},
|
||||
{"id":"254629487642729","lat":10.727374,"lng":106.650982},
|
||||
{"id":"2240818179453652","lat":10.727374,"lng":106.651953},
|
||||
{"id":"3763334003904362","lat":10.727374,"lng":106.654324},
|
||||
@@ -27689,7 +27680,6 @@
|
||||
{"id":"509030675468885","lat":10.727669,"lng":106.679971},
|
||||
{"id":"731616749145465","lat":10.727669,"lng":106.695904},
|
||||
{"id":"2509427375932485","lat":10.727674,"lng":106.558628},
|
||||
{"id":"692519916417327","lat":10.727674,"lng":106.620373},
|
||||
{"id":"728799898839514","lat":10.727674,"lng":106.632249},
|
||||
{"id":"384244247314366","lat":10.727674,"lng":106.649426},
|
||||
{"id":"693719172568264","lat":10.727674,"lng":106.650424},
|
||||
@@ -27949,7 +27939,6 @@
|
||||
{"id":"858256042818475","lat":10.727985,"lng":106.558617},
|
||||
{"id":"1144807866634880","lat":10.727985,"lng":106.576974},
|
||||
{"id":"973127321282429","lat":10.727985,"lng":106.601908},
|
||||
{"id":"763566038474136","lat":10.727985,"lng":106.620394},
|
||||
{"id":"800611141677899","lat":10.727985,"lng":106.641943},
|
||||
{"id":"1084261496340644","lat":10.727985,"lng":106.649501},
|
||||
{"id":"275620828830387","lat":10.727985,"lng":106.650462},
|
||||
@@ -28176,7 +28165,6 @@
|
||||
{"id":"999659404458039","lat":10.728275,"lng":106.726486},
|
||||
{"id":"361556539784495","lat":10.728275,"lng":106.7289},
|
||||
{"id":"1165157964791866","lat":10.72828,"lng":106.579206},
|
||||
{"id":"297243493338349","lat":10.72828,"lng":106.620421},
|
||||
{"id":"210760552108072","lat":10.72828,"lng":106.646953},
|
||||
{"id":"210373212155725","lat":10.72828,"lng":106.651014},
|
||||
{"id":"1850069445451501","lat":10.72828,"lng":106.667215},
|
||||
@@ -28419,7 +28407,6 @@
|
||||
{"id":"234839829597427","lat":10.728596,"lng":106.614965},
|
||||
{"id":"344237145180226","lat":10.728596,"lng":106.61909},
|
||||
{"id":"908510774254192","lat":10.728596,"lng":106.619112},
|
||||
{"id":"409716011573971","lat":10.728596,"lng":106.620458},
|
||||
{"id":"324144297245177","lat":10.728596,"lng":106.647903},
|
||||
{"id":"1391900578114946","lat":10.728596,"lng":106.65103},
|
||||
{"id":"1075345610553216","lat":10.728596,"lng":106.651508},
|
||||
@@ -28593,7 +28580,6 @@
|
||||
{"id":"1043987413572045","lat":10.728892,"lng":106.612401},
|
||||
{"id":"915303113513560","lat":10.728892,"lng":106.617604},
|
||||
{"id":"370091185731697","lat":10.728892,"lng":106.618999},
|
||||
{"id":"881979780339867","lat":10.728892,"lng":106.620491},
|
||||
{"id":"1559643548189106","lat":10.728892,"lng":106.625136},
|
||||
{"id":"1093866925127578","lat":10.728892,"lng":106.632775},
|
||||
{"id":"901711184946528","lat":10.728892,"lng":106.650483},
|
||||
@@ -28756,7 +28742,6 @@
|
||||
{"id":"993266061778764","lat":10.729171,"lng":106.724399},
|
||||
{"id":"883948270089993","lat":10.729176,"lng":106.616966},
|
||||
{"id":"1294431204567539","lat":10.729176,"lng":106.618795},
|
||||
{"id":"384939570805285","lat":10.729176,"lng":106.620528},
|
||||
{"id":"893655425535758","lat":10.729176,"lng":106.650445},
|
||||
{"id":"374957228414704","lat":10.729176,"lng":106.654378},
|
||||
{"id":"371889022060543","lat":10.729176,"lng":106.672477},
|
||||
@@ -28953,7 +28938,6 @@
|
||||
{"id":"3634691873480713","lat":10.729456,"lng":106.630501},
|
||||
{"id":"3611330259194876","lat":10.729461,"lng":106.613093},
|
||||
{"id":"1340070196959756","lat":10.729461,"lng":106.628687},
|
||||
{"id":"3651691601741030","lat":10.729466,"lng":106.620555},
|
||||
{"id":"166190453247808","lat":10.729466,"lng":106.728305},
|
||||
{"id":"1698622220666213","lat":10.729471,"lng":106.61342},
|
||||
{"id":"1113555069984874","lat":10.729471,"lng":106.704154},
|
||||
@@ -29171,8 +29155,6 @@
|
||||
{"id":"1144905913147301","lat":10.729766,"lng":106.687503},
|
||||
{"id":"581401204104645","lat":10.729766,"lng":106.700404},
|
||||
{"id":"1530439874232344","lat":10.729772,"lng":106.602225},
|
||||
{"id":"852982633251494","lat":10.729772,"lng":106.620587},
|
||||
{"id":"1107904950395902","lat":10.729772,"lng":106.620614},
|
||||
{"id":"1158604731772729","lat":10.729772,"lng":106.687814},
|
||||
{"id":"387651754326449","lat":10.729777,"lng":106.577409},
|
||||
{"id":"1471296533443933","lat":10.729777,"lng":106.603432},
|
||||
@@ -29299,7 +29281,6 @@
|
||||
{"id":"1736749013503611","lat":10.729872,"lng":106.606205},
|
||||
{"id":"351558517657362","lat":10.729872,"lng":106.618195},
|
||||
{"id":"371561982153290","lat":10.729882,"lng":106.626874},
|
||||
{"id":"1873968699703566","lat":10.729888,"lng":106.620598},
|
||||
{"id":"934310914332508","lat":10.729893,"lng":106.586711},
|
||||
{"id":"799316361910472","lat":10.729893,"lng":106.640993},
|
||||
{"id":"756415126513090","lat":10.729893,"lng":106.721406},
|
||||
@@ -29417,7 +29398,6 @@
|
||||
{"id":"911214590467553","lat":10.730093,"lng":106.690979},
|
||||
{"id":"204029552542055","lat":10.730093,"lng":106.702561},
|
||||
{"id":"1408251400081120","lat":10.730099,"lng":106.617277},
|
||||
{"id":"1448335919229230","lat":10.730099,"lng":106.62063},
|
||||
{"id":"895820572333304","lat":10.730099,"lng":106.630136},
|
||||
{"id":"1830956784014606","lat":10.730099,"lng":106.630688},
|
||||
{"id":"893986395378668","lat":10.730099,"lng":106.645328},
|
||||
@@ -29519,7 +29499,6 @@
|
||||
{"id":"1088439388830412","lat":10.730193,"lng":106.652114},
|
||||
{"id":"360993849922068","lat":10.730199,"lng":106.730402},
|
||||
{"id":"1045945369861307","lat":10.730199,"lng":106.742418},
|
||||
{"id":"755714256100130","lat":10.730204,"lng":106.62093},
|
||||
{"id":"6516783438397668","lat":10.730204,"lng":106.713295},
|
||||
{"id":"1150877142602033","lat":10.730204,"lng":106.717806},
|
||||
{"id":"1025067565392885","lat":10.730204,"lng":106.729222},
|
||||
@@ -29608,7 +29587,6 @@
|
||||
{"id":"1243680283491180","lat":10.730383,"lng":106.602327},
|
||||
{"id":"359855483353039","lat":10.730383,"lng":106.603555},
|
||||
{"id":"777753787540129","lat":10.730383,"lng":106.605894},
|
||||
{"id":"343652501833448","lat":10.730383,"lng":106.620668},
|
||||
{"id":"666919125631316","lat":10.730383,"lng":106.635141},
|
||||
{"id":"780845193881918","lat":10.730383,"lng":106.646084},
|
||||
{"id":"858428089099952","lat":10.730383,"lng":106.653476},
|
||||
@@ -29778,7 +29756,6 @@
|
||||
{"id":"803891508469547","lat":10.730668,"lng":106.707888},
|
||||
{"id":"3178418612294494","lat":10.730673,"lng":106.534917},
|
||||
{"id":"1257579448982217","lat":10.730673,"lng":106.576223},
|
||||
{"id":"1308688146472435","lat":10.730673,"lng":106.62092},
|
||||
{"id":"319607954312819","lat":10.730673,"lng":106.634105},
|
||||
{"id":"991550486020280","lat":10.730673,"lng":106.704964},
|
||||
{"id":"457042173649854","lat":10.730673,"lng":106.708172},
|
||||
@@ -29792,7 +29769,6 @@
|
||||
{"id":"303765089248570","lat":10.730678,"lng":106.725016},
|
||||
{"id":"696626569286676","lat":10.730684,"lng":106.600412},
|
||||
{"id":"1127641675070852","lat":10.730684,"lng":106.615491},
|
||||
{"id":"3490747427852607","lat":10.730684,"lng":106.62071},
|
||||
{"id":"622701053334198","lat":10.730684,"lng":106.632925},
|
||||
{"id":"191619423862078","lat":10.730684,"lng":106.688989},
|
||||
{"id":"6367487589979686","lat":10.730684,"lng":106.703993},
|
||||
@@ -29963,7 +29939,6 @@
|
||||
{"id":"2503475439813694","lat":10.730973,"lng":106.577677},
|
||||
{"id":"1088465032295014","lat":10.730973,"lng":106.581309},
|
||||
{"id":"1567882784078719","lat":10.730973,"lng":106.602434},
|
||||
{"id":"3479631049016266","lat":10.730973,"lng":106.620748},
|
||||
{"id":"1082715573174107","lat":10.730973,"lng":106.653487},
|
||||
{"id":"1461598574419781","lat":10.730973,"lng":106.674006},
|
||||
{"id":"1276423519913059","lat":10.730973,"lng":106.705404},
|
||||
@@ -30136,7 +30111,6 @@
|
||||
{"id":"221414947680592","lat":10.731258,"lng":106.686875},
|
||||
{"id":"914789567261541","lat":10.731258,"lng":106.692599},
|
||||
{"id":"462613019809589","lat":10.731269,"lng":106.519173},
|
||||
{"id":"393133670074452","lat":10.731269,"lng":106.62078},
|
||||
{"id":"398408432773465","lat":10.731269,"lng":106.628703},
|
||||
{"id":"620581336696606","lat":10.731269,"lng":106.641009},
|
||||
{"id":"1828837177575826","lat":10.731269,"lng":106.687192},
|
||||
@@ -30372,7 +30346,6 @@
|
||||
{"id":"798087588703568","lat":10.731595,"lng":106.590611},
|
||||
{"id":"203936459469866","lat":10.731595,"lng":106.597375},
|
||||
{"id":"269825332793905","lat":10.731595,"lng":106.618474},
|
||||
{"id":"362465896717083","lat":10.731595,"lng":106.620818},
|
||||
{"id":"1069725947633356","lat":10.731595,"lng":106.621188},
|
||||
{"id":"1384560512448875","lat":10.731595,"lng":106.621215},
|
||||
{"id":"935549194805695","lat":10.731595,"lng":106.635758},
|
||||
@@ -30427,7 +30400,6 @@
|
||||
{"id":"710507957460981","lat":10.731622,"lng":106.726502},
|
||||
{"id":"362391839782840","lat":10.731622,"lng":106.73435},
|
||||
{"id":"1504298373517759","lat":10.731627,"lng":106.535277},
|
||||
{"id":"1460430661557623","lat":10.731627,"lng":106.620904},
|
||||
{"id":"389552166942053","lat":10.731627,"lng":106.665283},
|
||||
{"id":"343882862091213","lat":10.731632,"lng":106.558505},
|
||||
{"id":"790647819569693","lat":10.731638,"lng":106.618779},
|
||||
@@ -30507,7 +30479,6 @@
|
||||
{"id":"370451262190147","lat":10.731875,"lng":106.725692},
|
||||
{"id":"211684891995721","lat":10.73188,"lng":106.602643},
|
||||
{"id":"2696547307181491","lat":10.73188,"lng":106.613957},
|
||||
{"id":"1456373888276252","lat":10.73188,"lng":106.620855},
|
||||
{"id":"735982454724293","lat":10.73188,"lng":106.626284},
|
||||
{"id":"298202463244349","lat":10.73188,"lng":106.633542},
|
||||
{"id":"3663196807332854","lat":10.73188,"lng":106.634223},
|
||||
@@ -30723,7 +30694,6 @@
|
||||
{"id":"371473685584558","lat":10.732165,"lng":106.616977},
|
||||
{"id":"352885510884310","lat":10.732165,"lng":106.622116},
|
||||
{"id":"269197692404129","lat":10.732165,"lng":106.737923},
|
||||
{"id":"1348695912454768","lat":10.73217,"lng":106.620888},
|
||||
{"id":"3200716270233188","lat":10.73217,"lng":106.725504},
|
||||
{"id":"1058571715419613","lat":10.732175,"lng":106.581309},
|
||||
{"id":"2171403723207344","lat":10.732175,"lng":106.674022},
|
||||
@@ -30744,7 +30714,6 @@
|
||||
{"id":"1525382808018239","lat":10.732191,"lng":106.577951},
|
||||
{"id":"1209579160257129","lat":10.732191,"lng":106.602654},
|
||||
{"id":"341595271696250","lat":10.732191,"lng":106.611682},
|
||||
{"id":"394184176520074","lat":10.732191,"lng":106.620904},
|
||||
{"id":"1136622114371128","lat":10.732191,"lng":106.621805},
|
||||
{"id":"875290010715589","lat":10.732191,"lng":106.625099},
|
||||
{"id":"395115543196853","lat":10.732191,"lng":106.63116},
|
||||
@@ -30843,7 +30812,6 @@
|
||||
{"id":"1961917734189807","lat":10.732249,"lng":106.679424},
|
||||
{"id":"323282716747601","lat":10.732254,"lng":106.621478},
|
||||
{"id":"354216963795005","lat":10.732254,"lng":106.742708},
|
||||
{"id":"7564212903612660","lat":10.732259,"lng":106.620898},
|
||||
{"id":"820525846414443","lat":10.732259,"lng":106.671619},
|
||||
{"id":"352944084502806","lat":10.732265,"lng":106.569008},
|
||||
{"id":"239504068990876","lat":10.732265,"lng":106.70579},
|
||||
@@ -30941,7 +30909,6 @@
|
||||
{"id":"1224166209020215","lat":10.732491,"lng":106.534842},
|
||||
{"id":"1133977510949699","lat":10.732491,"lng":106.611865},
|
||||
{"id":"925193435891586","lat":10.732491,"lng":106.619203},
|
||||
{"id":"368432549257099","lat":10.732491,"lng":106.620925},
|
||||
{"id":"932514221767090","lat":10.732491,"lng":106.622942},
|
||||
{"id":"742886821066794","lat":10.732491,"lng":106.65169},
|
||||
{"id":"339606689095087","lat":10.732491,"lng":106.711798},
|
||||
@@ -31116,7 +31083,6 @@
|
||||
{"id":"316071198119105","lat":10.732781,"lng":106.600841},
|
||||
{"id":"1422182048447959","lat":10.732781,"lng":106.602783},
|
||||
{"id":"995363798671319","lat":10.732781,"lng":106.612047},
|
||||
{"id":"1413168112907720","lat":10.732781,"lng":106.620947},
|
||||
{"id":"1410020253243081","lat":10.732781,"lng":106.626547},
|
||||
{"id":"770599505095309","lat":10.732781,"lng":106.633204},
|
||||
{"id":"902989417916053","lat":10.732781,"lng":106.668218},
|
||||
@@ -31312,7 +31278,6 @@
|
||||
{"id":"1855968941519327","lat":10.733071,"lng":106.672815},
|
||||
{"id":"1656036114912753","lat":10.733071,"lng":106.718992},
|
||||
{"id":"2070069076683924","lat":10.733076,"lng":106.619997},
|
||||
{"id":"1602441767227341","lat":10.733076,"lng":106.620973},
|
||||
{"id":"738628434874824","lat":10.733076,"lng":106.634771},
|
||||
{"id":"390172940026829","lat":10.733076,"lng":106.690539},
|
||||
{"id":"395910026100056","lat":10.733076,"lng":106.736137},
|
||||
@@ -31434,7 +31399,6 @@
|
||||
{"id":"918523866141898","lat":10.733171,"lng":106.621526},
|
||||
{"id":"1427372331318450","lat":10.733171,"lng":106.632898},
|
||||
{"id":"473672885271339","lat":10.733182,"lng":106.534805},
|
||||
{"id":"1163386471294641","lat":10.733187,"lng":106.621183},
|
||||
{"id":"1967817353599503","lat":10.733187,"lng":106.643686},
|
||||
{"id":"376745494969127","lat":10.733192,"lng":106.616119},
|
||||
{"id":"1457915551731643","lat":10.733192,"lng":106.691414},
|
||||
@@ -31542,7 +31506,6 @@
|
||||
{"id":"263523743374261","lat":10.733398,"lng":106.741903},
|
||||
{"id":"446739681495796","lat":10.733403,"lng":106.558505},
|
||||
{"id":"7122688127830506","lat":10.733403,"lng":106.588824},
|
||||
{"id":"294349759936907","lat":10.733403,"lng":106.621011},
|
||||
{"id":"637363965077659","lat":10.733403,"lng":106.627663},
|
||||
{"id":"302779659017711","lat":10.733403,"lng":106.632464},
|
||||
{"id":"919133412916233","lat":10.733403,"lng":106.653514},
|
||||
@@ -31771,7 +31734,6 @@
|
||||
{"id":"991603889181263","lat":10.733709,"lng":106.558923},
|
||||
{"id":"3665642197051237","lat":10.733709,"lng":106.601168},
|
||||
{"id":"758513372855100","lat":10.733709,"lng":106.602654},
|
||||
{"id":"2213691158836832","lat":10.733709,"lng":106.621048},
|
||||
{"id":"2992274397570774","lat":10.733709,"lng":106.632957},
|
||||
{"id":"1217809589196143","lat":10.733709,"lng":106.641374},
|
||||
{"id":"1438906463728729","lat":10.733709,"lng":106.644282},
|
||||
@@ -31999,7 +31961,6 @@
|
||||
{"id":"781748010490430","lat":10.734015,"lng":106.550179},
|
||||
{"id":"692633253072916","lat":10.734015,"lng":106.589849},
|
||||
{"id":"675199721462771","lat":10.734015,"lng":106.60826},
|
||||
{"id":"2093594041033190","lat":10.734015,"lng":106.621086},
|
||||
{"id":"344713804940733","lat":10.734015,"lng":106.668406},
|
||||
{"id":"910061157439756","lat":10.734015,"lng":106.690807},
|
||||
{"id":"342307698556018","lat":10.734015,"lng":106.693087},
|
||||
@@ -32209,7 +32170,6 @@
|
||||
{"id":"1625365374641364","lat":10.734315,"lng":106.702062},
|
||||
{"id":"1547155422785032","lat":10.73432,"lng":106.601409},
|
||||
{"id":"2767020413444911","lat":10.73432,"lng":106.608045},
|
||||
{"id":"936354891335540","lat":10.73432,"lng":106.621118},
|
||||
{"id":"3021452311345477","lat":10.73432,"lng":106.624793},
|
||||
{"id":"1383428629202918","lat":10.73432,"lng":106.659919},
|
||||
{"id":"1550162719096588","lat":10.73432,"lng":106.718187},
|
||||
@@ -44866,7 +44826,6 @@
|
||||
{"id":"1026912661757805","lat":10.749657,"lng":106.727977},
|
||||
{"id":"3639905276272409","lat":10.749662,"lng":106.645516},
|
||||
{"id":"924772925850760","lat":10.749662,"lng":106.655847},
|
||||
{"id":"904185627777749","lat":10.749662,"lng":106.669199},
|
||||
{"id":"1742979146485379","lat":10.749662,"lng":106.675422},
|
||||
{"id":"3593206534284545","lat":10.749662,"lng":106.717683},
|
||||
{"id":"1540091600165803","lat":10.749668,"lng":106.590922},
|
||||
@@ -63674,7 +63633,6 @@
|
||||
{"id":"2311795462352063","lat":10.771207,"lng":106.619112},
|
||||
{"id":"7547893308558185","lat":10.771207,"lng":106.635774},
|
||||
{"id":"754427676682120","lat":10.771207,"lng":106.644995},
|
||||
{"id":"8586167881470315","lat":10.771207,"lng":106.652747},
|
||||
{"id":"341477612089102","lat":10.771207,"lng":106.662253},
|
||||
{"id":"1798963357238368","lat":10.771207,"lng":106.668679},
|
||||
{"id":"2127672317581819","lat":10.771207,"lng":106.674306},
|
||||
@@ -66284,7 +66242,6 @@
|
||||
{"id":"1267176841191548","lat":10.773599,"lng":106.621601},
|
||||
{"id":"1093743312351580","lat":10.773599,"lng":106.629438},
|
||||
{"id":"992561181814673","lat":10.773599,"lng":106.648917},
|
||||
{"id":"954358642699857","lat":10.773599,"lng":106.653042},
|
||||
{"id":"1073481937182509","lat":10.773599,"lng":106.654817},
|
||||
{"id":"312171768407227","lat":10.773599,"lng":106.656534},
|
||||
{"id":"1741331899694571","lat":10.773599,"lng":106.657194},
|
||||
@@ -66777,7 +66734,6 @@
|
||||
{"id":"590144237030260","lat":10.774184,"lng":106.627867},
|
||||
{"id":"217017891408923","lat":10.774184,"lng":106.648112},
|
||||
{"id":"329116359853173","lat":10.774184,"lng":106.648498},
|
||||
{"id":"913391510460951","lat":10.774184,"lng":106.653117},
|
||||
{"id":"970626714568415","lat":10.774184,"lng":106.664506},
|
||||
{"id":"347420058093048","lat":10.774184,"lng":106.671823},
|
||||
{"id":"24324253347220264","lat":10.774184,"lng":106.723836},
|
||||
@@ -67060,7 +67016,6 @@
|
||||
{"id":"1095894834920256","lat":10.774495,"lng":106.632448},
|
||||
{"id":"663012642646823","lat":10.774495,"lng":106.63653},
|
||||
{"id":"1060548418526428","lat":10.774495,"lng":106.64831},
|
||||
{"id":"901750884930894","lat":10.774495,"lng":106.653138},
|
||||
{"id":"1565042817645741","lat":10.774495,"lng":106.671876},
|
||||
{"id":"263135573296247","lat":10.774495,"lng":106.686333},
|
||||
{"id":"792839732197771","lat":10.774495,"lng":106.729571},
|
||||
@@ -76892,7 +76847,6 @@
|
||||
{"id":"1490596681616508","lat":10.784376,"lng":106.586754},
|
||||
{"id":"1216131142955074","lat":10.784376,"lng":106.594484},
|
||||
{"id":"509628938698195","lat":10.784376,"lng":106.618844},
|
||||
{"id":"912910303541467","lat":10.784376,"lng":106.642195},
|
||||
{"id":"1570129313802123","lat":10.784376,"lng":106.661711},
|
||||
{"id":"558736836478662","lat":10.784376,"lng":106.69409},
|
||||
{"id":"1494897421353898","lat":10.784376,"lng":106.735353},
|
||||
@@ -77854,7 +77808,6 @@
|
||||
{"id":"1169065591146265","lat":10.785219,"lng":106.814232},
|
||||
{"id":"871152897986084","lat":10.785224,"lng":106.749317},
|
||||
{"id":"3597839097146995","lat":10.785229,"lng":106.635291},
|
||||
{"id":"1513561276112058","lat":10.785229,"lng":106.641889},
|
||||
{"id":"894353755028372","lat":10.785229,"lng":106.647291},
|
||||
{"id":"1570629220007751","lat":10.785229,"lng":106.653305},
|
||||
{"id":"1300630140525604","lat":10.785229,"lng":106.67848},
|
||||
@@ -78832,7 +78785,6 @@
|
||||
{"id":"657064613262515","lat":10.786025,"lng":106.7646},
|
||||
{"id":"1389718048560573","lat":10.78603,"lng":106.60171},
|
||||
{"id":"651741510457010","lat":10.78603,"lng":106.629621},
|
||||
{"id":"8674420159317746","lat":10.78603,"lng":106.641605},
|
||||
{"id":"1322126528494764","lat":10.78603,"lng":106.663792},
|
||||
{"id":"1451757595408387","lat":10.786036,"lng":106.590289},
|
||||
{"id":"1482801549120682","lat":10.786036,"lng":106.615802},
|
||||
@@ -82827,7 +82779,6 @@
|
||||
{"id":"1078335316927849","lat":10.789477,"lng":106.62976},
|
||||
{"id":"885892713056564","lat":10.789477,"lng":106.637791},
|
||||
{"id":"215776984883750","lat":10.789477,"lng":106.640344},
|
||||
{"id":"2567314493452341","lat":10.789477,"lng":106.640409},
|
||||
{"id":"1051279729619134","lat":10.789477,"lng":106.690019},
|
||||
{"id":"1063732531612948","lat":10.789477,"lng":106.696681},
|
||||
{"id":"1030614581996656","lat":10.789477,"lng":106.714835},
|
||||
@@ -92092,7 +92043,6 @@
|
||||
{"id":"660547606282855","lat":10.796707,"lng":106.683844},
|
||||
{"id":"755080063326902","lat":10.796707,"lng":106.687959},
|
||||
{"id":"308902308684797","lat":10.796707,"lng":106.690464},
|
||||
{"id":"232569889864686","lat":10.796707,"lng":106.691971},
|
||||
{"id":"886326456205945","lat":10.796707,"lng":106.695324},
|
||||
{"id":"1993147544431115","lat":10.796707,"lng":106.703607},
|
||||
{"id":"792709422167633","lat":10.796707,"lng":106.722377},
|
||||
@@ -92987,7 +92937,6 @@
|
||||
{"id":"892956768807329","lat":10.797571,"lng":106.686661},
|
||||
{"id":"1391745975106166","lat":10.797571,"lng":106.687079},
|
||||
{"id":"2700641240075238","lat":10.797571,"lng":106.68797},
|
||||
{"id":"751041336838503","lat":10.797571,"lng":106.69202},
|
||||
{"id":"207244425403571","lat":10.797571,"lng":106.704749},
|
||||
{"id":"832195191891373","lat":10.797571,"lng":106.724222},
|
||||
{"id":"358444699928859","lat":10.797571,"lng":106.738181},
|
||||
@@ -96233,7 +96182,6 @@
|
||||
{"id":"329346086692004","lat":10.800348,"lng":106.779894},
|
||||
{"id":"934810305048717","lat":10.800353,"lng":106.665241},
|
||||
{"id":"271578665287348","lat":10.800353,"lng":106.666448},
|
||||
{"id":"1026240492538525","lat":10.800353,"lng":106.66744},
|
||||
{"id":"895801912165306","lat":10.800353,"lng":106.741968},
|
||||
{"id":"339249652264968","lat":10.800358,"lng":106.569008},
|
||||
{"id":"1020566369166246","lat":10.800358,"lng":106.783494},
|
||||
@@ -119427,7 +119375,6 @@
|
||||
{"id":"734522548741115","lat":10.824481,"lng":106.685963},
|
||||
{"id":"689886593121315","lat":10.824486,"lng":106.691403},
|
||||
{"id":"1144866656476505","lat":10.824486,"lng":106.703982},
|
||||
{"id":"373618955201903","lat":10.824491,"lng":106.709674},
|
||||
{"id":"344545841948055","lat":10.824491,"lng":106.796717},
|
||||
{"id":"678930210821170","lat":10.824496,"lng":106.601104},
|
||||
{"id":"749135060566921","lat":10.824496,"lng":106.60378},
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
// Generated by scripts/build-region-boundaries.mjs. Do not edit by hand.
|
||||
//
|
||||
// The region tree: country > province > district. Safe to import from a
|
||||
// client component -- it holds names and extents, never panorama data.
|
||||
//
|
||||
// A node marked coverage: 'unresolved' has no boundary. That is recorded,
|
||||
// not a build failure; see the Coverage note in docs/project-overview.md.
|
||||
|
||||
export const REGIONS = {
|
||||
"VN": {"code":"VN","name":"Vietnam","parent":null,"level":"country","children":["HN","TPHCM","DN","LD","LA"],"center":[15.87185,106.94179],"bbox":[105.28896,10.35828,108.59461,21.38542]},
|
||||
"HN": {"code":"HN","name":"Ha Noi","parent":"VN","level":"province","children":["HN-BADINH","HN-HOANKIEM","HN-TAYHO","HN-LONGBIEN","HN-CAUGIAY","HN-DONGDA","HN-HAIBATRUNG","HN-HOANGMAI","HN-THANHXUAN","HN-BACTULIEM","HN-NAMTULIEM","HN-HADONG","HN-SONTAY","HN-BAVI","HN-CHUONGMY","HN-DANPHUONG","HN-DONGANH","HN-GIALAM","HN-HOAIDUC","HN-MELINH","HN-MYDUC","HN-PHUXUYEN","HN-PHUCTHO","HN-QUOCOAI","HN-SOCSON","HN-THACHTHAT","HN-THANHOAI","HN-THANHTRI","HN-THUONGTIN","HN-UNGHOA"],"center":[21.0285,105.8542],"bbox":[105.28896,20.56452,106.02004,21.38542]},
|
||||
"HN-BADINH": {"code":"HN-BADINH","name":"Ba Dinh","parent":"HN","level":"district","children":[],"center":[21.03578,105.826],"bbox":[105.80135,21.01596,105.85597,21.05337]},
|
||||
"HN-HOANKIEM": {"code":"HN-HOANKIEM","name":"Hoan Kiem","parent":"HN","level":"district","children":[],"center":[21.02901,105.85378],"bbox":[105.84093,21.01813,105.86728,21.04321]},
|
||||
"HN-TAYHO": {"code":"HN-TAYHO","name":"Tay Ho","parent":"HN","level":"district","children":[],"center":[21.06931,105.81988],"bbox":[105.79178,21.03951,105.84828,21.09737]},
|
||||
"HN-LONGBIEN": {"code":"HN-LONGBIEN","name":"Long Bien","parent":"HN","level":"district","children":[],"center":[21.04246,105.89397],"bbox":[105.84106,20.99411,105.93877,21.08081]},
|
||||
"HN-CAUGIAY": {"code":"HN-CAUGIAY","name":"Cau Giay","parent":"HN","level":"district","children":[],"center":[21.0306,105.79168],"bbox":[105.76716,21.00066,105.81352,21.05879]},
|
||||
"HN-DONGDA": {"code":"HN-DONGDA","name":"Dong Da","parent":"HN","level":"district","children":[],"center":[21.01547,105.82503],"bbox":[105.79763,20.99742,105.84263,21.03215]},
|
||||
"HN-HAIBATRUNG": {"code":"HN-HAIBATRUNG","name":"Hai Ba Trung","parent":"HN","level":"district","children":[],"center":[21.00564,105.8585],"bbox":[105.8409,20.98996,105.88241,21.02127]},
|
||||
"HN-HOANGMAI": {"code":"HN-HOANGMAI","name":"Hoang Mai","parent":"HN","level":"district","children":[],"center":[20.97454,105.86322],"bbox":[105.80739,20.94544,105.90879,21.00376]},
|
||||
"HN-THANHXUAN": {"code":"HN-THANHXUAN","name":"Thanh Xuan","parent":"HN","level":"district","children":[],"center":[20.99482,105.81531],"bbox":[105.79218,20.98056,105.84287,21.00885]},
|
||||
"HN-BACTULIEM": {"code":"HN-BACTULIEM","name":"Bac Tu Liem","parent":"HN","level":"district","children":[],"center":[21.07198,105.7613],"bbox":[105.71721,21.03798,105.80499,21.10812]},
|
||||
"HN-NAMTULIEM": {"code":"HN-NAMTULIEM","name":"Nam Tu Liem","parent":"HN","level":"district","children":[],"center":[21.01256,105.76151],"bbox":[105.72656,20.97966,105.7992,21.05656]},
|
||||
"HN-HADONG": {"code":"HN-HADONG","name":"Ha Dong","parent":"HN","level":"district","children":[],"center":[20.95356,105.75706],"bbox":[105.70525,20.91082,105.8043,20.99698]},
|
||||
"HN-SONTAY": {"code":"HN-SONTAY","name":"Son Tay","parent":"HN","level":"district","children":[],"center":[21.09694,105.47954],"bbox":[105.41344,21.02175,105.53798,21.17188]},
|
||||
"HN-BAVI": {"code":"HN-BAVI","name":"Ba Vi","parent":"HN","level":"district","children":[],"center":[21.14125,105.38297],"bbox":[105.28896,21.0074,105.47524,21.30451]},
|
||||
"HN-CHUONGMY": {"code":"HN-CHUONGMY","name":"Chuong My","parent":"HN","level":"district","children":[],"center":[20.87763,105.66036],"bbox":[105.55102,20.7919,105.75984,20.96484]},
|
||||
"HN-DANPHUONG": {"code":"HN-DANPHUONG","name":"Dan Phuong","parent":"HN","level":"district","children":[],"center":[21.11385,105.67739],"bbox":[105.62321,21.06614,105.74057,21.17336]},
|
||||
"HN-DONGANH": {"code":"HN-DONGANH","name":"Dong Anh","parent":"HN","level":"district","children":[],"center":[21.13619,105.84265],"bbox":[105.73214,21.06689,105.92674,21.20628]},
|
||||
"HN-GIALAM": {"code":"HN-GIALAM","name":"Gia Lam","parent":"HN","level":"district","children":[],"center":[21.02888,105.95055],"bbox":[105.88147,20.92633,106.02004,21.12119]},
|
||||
"HN-HOAIDUC": {"code":"HN-HOAIDUC","name":"Hoai Duc","parent":"HN","level":"district","children":[],"center":[21.02405,105.69946],"bbox":[105.64615,20.95542,105.74275,21.09028]},
|
||||
"HN-MELINH": {"code":"HN-MELINH","name":"Me Linh","parent":"HN","level":"district","children":[],"center":[21.18352,105.7058],"bbox":[105.61499,21.12176,105.7909,21.23963]},
|
||||
"HN-MYDUC": {"code":"HN-MYDUC","name":"My Duc","parent":"HN","level":"district","children":[],"center":[20.68678,105.72026],"bbox":[105.64768,20.56452,105.81286,20.82997]},
|
||||
"HN-PHUXUYEN": {"code":"HN-PHUXUYEN","name":"Phu Xuyen","parent":"HN","level":"district","children":[],"center":[20.73559,105.90112],"bbox":[105.7918,20.65079,106.00392,20.80684]},
|
||||
"HN-PHUCTHO": {"code":"HN-PHUCTHO","name":"Phuc Tho","parent":"HN","level":"district","children":[],"center":[21.11707,105.58374],"bbox":[105.50053,21.04026,105.66491,21.17912]},
|
||||
"HN-QUOCOAI": {"code":"HN-QUOCOAI","name":"Quoc Oai","parent":"HN","level":"district","children":[],"center":[20.96769,105.59765],"bbox":[105.4782,20.90939,105.72589,21.04616]},
|
||||
"HN-SOCSON": {"code":"HN-SOCSON","name":"Soc Son","parent":"HN","level":"district","children":[],"center":[21.27256,105.83045],"bbox":[105.72329,21.17692,105.93902,21.38542]},
|
||||
"HN-THACHTHAT": {"code":"HN-THACHTHAT","name":"Thach That","parent":"HN","level":"district","children":[],"center":[21.01894,105.53016],"bbox":[105.39192,20.94573,105.63988,21.10199]},
|
||||
"HN-THANHOAI": {"code":"HN-THANHOAI","name":"Thanh Oai","parent":"HN","level":"district","children":[],"center":[20.85577,105.78086],"bbox":[105.72512,20.78033,105.84123,20.93973]},
|
||||
"HN-THANHTRI": {"code":"HN-THANHTRI","name":"Thanh Tri","parent":"HN","level":"district","children":[],"center":[20.93266,105.84089],"bbox":[105.78821,20.89699,105.91311,20.98653]},
|
||||
"HN-THUONGTIN": {"code":"HN-THUONGTIN","name":"Thuong Tin","parent":"HN","level":"district","children":[],"center":[20.84497,105.87482],"bbox":[105.81267,20.75192,105.93617,20.91223]},
|
||||
"HN-UNGHOA": {"code":"HN-UNGHOA","name":"Ung Hoa","parent":"HN","level":"district","children":[],"center":[20.7119,105.79932],"bbox":[105.69662,20.61188,105.90562,20.80969]},
|
||||
"TPHCM": {"code":"TPHCM","name":"Ho Chi Minh","parent":"VN","level":"province","children":["TPHCM-Q1","TPHCM-Q3","TPHCM-Q4","TPHCM-Q5","TPHCM-Q6","TPHCM-Q7","TPHCM-Q8","TPHCM-Q10","TPHCM-Q11","TPHCM-Q12","TPHCM-BINHTAN","TPHCM-BINHTHANH","TPHCM-GOVAP","TPHCM-PHUNHUAN","TPHCM-TANBINH","TPHCM-TANPHU","TPHCM-THUDUC","TPHCM-BINHCHANH","TPHCM-CANGIO","TPHCM-CUCHI","TPHCM-HOCMON","TPHCM-NHABE"],"center":[10.8231,106.6297],"bbox":[106.46356,10.35828,107.02758,10.92934],"missingParts":1},
|
||||
"TPHCM-Q1": {"code":"TPHCM-Q1","name":"District 1","parent":"TPHCM","level":"district","children":[],"center":[10.77622,106.69715],"bbox":[106.68169,10.75352,106.7152,10.79693]},
|
||||
"TPHCM-Q3": {"code":"TPHCM-Q3","name":"District 3","parent":"TPHCM","level":"district","children":[],"center":[10.78146,106.68284],"bbox":[106.66429,10.76543,106.69813,10.79251]},
|
||||
"TPHCM-Q4": {"code":"TPHCM-Q4","name":"District 4","parent":"TPHCM","level":"district","children":[],"center":[10.75846,106.70554],"bbox":[106.68642,10.75171,106.72228,10.76976]},
|
||||
"TPHCM-Q5": {"code":"TPHCM-Q5","name":"District 5","parent":"TPHCM","level":"district","children":[],"center":[10.75494,106.66946],"bbox":[106.65051,10.74614,106.68727,10.76543]},
|
||||
"TPHCM-Q6": {"code":"TPHCM-Q6","name":"District 6","parent":"TPHCM","level":"district","children":[],"center":[10.74587,106.63524],"bbox":[106.62108,10.72973,106.65505,10.76202]},
|
||||
"TPHCM-Q7": {"code":"TPHCM-Q7","name":"District 7","parent":"TPHCM","level":"district","children":[],"center":[10.73646,106.72962],"bbox":[106.69009,10.69795,106.76866,10.77763]},
|
||||
"TPHCM-Q8": {"code":"TPHCM-Q8","name":"District 8","parent":"TPHCM","level":"district","children":[],"center":[10.7245,106.64055],"bbox":[106.59456,10.68985,106.69285,10.75211]},
|
||||
"TPHCM-Q10": {"code":"TPHCM-Q10","name":"District 10","parent":"TPHCM","level":"district","children":[],"center":[10.77237,106.66826],"bbox":[106.65613,10.75843,106.6828,10.78693]},
|
||||
"TPHCM-Q11": {"code":"TPHCM-Q11","name":"District 11","parent":"TPHCM","level":"district","children":[],"center":[10.76412,106.64773],"bbox":[106.6342,10.7534,106.66156,10.77845]},
|
||||
"TPHCM-Q12": {"code":"TPHCM-Q12","name":"District 12","parent":"TPHCM","level":"district","children":[],"center":[10.86453,106.65697],"bbox":[106.60328,10.81861,106.7182,10.9042]},
|
||||
"TPHCM-BINHTAN": {"code":"TPHCM-BINHTAN","name":"Binh Tan","parent":"TPHCM","level":"district","children":[],"center":[10.76473,106.59754],"bbox":[106.55831,10.71096,106.62526,10.82978]},
|
||||
"TPHCM-BINHTHANH": {"code":"TPHCM-BINHTHANH","name":"Binh Thanh","parent":"TPHCM","level":"district","children":[],"center":[10.81206,106.7162],"bbox":[106.68385,10.78534,106.75139,10.83917]},
|
||||
"TPHCM-GOVAP": {"code":"TPHCM-GOVAP","name":"Go Vap","parent":"TPHCM","level":"district","children":[],"center":[10.8383,106.66741],"bbox":[106.63194,10.80997,106.70106,10.86079]},
|
||||
"TPHCM-PHUNHUAN": {"code":"TPHCM-PHUNHUAN","name":"Phu Nhuan","parent":"TPHCM","level":"district","children":[],"center":[10.80033,106.67817],"bbox":[106.66596,10.78791,106.69248,10.81414]},
|
||||
"TPHCM-TANBINH": {"code":"TPHCM-TANBINH","name":"Tan Binh","parent":"TPHCM","level":"district","children":[],"center":[10.80882,106.65239],"bbox":[106.62789,10.76891,106.67861,10.83725]},
|
||||
"TPHCM-TANPHU": {"code":"TPHCM-TANPHU","name":"Tan Phu","parent":"TPHCM","level":"district","children":[],"center":[10.79255,106.62689],"bbox":[106.6074,10.75817,106.64768,10.82469]},
|
||||
"TPHCM-THUDUC": {"code":"TPHCM-THUDUC","name":"Thu Duc","parent":"TPHCM","level":"district","children":[],"center":[10.82035,106.78951],"bbox":[106.69783,10.74212,106.88176,10.89885]},
|
||||
"TPHCM-BINHCHANH": {"code":"TPHCM-BINHCHANH","name":"Binh Chanh","parent":"TPHCM","level":"district","children":[],"center":[10.73382,106.57587],"bbox":[106.46356,10.62531,106.69495,10.87364]},
|
||||
"TPHCM-CANGIO": {"code":"TPHCM-CANGIO","name":"Can Gio","parent":"TPHCM","level":"district","children":[],"center":[10.51704,106.881],"bbox":[106.73846,10.35818,107.02767,10.67427]},
|
||||
"TPHCM-CUCHI": {"code":"TPHCM-CUCHI","name":"Cu Chi","parent":"TPHCM","level":"district","children":[],"coverage":"unresolved"},
|
||||
"TPHCM-HOCMON": {"code":"TPHCM-HOCMON","name":"Hoc Mon","parent":"TPHCM","level":"district","children":[],"center":[10.88585,106.58983],"bbox":[106.49562,10.8261,106.69279,10.92934]},
|
||||
"TPHCM-NHABE": {"code":"TPHCM-NHABE","name":"Nha Be","parent":"TPHCM","level":"district","children":[],"center":[10.6543,106.73051],"bbox":[106.67328,10.57636,106.78373,10.72563]},
|
||||
"DN": {"code":"DN","name":"Da Nang","parent":"VN","level":"province","children":["DN-HAICHAU","DN-THANHKHE","DN-SONTRA","DN-NGUHANHSON","DN-LIENCHIEU","DN-CAMLE","DN-HOAVANG"],"center":[16.0544,108.2022],"bbox":[107.81854,15.91799,108.33864,16.2255]},
|
||||
"DN-HAICHAU": {"code":"DN-HAICHAU","name":"Hai Chau","parent":"DN","level":"district","children":[],"center":[16.05669,108.21143],"bbox":[108.18883,16.02131,108.236,16.10365]},
|
||||
"DN-THANHKHE": {"code":"DN-THANHKHE","name":"Thanh Khe","parent":"DN","level":"district","children":[],"center":[16.06315,108.19039],"bbox":[108.16378,16.02637,108.21456,16.08084]},
|
||||
"DN-SONTRA": {"code":"DN-SONTRA","name":"Son Tra","parent":"DN","level":"district","children":[],"center":[16.11094,108.26514],"bbox":[108.20903,16.05099,108.33864,16.15565]},
|
||||
"DN-NGUHANHSON": {"code":"DN-NGUHANHSON","name":"Ngu Hanh Son","parent":"DN","level":"district","children":[],"center":[15.99755,108.25003],"bbox":[108.21683,15.95597,108.28957,16.05675]},
|
||||
"DN-LIENCHIEU": {"code":"DN-LIENCHIEU","name":"Lien Chieu","parent":"DN","level":"district","children":[],"center":[16.13388,108.14835],"bbox":[108.08699,16.0331,108.21061,16.22576]},
|
||||
"DN-CAMLE": {"code":"DN-CAMLE","name":"Cam Le","parent":"DN","level":"district","children":[],"center":[16.01409,108.19606],"bbox":[108.15769,15.97164,108.23951,16.06188]},
|
||||
"DN-HOAVANG": {"code":"DN-HOAVANG","name":"Hoa Vang","parent":"DN","level":"district","children":[],"center":[16.06583,108.01436],"bbox":[107.81854,15.91799,108.22494,16.21544]},
|
||||
"LD": {"code":"LD","name":"Lam Dong","parent":"VN","level":"province","children":["DL"],"center":[11.92055,108.4644],"bbox":[108.31521,11.80798,108.59461,12.00855],"partialCoverage":"one town covered"},
|
||||
"DL": {"code":"DL","name":"Da Lat","parent":"LD","level":"district","children":[],"center":[11.9404,108.4583],"bbox":[108.31521,11.80798,108.59461,12.00855]},
|
||||
"LA": {"code":"LA","name":"Long An","parent":"VN","level":"province","children":["DH"],"center":[10.88908,106.40895],"bbox":[106.27082,10.7409,106.53287,11.02578],"partialCoverage":"one town covered"},
|
||||
"DH": {"code":"DH","name":"Duc Hoa","parent":"LA","level":"district","children":[],"center":[10.8888,106.3825],"bbox":[106.27082,10.7409,106.53287,11.02578]},
|
||||
};
|
||||
+31
-58
@@ -1,77 +1,50 @@
|
||||
import * as turf from '@turf/turf';
|
||||
import { REGIONS } from '../data/regions/index.js';
|
||||
import { provinces } from './regions.js';
|
||||
|
||||
// Cities enum-like structure.
|
||||
// Region lookups, derived from the generated tree in src/data/regions/.
|
||||
//
|
||||
// bbox comes from src/data/boundaries/<code>.geojson, which holds each city's
|
||||
// pre-2025-merger extent. Vietnam merged its provinces in mid-2025, so today's
|
||||
// official Ho Chi Minh City covers 36,566 km2 and reaches Vung Tau; the legacy
|
||||
// outline is the area people actually mean by the city.
|
||||
// These used to be a hand-written map of five cities that duplicated the bbox
|
||||
// and centre already recorded in each boundary file, which meant two places to
|
||||
// update and a test to police the drift. The tree is generated by
|
||||
// scripts/build-region-boundaries.mjs from those same boundaries, so there is
|
||||
// one source now.
|
||||
//
|
||||
// center stays the recognisable city centre rather than the polygon's centroid,
|
||||
// bbox is a pre-2025-merger extent. Vietnam merged its provinces in mid-2025, so
|
||||
// today's official Ho Chi Minh City covers 36,566 km2 and reaches Vung Tau; the
|
||||
// legacy outline is the area people actually mean by the city.
|
||||
//
|
||||
// centre stays the recognisable city centre rather than the polygon's centroid,
|
||||
// which for an irregular outline can land somewhere no one associates with the
|
||||
// place. The map fits the bbox anyway.
|
||||
// place. The build honours a per-node override for exactly that reason. The map
|
||||
// fits the bbox anyway.
|
||||
//
|
||||
// There is no Mapillary query window any more: locations come from the prebuilt
|
||||
// index in src/data/panos/, not from searching an area.
|
||||
export const CITIES = {
|
||||
HN: {
|
||||
code: 'HN',
|
||||
name: 'Ha Noi',
|
||||
center: [21.0285, 105.8542],
|
||||
bbox: [105.28896, 20.56452, 106.02004, 21.38542],
|
||||
enabled: true
|
||||
},
|
||||
DN: {
|
||||
code: 'DN',
|
||||
name: 'Da Nang',
|
||||
center: [16.0544, 108.2022],
|
||||
bbox: [107.81854, 15.91799, 108.33864, 16.2255],
|
||||
enabled: true
|
||||
},
|
||||
TPHCM: {
|
||||
code: 'TPHCM',
|
||||
name: 'Ho Chi Minh',
|
||||
center: [10.8231, 106.6297],
|
||||
bbox: [106.46356, 10.35828, 107.02758, 10.92934],
|
||||
enabled: true
|
||||
},
|
||||
DL: {
|
||||
code: 'DL',
|
||||
name: 'Da Lat',
|
||||
center: [11.9404, 108.4583],
|
||||
bbox: [108.31521, 11.80798, 108.5944, 12.00855],
|
||||
enabled: true
|
||||
},
|
||||
DH: {
|
||||
code: 'DH',
|
||||
name: 'Duc Hoa (Long An)',
|
||||
center: [10.8888, 106.3825],
|
||||
bbox: [106.27082, 10.7409, 106.53287, 11.02578],
|
||||
enabled: true
|
||||
}
|
||||
};
|
||||
// Keyed by EVERY node, not just the playable provinces, so a bookmarked
|
||||
// /game?location=DL still resolves after Da Lat became a district of Lam Dong.
|
||||
export const CITIES = REGIONS;
|
||||
|
||||
// Helper functions for backward compatibility
|
||||
export const cityCenters = Object.fromEntries(
|
||||
Object.values(CITIES).map(city => [city.code, city.center])
|
||||
Object.values(REGIONS)
|
||||
.filter((region) => region.center)
|
||||
.map((region) => [region.code, region.center])
|
||||
);
|
||||
|
||||
export const cityNames = Object.fromEntries(
|
||||
Object.values(CITIES).map(city => [city.code, city.name])
|
||||
Object.values(REGIONS).map((region) => [region.code, region.name])
|
||||
);
|
||||
|
||||
|
||||
export const cityBboxes = Object.fromEntries(
|
||||
Object.values(CITIES).map(city => [city.code, city.bbox])
|
||||
Object.values(REGIONS)
|
||||
.filter((region) => region.bbox)
|
||||
.map((region) => [region.code, region.bbox])
|
||||
);
|
||||
|
||||
// Cities list for UI components (only enabled cities)
|
||||
export const cities = Object.values(CITIES)
|
||||
.filter(city => city.enabled)
|
||||
.map(city => ({
|
||||
code: city.code,
|
||||
name: city.name.toUpperCase()
|
||||
}));
|
||||
// Province list for UI components. Districts are reached by expanding a
|
||||
// province, not from this flat list.
|
||||
export const cities = provinces().map((code) => ({
|
||||
code,
|
||||
name: REGIONS[code].name.toUpperCase()
|
||||
}));
|
||||
|
||||
// Calculate distance between two coordinates in meters using Turf
|
||||
export function calculateDistance(lat1, lon1, lat2, lon2) {
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
// Navigation over the region tree: country > province > district.
|
||||
//
|
||||
// Client-safe by construction. This module imports the generated tree and the
|
||||
// generated panorama counts and NOTHING else -- in particular never
|
||||
// pano-index.js or src/data/panos/, which together are ~25MB of exact
|
||||
// panorama coordinates. A client component that reached those would ship every
|
||||
// answer to the browser. tests/regions.test.js enforces the boundary.
|
||||
//
|
||||
// Scores roll upward: a guess resolves to the leaf its panorama sits in, and
|
||||
// credits that leaf, its province, and the country. ancestorsOf() is what the
|
||||
// leaderboard fans out over.
|
||||
|
||||
import { REGIONS } from '../data/regions/index.js';
|
||||
|
||||
export const COUNTRY_CODE = 'VN';
|
||||
|
||||
/**
|
||||
* One region node.
|
||||
* @param {string} code Region code, e.g. 'TPHCM-Q7'.
|
||||
* @returns {Object} The node.
|
||||
*/
|
||||
export function getRegion(code) {
|
||||
const region = REGIONS[code];
|
||||
if (!region) {
|
||||
throw new Error(
|
||||
`Unknown region: ${code}. Known codes: ${Object.keys(REGIONS).join(', ')}`
|
||||
);
|
||||
}
|
||||
return region;
|
||||
}
|
||||
|
||||
/** True when the code names a region, without throwing. */
|
||||
export function isRegion(code) {
|
||||
return Boolean(code && REGIONS[code]);
|
||||
}
|
||||
|
||||
/**
|
||||
* The node and every ancestor above it, ending at the country.
|
||||
*
|
||||
* This is the fan-out chain: a guess in TPHCM-Q7 credits
|
||||
* ['TPHCM-Q7', 'TPHCM', 'VN'].
|
||||
* @param {string} code Region code.
|
||||
* @returns {string[]} Codes, self first.
|
||||
*/
|
||||
export function ancestorsOf(code) {
|
||||
const chain = [];
|
||||
let current = getRegion(code);
|
||||
while (current) {
|
||||
chain.push(current.code);
|
||||
current = current.parent ? getRegion(current.parent) : null;
|
||||
}
|
||||
return chain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Direct children of a node.
|
||||
* @param {string} code Region code.
|
||||
* @returns {string[]} Child codes, empty for a leaf.
|
||||
*/
|
||||
export function childrenOf(code) {
|
||||
return getRegion(code).children ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Every leaf at or below a node. A leaf is its own only leaf.
|
||||
* @param {string} code Region code.
|
||||
* @returns {string[]} Leaf codes.
|
||||
*/
|
||||
export function leavesUnder(code) {
|
||||
const children = childrenOf(code);
|
||||
if (children.length === 0) return [code];
|
||||
return children.flatMap((child) => leavesUnder(child));
|
||||
}
|
||||
|
||||
/**
|
||||
* The province a node belongs to.
|
||||
* @param {string} code Region code.
|
||||
* @returns {string|null} Province code, or null for the country itself.
|
||||
*/
|
||||
export function provinceOf(code) {
|
||||
return ancestorsOf(code).find((c) => getRegion(c).level === 'province') ?? null;
|
||||
}
|
||||
|
||||
/** Every province, in declaration order. */
|
||||
export function provinces() {
|
||||
return childrenOf(COUNTRY_CODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ancestor names, country first, for a UI header or a result reveal.
|
||||
*
|
||||
* Only safe to show the resolved leaf's path AFTER a guess: before it, the
|
||||
* district names the answer.
|
||||
* @param {string} code Region code.
|
||||
* @returns {string[]} Names, outermost first.
|
||||
*/
|
||||
export function regionPath(code) {
|
||||
return ancestorsOf(code)
|
||||
.reverse()
|
||||
.map((c) => getRegion(c).name);
|
||||
}
|
||||
|
||||
/** Every node in the tree, in declaration order. */
|
||||
export function allRegions() {
|
||||
return Object.keys(REGIONS);
|
||||
}
|
||||
|
||||
/**
|
||||
* True when a node has no boundary because its OSM lookup did not resolve.
|
||||
*
|
||||
* Distinct from having no street imagery: an unresolved node never entered its
|
||||
* province's union, so its panoramas were clipped away at index-build time.
|
||||
* See the Coverage note in docs/project-overview.md.
|
||||
* @param {string} code Region code.
|
||||
* @returns {boolean} True when unresolved.
|
||||
*/
|
||||
export function isUnresolved(code) {
|
||||
return getRegion(code).coverage === 'unresolved';
|
||||
}
|
||||
+30
-22
@@ -1,11 +1,14 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import {
|
||||
CITIES,
|
||||
cities,
|
||||
cityNames,
|
||||
cityCenters,
|
||||
cityBboxes,
|
||||
calculateDistance,
|
||||
calculateScore,
|
||||
formatDistance,
|
||||
} from '../src/lib/game.js';
|
||||
import { allRegions, getRegion, provinces } from '../src/lib/regions.js';
|
||||
|
||||
describe('calculateScore', () => {
|
||||
// The bands are the whole scoring rule, so each boundary is pinned on both
|
||||
@@ -71,35 +74,40 @@ describe('formatDistance', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('city configuration', () => {
|
||||
it('exposes only enabled cities to the UI', () => {
|
||||
const exposed = cities.map((city) => city.code).sort();
|
||||
const expected = Object.values(CITIES)
|
||||
.filter((city) => city.enabled)
|
||||
.map((city) => city.code)
|
||||
.sort();
|
||||
expect(exposed).toEqual(expected);
|
||||
describe('region lookups', () => {
|
||||
// Shape and geography of the tree itself are covered by regions.test.js.
|
||||
// These assert only that game.js's derived lookups stay faithful to it.
|
||||
|
||||
it('offers the provinces, and only the provinces, as entry points', () => {
|
||||
expect(cities.map((city) => city.code)).toEqual(provinces());
|
||||
});
|
||||
|
||||
it('uppercases city names for the UI', () => {
|
||||
it('uppercases entry-point names for the UI', () => {
|
||||
for (const city of cities) {
|
||||
expect(city.name).toBe(city.name.toUpperCase());
|
||||
}
|
||||
});
|
||||
|
||||
it('gives every city a well-formed bbox', () => {
|
||||
for (const city of Object.values(CITIES)) {
|
||||
const [minLng, minLat, maxLng, maxLat] = city.bbox;
|
||||
expect(maxLng).toBeGreaterThan(minLng);
|
||||
expect(maxLat).toBeGreaterThan(minLat);
|
||||
it('names every node, not just the entry points', () => {
|
||||
// A bookmarked /game?location=DL has to keep resolving now that Da Lat is a
|
||||
// district of Lam Dong rather than a top-level city.
|
||||
for (const code of allRegions()) {
|
||||
expect(cityNames[code], code).toBe(getRegion(code).name);
|
||||
}
|
||||
expect(cityNames.DL).toBe('Da Lat');
|
||||
});
|
||||
|
||||
// The centre must sit inside the box it labels, or the map opens on one
|
||||
// city while the panorama comes from another.
|
||||
const [centreLat, centreLng] = city.center;
|
||||
expect(centreLat).toBeGreaterThanOrEqual(minLat);
|
||||
expect(centreLat).toBeLessThanOrEqual(maxLat);
|
||||
expect(centreLng).toBeGreaterThanOrEqual(minLng);
|
||||
expect(centreLng).toBeLessThanOrEqual(maxLng);
|
||||
it('mirrors the tree for centres and boxes it has', () => {
|
||||
for (const code of allRegions()) {
|
||||
const region = getRegion(code);
|
||||
if (region.center) expect(cityCenters[code], code).toEqual(region.center);
|
||||
if (region.bbox) expect(cityBboxes[code], code).toEqual(region.bbox);
|
||||
}
|
||||
});
|
||||
|
||||
it('omits nodes with no boundary rather than inventing one', () => {
|
||||
// Cu Chi has no OSM relation left, so it has no extent to look up.
|
||||
expect(cityBboxes['TPHCM-CUCHI']).toBeUndefined();
|
||||
expect(cityNames['TPHCM-CUCHI']).toBe('Cu Chi');
|
||||
});
|
||||
});
|
||||
+17
-15
@@ -1,10 +1,12 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import * as turf from '@turf/turf';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { getCityIndex, pickRandomPano, countPanos } from '../src/lib/pano-index.js';
|
||||
import { CITIES } from '../src/lib/game.js';
|
||||
import { getCityIndex, pickRandomPano, countPanos, indexedCities } from '../src/lib/pano-index.js';
|
||||
import { getRegion } from '../src/lib/regions.js';
|
||||
|
||||
const CODES = Object.keys(CITIES);
|
||||
// Indexes are built per province, so the province list -- not every node in the
|
||||
// tree -- is what has one. Districts are served by filtering their province.
|
||||
const CODES = indexedCities();
|
||||
|
||||
describe('panorama index', () => {
|
||||
it.each(CODES)('%s has an index with panoramas', (code) => {
|
||||
@@ -34,7 +36,7 @@ describe('panorama index', () => {
|
||||
it.each(CODES)('%s panoramas sit inside the city bbox', (code) => {
|
||||
// The index is built from tiles covering the bbox, then clipped to the
|
||||
// boundary, so anything outside means the clip or the bbox is wrong.
|
||||
const [minLng, minLat, maxLng, maxLat] = CITIES[code].bbox;
|
||||
const [minLng, minLat, maxLng, maxLat] = getRegion(code).bbox;
|
||||
const stray = getCityIndex(code).panos.find(
|
||||
(p) => p.lat < minLat || p.lat > maxLat || p.lng < minLng || p.lng > maxLng
|
||||
);
|
||||
@@ -43,7 +45,7 @@ describe('panorama index', () => {
|
||||
|
||||
it.each(CODES)('%s panoramas sit inside the city boundary', (code) => {
|
||||
const boundary = JSON.parse(
|
||||
readFileSync(`src/data/boundaries/${code.toLowerCase()}.json`, 'utf8')
|
||||
readFileSync(`src/data/boundaries/${code.toLowerCase()}/${code.toLowerCase()}.json`, 'utf8')
|
||||
);
|
||||
// Sample rather than test every point: booleanPointInPolygon against a
|
||||
// detailed outline is slow, and a clipping bug would not hide in a sample.
|
||||
@@ -58,32 +60,32 @@ describe('panorama index', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it.each(CODES)('%s index bbox agrees with the city config', (code) => {
|
||||
expect(getCityIndex(code).bbox).toEqual(CITIES[code].bbox);
|
||||
it.each(CODES)('%s index bbox agrees with the region tree', (code) => {
|
||||
expect(getCityIndex(code).bbox).toEqual(getRegion(code).bbox);
|
||||
});
|
||||
});
|
||||
|
||||
describe('pickRandomPano', () => {
|
||||
it('returns an entry from the city', () => {
|
||||
const chosen = pickRandomPano('DL');
|
||||
expect(getCityIndex('DL').panos.some((p) => p.id === chosen.id)).toBe(true);
|
||||
it('returns an entry from the province', () => {
|
||||
const chosen = pickRandomPano('LD');
|
||||
expect(getCityIndex('LD').panos.some((p) => p.id === chosen.id)).toBe(true);
|
||||
});
|
||||
|
||||
it('never returns an excluded id', () => {
|
||||
const { panos } = getCityIndex('DL');
|
||||
const { panos } = getCityIndex('LD');
|
||||
const exclude = new Set(panos.slice(0, panos.length - 1).map((p) => p.id));
|
||||
// Only one candidate is left, so the choice is forced and checkable.
|
||||
expect(pickRandomPano('DL', exclude).id).toBe(panos[panos.length - 1].id);
|
||||
expect(pickRandomPano('LD', exclude).id).toBe(panos[panos.length - 1].id);
|
||||
});
|
||||
|
||||
it('throws when everything is excluded', () => {
|
||||
const all = new Set(getCityIndex('DL').panos.map((p) => p.id));
|
||||
expect(() => pickRandomPano('DL', all)).toThrow(/No panoramas left/);
|
||||
const all = new Set(getCityIndex('LD').panos.map((p) => p.id));
|
||||
expect(() => pickRandomPano('LD', all)).toThrow(/No panoramas left/);
|
||||
});
|
||||
|
||||
it('spreads across the index rather than returning one entry', () => {
|
||||
const seen = new Set();
|
||||
for (let i = 0; i < 100; i++) seen.add(pickRandomPano('DL').id);
|
||||
for (let i = 0; i < 100; i++) seen.add(pickRandomPano('LD').id);
|
||||
expect(seen.size).toBeGreaterThan(10);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,223 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { readFileSync, existsSync } from 'node:fs';
|
||||
import { dirname, resolve } from 'node:path';
|
||||
import {
|
||||
COUNTRY_CODE,
|
||||
getRegion,
|
||||
isRegion,
|
||||
ancestorsOf,
|
||||
childrenOf,
|
||||
leavesUnder,
|
||||
provinceOf,
|
||||
provinces,
|
||||
regionPath,
|
||||
allRegions,
|
||||
isUnresolved,
|
||||
} from '../src/lib/regions.js';
|
||||
|
||||
const LEAVES = allRegions().filter((code) => childrenOf(code).length === 0);
|
||||
const RESOLVED_LEAVES = LEAVES.filter((code) => !isUnresolved(code));
|
||||
|
||||
describe('region tree shape', () => {
|
||||
it('has exactly one root, and it is the country', () => {
|
||||
const roots = allRegions().filter((code) => getRegion(code).parent === null);
|
||||
expect(roots).toEqual([COUNTRY_CODE]);
|
||||
expect(getRegion(COUNTRY_CODE).level).toBe('country');
|
||||
});
|
||||
|
||||
it('every parent reference resolves', () => {
|
||||
for (const code of allRegions()) {
|
||||
const { parent } = getRegion(code);
|
||||
if (parent !== null) expect(isRegion(parent), `${code} -> ${parent}`).toBe(true);
|
||||
}
|
||||
});
|
||||
|
||||
it('every child reference resolves and points back', () => {
|
||||
for (const code of allRegions()) {
|
||||
for (const child of childrenOf(code)) {
|
||||
expect(isRegion(child), `${code} -> ${child}`).toBe(true);
|
||||
expect(getRegion(child).parent).toBe(code);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('has no cycles', () => {
|
||||
// ancestorsOf would loop forever on a cycle, so walk with a guard instead.
|
||||
for (const code of allRegions()) {
|
||||
const seen = new Set();
|
||||
let current = code;
|
||||
while (current) {
|
||||
expect(seen.has(current), `cycle through ${current}`).toBe(false);
|
||||
seen.add(current);
|
||||
current = getRegion(current).parent;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('holds one country, five provinces and 61 leaves', () => {
|
||||
const byLevel = (level) => allRegions().filter((c) => getRegion(c).level === level);
|
||||
expect(byLevel('country')).toHaveLength(1);
|
||||
expect(byLevel('province')).toHaveLength(5);
|
||||
expect(LEAVES).toHaveLength(61);
|
||||
});
|
||||
|
||||
it('gives every province the country as parent', () => {
|
||||
for (const code of provinces()) expect(getRegion(code).parent).toBe(COUNTRY_CODE);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ancestry', () => {
|
||||
it('walks every leaf up to the country in three steps', () => {
|
||||
for (const code of LEAVES) {
|
||||
const chain = ancestorsOf(code);
|
||||
expect(chain, code).toHaveLength(3);
|
||||
expect(chain[0]).toBe(code);
|
||||
expect(chain[2]).toBe(COUNTRY_CODE);
|
||||
}
|
||||
});
|
||||
|
||||
it('places Da Lat under Lam Dong and Duc Hoa under Long An', () => {
|
||||
// Pre-2025 administrative units throughout: Duc Hoa belongs to Long An, not
|
||||
// the Tay Ninh it was merged into in 2025.
|
||||
expect(ancestorsOf('DL')).toEqual(['DL', 'LD', COUNTRY_CODE]);
|
||||
expect(ancestorsOf('DH')).toEqual(['DH', 'LA', COUNTRY_CODE]);
|
||||
});
|
||||
|
||||
it('resolves the province of a node at every level', () => {
|
||||
expect(provinceOf('TPHCM-Q7')).toBe('TPHCM');
|
||||
expect(provinceOf('TPHCM')).toBe('TPHCM');
|
||||
expect(provinceOf(COUNTRY_CODE)).toBeNull();
|
||||
});
|
||||
|
||||
it('collects every leaf under a node', () => {
|
||||
expect(leavesUnder(COUNTRY_CODE).sort()).toEqual([...LEAVES].sort());
|
||||
expect(leavesUnder('LD')).toEqual(['DL']);
|
||||
expect(leavesUnder('DL')).toEqual(['DL']);
|
||||
});
|
||||
|
||||
it('renders an outermost-first path', () => {
|
||||
expect(regionPath('DL')).toEqual(['Vietnam', 'Lam Dong', 'Da Lat']);
|
||||
});
|
||||
|
||||
it('rejects an unknown code with the known ones listed', () => {
|
||||
expect(() => getRegion('NOPE')).toThrow(/Unknown region: NOPE/);
|
||||
expect(isRegion('NOPE')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('geography', () => {
|
||||
it('gives every resolved node a well-formed bbox', () => {
|
||||
for (const code of allRegions()) {
|
||||
const region = getRegion(code);
|
||||
if (!region.bbox) continue;
|
||||
const [minLng, minLat, maxLng, maxLat] = region.bbox;
|
||||
expect(maxLng, code).toBeGreaterThan(minLng);
|
||||
expect(maxLat, code).toBeGreaterThan(minLat);
|
||||
}
|
||||
});
|
||||
|
||||
it('keeps every centre inside the box it labels', () => {
|
||||
// A centre outside its own bbox opens the map on one place while the
|
||||
// panorama comes from another.
|
||||
for (const code of allRegions()) {
|
||||
const region = getRegion(code);
|
||||
if (!region.bbox || !region.center) continue;
|
||||
const [minLng, minLat, maxLng, maxLat] = region.bbox;
|
||||
const [lat, lng] = region.center;
|
||||
expect(lat, `${code} centre latitude`).toBeGreaterThanOrEqual(minLat);
|
||||
expect(lat, `${code} centre latitude`).toBeLessThanOrEqual(maxLat);
|
||||
expect(lng, `${code} centre longitude`).toBeGreaterThanOrEqual(minLng);
|
||||
expect(lng, `${code} centre longitude`).toBeLessThanOrEqual(maxLng);
|
||||
}
|
||||
});
|
||||
|
||||
it('nests every resolved leaf inside its province', () => {
|
||||
for (const code of RESOLVED_LEAVES) {
|
||||
const leaf = getRegion(code);
|
||||
const province = getRegion(provinceOf(code));
|
||||
if (!leaf.bbox || !province.bbox) continue;
|
||||
// Simplification moves both outlines by a few metres, so allow a small
|
||||
// slack rather than demanding exact containment.
|
||||
const slack = 0.01;
|
||||
expect(leaf.bbox[0], `${code} west`).toBeGreaterThanOrEqual(province.bbox[0] - slack);
|
||||
expect(leaf.bbox[1], `${code} south`).toBeGreaterThanOrEqual(province.bbox[1] - slack);
|
||||
expect(leaf.bbox[2], `${code} east`).toBeLessThanOrEqual(province.bbox[2] + slack);
|
||||
expect(leaf.bbox[3], `${code} north`).toBeLessThanOrEqual(province.bbox[3] + slack);
|
||||
}
|
||||
});
|
||||
|
||||
it('keeps the hand-picked centres of the original five entry points', () => {
|
||||
// src/lib/game.js explains why these are not polygon centroids: the centre
|
||||
// of mass of an irregular outline lands somewhere no one recognises. The
|
||||
// build honours a per-node override so a regenerated tree cannot lose them.
|
||||
expect(getRegion('HN').center).toEqual([21.0285, 105.8542]);
|
||||
expect(getRegion('DN').center).toEqual([16.0544, 108.2022]);
|
||||
expect(getRegion('TPHCM').center).toEqual([10.8231, 106.6297]);
|
||||
expect(getRegion('DL').center).toEqual([11.9404, 108.4583]);
|
||||
expect(getRegion('DH').center).toEqual([10.8888, 106.3825]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('coverage', () => {
|
||||
it('marks Lam Dong and Long An as partially covered', () => {
|
||||
expect(getRegion('LD').partialCoverage).toBeTruthy();
|
||||
expect(getRegion('LA').partialCoverage).toBeTruthy();
|
||||
});
|
||||
|
||||
it('records an unresolved leaf instead of dropping it', () => {
|
||||
// Cu Chi has no boundary relation left in OpenStreetMap, so it cannot be
|
||||
// built. It still belongs in the tree: absent coverage is data, not a gap
|
||||
// in the model. See the Coverage note in docs/project-overview.md.
|
||||
expect(isRegion('TPHCM-CUCHI')).toBe(true);
|
||||
expect(getRegion('TPHCM-CUCHI').bbox).toBeUndefined();
|
||||
expect(isUnresolved('TPHCM-CUCHI')).toBe(true);
|
||||
});
|
||||
|
||||
it('counts unresolved leaves against their province', () => {
|
||||
for (const code of provinces()) {
|
||||
const unresolved = childrenOf(code).filter(isUnresolved).length;
|
||||
expect(getRegion(code).missingParts ?? 0, code).toBe(unresolved);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('client safety', () => {
|
||||
it('never reaches the panorama data', () => {
|
||||
// src/lib/regions.js is imported by client components. The panorama
|
||||
// indexes are ~25MB of exact answers; a path from one to the other would
|
||||
// ship every round's solution to the browser.
|
||||
const seen = new Set();
|
||||
const walk = (file) => {
|
||||
if (seen.has(file) || !existsSync(file)) return;
|
||||
seen.add(file);
|
||||
const source = readFileSync(file, 'utf8');
|
||||
for (const match of source.matchAll(/from\s+'(\.[^']+)'/g)) {
|
||||
const target = resolve(dirname(file), match[1]);
|
||||
expect(target.replace(/\\/g, '/')).not.toContain('data/panos');
|
||||
if (target.endsWith('.js')) walk(target);
|
||||
}
|
||||
};
|
||||
walk(resolve('src/lib/regions.js'));
|
||||
expect(seen.size).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('generated barrels', () => {
|
||||
// Region codes contain hyphens, which are legal in a filename but not in a
|
||||
// bare import identifier or an unquoted object key. A barrel generated
|
||||
// without sanitising them is a syntax error that breaks the whole build, so
|
||||
// importing them at all is the assertion.
|
||||
|
||||
it('the region tree parses and covers every node', async () => {
|
||||
const { REGIONS } = await import('../src/data/regions/index.js');
|
||||
expect(Object.keys(REGIONS)).toHaveLength(allRegions().length);
|
||||
});
|
||||
|
||||
it('the boundary barrel parses and holds every resolved region', async () => {
|
||||
const { REGION_BOUNDARIES } = await import('../src/data/boundaries/index.js');
|
||||
const withGeometry = allRegions().filter((code) => getRegion(code).bbox);
|
||||
expect(Object.keys(REGION_BOUNDARIES).sort()).toEqual(
|
||||
withGeometry.filter((code) => code !== COUNTRY_CODE).sort()
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user