A complete India ADM1 (36 states) is staged beside UPG-100's 34-unit partition

The served gb-IND-ADM1.geojson is the 100-list's curated partition (Mizoram and Nagaland excluded,
listId-tagged), so a map colouring every state had data for 36 and shapes for 34. data/boundaries/
gb-IND-ADM1-full.geojson is all 36 from geoBoundaries gbOpen (boundaryID IND-ADM1-1811400, DataMeet /
Election Commission of India, CC BY 2.5 IN), with source and licence in gb-IND-ADM1-full.meta.json;
generate-tree stages both, and the dev server serves data/ directly. Reproducible with
library/scripts/boundaries/source-full-adm1.mjs (coordinates rounded to 3 decimals: 1.84 MB). The curated
file is untouched.
This commit is contained in:
Doxa Maps 2026-09-26 06:53:25 +00:00
parent 47a69fa1ce
commit 9d56943fae
4 changed files with 82 additions and 0 deletions

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,14 @@
{
"file": "gb-IND-ADM1-full.geojson",
"units": 36,
"source": "geoBoundaries gbOpen (www.geoboundaries.org)",
"boundaryID": "IND-ADM1-1811400",
"boundarySource": "DataMeet India community, Election Commission of India",
"licence": "Creative Commons Attribution 2.5 India (CC BY 2.5 IN)",
"licenceSource": "github.com/datameet/maps",
"yearRepresented": "2011",
"sourceUpdated": "Thu Jan 19 07:31:04 2023",
"fetchedFrom": "https://github.com/wmgeolab/geoBoundaries/raw/9469f09/releaseData/gbOpen/IND/ADM1/geoBoundaries-IND-ADM1_simplified.geojson",
"processing": "coordinates rounded to 3 decimals, consecutive duplicate points dropped (library/scripts/boundaries/source-full-adm1.mjs)",
"note": "Complete ADM1. The curated gb-IND-ADM1.geojson (the 100-list partition, listId-tagged) is a different file and is not replaced."
}

View file

