Global index & schemas¶
data/index.json is the whole catalog in one file: every territory and every
dataset, with each country's manifest embedded verbatim. One
request instead of one per territory, and the entry point the
client libraries read.
What it is¶
wgj index walks the manifests and writes a single JSON document
that contains them, plus a few derived fields a client needs before it has
downloaded anything: the published levels, the licences that actually govern
the files, which level is the municipal tier and what the tiers are called
locally. Because the manifests are embedded rather than summarised, the index
can never disagree with a manifest — and CI regenerates it on every change
(wgj index --check) to make sure the committed copy is current.
Shape¶
{
"schema_version": 1,
"bodies": ["earth"],
"totals": { "countries": 106, "datasets": 192, "features": 75460, "bytes": 232336401 },
"countries": [ … ]
}
| Key | Meaning |
|---|---|
schema_version |
Format version of the index. 1 today |
bodies |
The bodies with data: ["earth"] until the Moon and Mars arrive |
totals |
countries, datasets, features and bytes of full-resolution data across the corpus |
countries |
One entry per territory, sorted by body then iso_a3 |
Per country (countries[])¶
Chile's entry, with the datasets abridged:
{
"body": "earth",
"iso_a3": "CHL",
"iso_a2": "CL",
"m49_region": "South America",
"name": { "en": "Chile", "es": "Chile" },
"status": "ok",
"manifest": "data/earth/CHL/manifest.json",
"license": "mixed",
"licenses": ["CC-BY-4.0", "public-domain"],
"levels": ["ADM0", "ADM1", "ADM2", "ADM3"],
"municipal_level": "ADM3",
"terms": {
"adm1": { "en": "Region", "es": "Región" },
"adm2": { "en": "Province", "es": "Provincia" },
"municipal": { "en": "Commune", "es": "Comuna" }
},
"crs": { "authority": "OGC", "code": "CRS84", "epsg": 4326 },
"source": {
"name": "IDE Chile / SUBDERE — División Política Administrativa 2023",
"url": "https://www.geoportal.cl/",
"license": "mixed",
"retrieved": "2026-08-11",
"licenses": ["CC-BY-4.0", "public-domain"]
},
"datasets": [ … ]
}
| Field | Meaning |
|---|---|
body, iso_a3, iso_a2, m49_region, name, status |
Identity, copied from the manifest |
manifest |
Path of the manifest, relative to the repository root |
license, licenses |
Roll-up of datasets[].license: the single value, or "mixed", and the sorted distinct values |
levels |
The published levels, in order |
municipal_level |
Which level is the municipal tier, or null when none is published |
terms |
Local names of the tiers in both languages — adm1, adm2 where one exists, municipal — from pipeline/src/wgj/tables/countries.json. Present when the registry defines them |
crs, source, notes |
As in the manifest; notes only where the manifest has one |
datasets |
The manifest's datasets array, verbatim — paths, bytes, sha256, features, bbox, properties, previews and split parts, exactly as Manifest format describes |
Size and determinism¶
About 650 KB on disk, 80 KB gzipped — smaller than most single data files. It has
no timestamp and no version field, deliberately: the file is byte-deterministic
for a given state of the manifests, so CI can regenerate it and git diff. The
data version is the git ref you fetched it from.
Fetching it¶
v1.0.0 is tagged from the merge of the data contract; until then only
main resolves. Every tagged release also attaches index.json as a
Release asset, next to the per-country zips and a SHA256SUMS:
https://github.com/andresgmg/World-GeoJSON/releases/tag/v1.0.0. See
Download & CDN.
The path of every dataset is repository-relative, so base + path is the
download URL for whichever base you fetched the index from. Listing every
territory that publishes first-level divisions:
const base = "https://raw.githubusercontent.com/andresgmg/World-GeoJSON/main/";
const index = await fetch(`${base}data/index.json`).then((r) => r.json());
for (const c of index.countries) {
const adm1 = c.datasets.find((d) => d.level === "ADM1");
if (adm1) {
console.log(c.iso_a3, c.name.en, adm1.features, `${base}${adm1.path}`);
}
}
import json
import urllib.request
BASE = "https://raw.githubusercontent.com/andresgmg/World-GeoJSON/main/"
with urllib.request.urlopen(BASE + "data/index.json") as fh:
index = json.load(fh)
for c in index["countries"]:
adm1 = next((d for d in c["datasets"] if d["level"] == "ADM1"), None)
if adm1:
print(c["iso_a3"], c["name"]["en"], adm1["features"], BASE + adm1["path"])
Before trusting a downloaded file, compare its SHA-256 with the sha256 the
index carries for it — see
Download & CDN → Checksums.
Schemas¶
Everything the pipeline writes is described by a JSON Schema (draft 2020-12).
The five files live in schemas/ and are served from this site at their $id
URL, so a validator can resolve the cross-references online:
| File | $id |
What it validates |
|---|---|---|
manifest.schema.json |
https://andresgmg.github.io/World-GeoJSON/schemas/manifest.schema.json | Every data/{body}/{ISO3}/manifest.json — identity, provenance and one entry per level. Its license enum is the licence allow-list |
index.schema.json |
https://andresgmg.github.io/World-GeoJSON/schemas/index.schema.json | data/index.json, this page |
feature.schema.json |
https://andresgmg.github.io/World-GeoJSON/schemas/feature.schema.json | One Feature of a full-resolution file: type, the required id, properties, a polygon geometry |
feature-properties.schema.json |
https://andresgmg.github.io/World-GeoJSON/schemas/feature-properties.schema.json | The properties object of a feature — the contract; no key outside it is allowed |
countries.schema.json |
https://andresgmg.github.io/World-GeoJSON/schemas/countries.schema.json | pipeline/src/wgj/tables/countries.json, the registry the pipeline reads |
wgj validate applies all five in CI: every manifest, the index,
the registry and every feature of every full-resolution file. Previews are not
covered — they carry a subset of the properties.
To check a file yourself with the jsonschema
package — the properties schema has no cross-references, so it needs nothing
else:
pip install jsonschema
python -c "
import json, jsonschema
schema = json.load(open('schemas/feature-properties.schema.json'))
for f in json.load(open('data/earth/CHL/CHL_ADM1.geojson'))['features']:
jsonschema.validate(f['properties'], schema)
print('ok')
"
For the schemas that reference each other (feature.schema.json →
feature-properties.schema.json; index.schema.json and
countries.schema.json → definitions in manifest.schema.json) run
wgj validate, which resolves them from the local
schemas/ directory and adds the checks a schema cannot express — id
uniqueness, parentID resolution, bbox against coordinates, checksums.