60 countries carrying pins had no country row at all

Set out to verify the map's second mode, "Resources found", which had never been tested.
It works: the legend switches, the arithmetic is an exact complement of the gap view — 0
of 170 countries disagreed — and the two modes genuinely repaint, 98/46/12/6 polygons by
band in gap against 85/66/9/2 in coverage.

Proving that took three attempts and the third one found the real defect. Comparing the
fill expression proved nothing, because it is ["get","color"] in BOTH modes — the colour
is baked onto each feature and switching modes rewrites the source data. Comparing the
features instead showed 157 coloured and 5 transparent against 170 rows in the table,
and that mismatch was the thread worth pulling.

The previous refresh of this table only UPDATED rows that already existed, and the table
came from the curated 2,106-record set. So 60 countries carrying pins had no row at all —
150 people groups with nothing found, invisible to every country-level surface. Canada
20, Ghana 15, Timor-Leste 13, Australia 7. 72 countries are now added from the pins, and
0 countries with bare groups are missing; all 242 rows agree with the pins on groups and
no_resource.

Curated columns on the new rows are null, never zero: this build does not know a growth
rate for Canada, and zero is a claim. They are flagged from_pins_only so the different
basis is visible rather than implied. Names come from people-groups-slim's CountryName,
because a row labelled "CAN" renders "CAN" as the sheet's title. UNm49Region was
deliberately NOT used for region — it is a numeric code, and "19" is worse than blank.
One row stays a bare code: Kosovo, whose non-standard KOS has no name in the source, and
inventing one for a single row is not worth it.

The boundary file turns out to hold only 162 polygons and has no Canada, Australia,
Timor-Leste or Taiwan, so this paints five previously-transparent countries and fixes the
rest where it actually shows: clicking a pin in Canada now opens a sheet titled "Canada"
with 304 groups, matching the pins exactly, where before it fell through to a bare ISO
code with no statistics.
This commit is contained in:
doxa-maps-88655c70 2026-09-24 15:19:42 +00:00
parent 35933d4ec4
commit cde152bde7
3 changed files with 2348 additions and 4 deletions

File diff suppressed because it is too large Load diff

View file

@ -36,6 +36,22 @@ const OUT = resolve(REPO, 'data/tables/gospel-by-country.json')
const pins = JSON.parse(readFileSync(resolve(REPO, 'data/tables/people-group-pins.json'), 'utf8'))
const j = JSON.parse(readFileSync(OUT, 'utf8'))
// Real names, from the same source the pins are built from. A row labelled "CAN" would
// render "CAN" as the country sheet's title; people-groups-slim carries CountryName and
// UNm49Region per group, so there is no reason to show a code.
const nameOf = {}
try {
const raw = JSON.parse(readFileSync(resolve(REPO, 'data/people-groups/people-groups-slim.json'), 'utf8'))
const rows = Array.isArray(raw) ? raw : Object.values(raw).find(Array.isArray)
for (const r of rows || []) {
const i = r.ISOAlpha3
if (!i) continue
if (!nameOf[i] && r.CountryName) nameOf[i] = String(r.CountryName).trim()
// UNm49Region is a NUMERIC region code (19, 142, ...), not a name. Rendering "19" as a
// region is worse than rendering nothing, and every existing row carries region: ''.
}
} catch { /* names are a nicety; the bands are the point */ }
const agg = {}
for (const [, , , iso3, code, pop, tier] of pins.pins) {
if (!iso3) continue
@ -49,6 +65,30 @@ for (const [, , , iso3, code, pop, tier] of pins.pins) {
}
const band = (p) => (p >= 60 ? 'critical' : p >= 30 ? 'high' : p > 0 ? 'partial' : 'covered')
// ═══ COUNTRIES THE PINS TOUCH BUT THE TABLE NEVER HAD ═════════════════════════════════
// The first version of this script only UPDATED rows that already existed, and the table
// came from the curated 2,106-record set. So 60 countries carrying pins had no row at all
// and could never be coloured — 150 groups with nothing found, invisible to the country
// view. Canada 20, Ghana 15, Timor-Leste 13, Australia 7.
// They are added with the pin-derived fields only. The curated columns are set to null
// rather than zero: this build does not know a growth rate for them, and zero is a claim.
const known = new Set(j.countries.map((c) => c.iso3))
let added = 0
for (const [iso3, e] of Object.entries(agg)) {
if (known.has(iso3) || !e.groups) continue
j.countries.push({
iso3,
country: nameOf[iso3] || iso3,
region: '',
groups: 0, no_resource: 0, pop_no_resource: 0, scripture: 0, film: 0,
growing_unreached: null, deaths_day: null, growth: null,
pct_no_resource: 0, band: 'covered',
from_pins_only: true,
})
added++
}
let moved = 0, missing = 0
for (const c of j.countries) {
const e = agg[c.iso3]
@ -70,8 +110,8 @@ j.stats = {
refreshed: new Date().toISOString().slice(0, 10),
basis: 'data/tables/people-group-pins.json — the same index the pins are drawn from, so the fill and the dots cannot disagree.',
caveat: 'NO RESOURCE means not found in the pools checked — eBible, PNG.Bible, Arclight/JESUS Film, Door43 and Faith Comes By Hearing. GRN audio, api.bible, DBL and progress.bible are unchecked; absence here is never proof that nothing exists.',
curated_columns: 'growing_unreached, deaths_day and growth come from the curated country analysis over a narrower set and are NOT derived from the pins.',
curated_columns: 'growing_unreached, deaths_day and growth come from the curated country analysis over a narrower set and are NOT derived from the pins. A row flagged from_pins_only carries null for all three, because this build does not know them — null, never zero, which would be a claim.',
}
writeSnapshot(OUT, `${JSON.stringify(j, null, 1)}\n`, (x) => (x.countries || []).length, 'countries')
console.log(`COUNTRY-BANDS: ${j.countries.length} countries, ${moved} changed band, ${missing} had no pins and were left alone.`)
console.log(`COUNTRY-BANDS: ${j.countries.length} countries, ${added} newly added from the pins, ${moved} changed band, ${missing} had no pins and were left alone.`)

File diff suppressed because it is too large Load diff