@ -1515,6 +1515,10 @@ function main() {
[join(dataDir, 'adm0', 'world-adm0.geojson'), 'world-adm0.geojson'],
[join(dataDir, 'gb-IND-ADM1.geojson'), 'gb-IND-ADM1.geojson'],
[join(dataDir, 'gb-CHN-ADM1.geojson'), 'gb-CHN-ADM1.geojson'],
// COMPLETE India ADM1 (36 units, geoBoundaries gbOpen, CC BY 2.5 IN; source + licence in its meta),
// for maps that colour every state. The curated file above is UPG-100's 34-unit partition.
[resolve(__dirname, '..', 'data', 'boundaries', 'gb-IND-ADM1-full.geojson'), 'gb-IND-ADM1-full.geojson'],
[resolve(__dirname, '..', 'data', 'boundaries', 'gb-IND-ADM1-full.meta.json'), 'gb-IND-ADM1-full.meta.json'],
]) if (existsSync(from)) cpSync(from, join(bDir, to))
console.log('[generate-tree] gathered boundary geojson')
@ -1573,6 +1577,8 @@ function main() {
'boundaries/world-adm0.geojson',
'boundaries/gb-IND-ADM1.geojson',
'boundaries/gb-CHN-ADM1.geojson',
'boundaries/gb-IND-ADM1-full.geojson', // all 36 states (Cities with Internet's NFHS layer)
'boundaries/gb-IND-ADM1-full.meta.json', // its source and licence, served beside it
// people-groups — all-people-groups.vue (the slim JSON only; NOT the 16 MB CSV,
// which no element fetches and which dropin.exclude already withholds).
'people-groups/people-groups-slim.json',

View file

@ -0,0 +1,61 @@
#!/usr/bin/env node
/**
* source-full-adm1.mjs — a COMPLETE ADM1 layer for one country, beside (never replacing) the curated file.
*
* WHY: the shipped boundaries/gb-IND-ADM1.geojson is UPG-100's 100-list PARTITION (34 of India's 36 units;
* Mizoram and Nagaland excluded, and listId-tagged, see plugin-designs/my-upg-100-list/data/
* source-boundaries.mjs). A map that colours EVERY state (Cities with Internet's NFHS-5 layer) had data for
* 36 states and shapes for 34. This writes data/boundaries/gb-<ISO3>-ADM1-full.geojson, all units, from
* geoBoundaries gbOpen, with the source and licence in a .meta.json beside it; generate-tree stages both.
*
* Schema { iso3, shapeGroup, shapeName, shapeISO } (the curated file's, minus the curation's listId).
* Coordinates rounded to 3 decimals (~110 m, far finer than a state choropleth needs) and consecutive
* duplicate points dropped, to keep the served file small.
*
* USAGE node library/scripts/boundaries/source-full-adm1.mjs [ISO3=IND] (network, build time only)
*/
import { writeFileSync, mkdirSync } from 'node:fs'
import { resolve, dirname } from 'node:path'
import { fileURLToPath } from 'node:url'
const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '../../..')
const ISO3 = (process.argv[2] || 'IND').toUpperCase()
const API = `https://www.geoboundaries.org/api/current/gbOpen/${ISO3}/ADM1/`
const meta = await (await fetch(API)).json()
const url = meta.simplifiedGeometryGeoJSON || meta.gjDownloadURL
const src = await (await fetch(url)).json()
const r = (n) => Math.round(n * 1000) / 1000
const ring = (pts) => {
const out = []
for (const [x, y] of pts) { const p = [r(x), r(y)]; const q = out[out.length - 1]; if (!q || q[0] !== p[0] || q[1] !== p[1]) out.push(p) }
if (out.length && (out[0][0] !== out[out.length - 1][0] || out[0][1] !== out[out.length - 1][1])) out.push(out[0].slice())
return out.length >= 4 ? out : null
}
const poly = (rings) => { const rs = rings.map(ring).filter(Boolean); return rs.length ? rs : null }
const geom = (g) => {
if (g.type === 'Polygon') { const p = poly(g.coordinates); return p && { type: 'Polygon', coordinates: p } }
const ps = g.coordinates.map(poly).filter(Boolean)
return ps.length ? { type: 'MultiPolygon', coordinates: ps } : null
}
const features = []
for (const f of src.features) {
const g = geom(f.geometry)
if (!g) throw new Error(`${f.properties.shapeName}: geometry vanished when rounded`)
features.push({ type: 'Feature', properties: { iso3: ISO3, shapeGroup: f.properties.shapeGroup || ISO3, shapeName: f.properties.shapeName, shapeISO: f.properties.shapeISO || '' }, geometry: g })
}
if (String(features.length) !== String(meta.admUnitCount)) throw new Error(`expected ${meta.admUnitCount} units, got ${features.length}`)
const dir = resolve(ROOT, 'data/boundaries'); mkdirSync(dir, { recursive: true })
const out = resolve(dir, `gb-${ISO3}-ADM1-full.geojson`)
writeFileSync(out, JSON.stringify({ type: 'FeatureCollection', features }))
writeFileSync(resolve(dir, `gb-${ISO3}-ADM1-full.meta.json`), JSON.stringify({
file: `gb-${ISO3}-ADM1-full.geojson`, units: features.length,
source: 'geoBoundaries gbOpen (www.geoboundaries.org)', boundaryID: meta.boundaryID,
boundarySource: meta.boundarySource, licence: meta.boundaryLicense, licenceSource: meta.licenseSource,
yearRepresented: meta.boundaryYearRepresented, sourceUpdated: meta.sourceDataUpdateDate, fetchedFrom: url,
processing: 'coordinates rounded to 3 decimals, consecutive duplicate points dropped (library/scripts/boundaries/source-full-adm1.mjs)',
note: 'Complete ADM1. The curated gb-' + ISO3 + '-ADM1.geojson (the 100-list partition, listId-tagged) is a different file and is not replaced.',
}, null, 2) + '\n')
console.log(`${out}: ${features.length} units`)