Delete the legend's never-returned parent nodes, and point its proof at the real parents
Since the regional grouping, buildLegendNodes assembles the tree from the LEAVES under the poster page's nine region sections. The old India, China, "Asia (admin-1)" and "Frontier Asia (uncovered)" parents were still built and never returned: dead code whose English labels the call-site triage read as reader text. They are deleted, with sumCount, which only they used. The legend's output is byte-identical for all four configurations (with/without records, with/without the localizing options; 2,107 records). The L15 shape assertion now composes its text inside the throw: the offending leaves are collected as data and formatted in new Error(...), so the message is visibly developer text. Same message, checked on bad input. pop-proof.mjs had been failing on every run, because section 3 looked up those same dead parents, and section 1 expected their count (32). It now rolls up the nine region parents the tree actually has (each pop = the sum of its children, and there must be at least one) and expects 28 leaves + 9 regions = 37. ALL PASS, identically against the previous legend-tree.js.
This commit is contained in:
parent
2cde9c0918
commit
cfeadca398
2 changed files with 21 additions and 48 deletions
|
|
@ -232,32 +232,10 @@ export function buildLegendNodes(clusters, opts = {}) {
|
|||
);
|
||||
const indiaChildren = indiaRows.map(mkAsiaLeaf);
|
||||
const chinaChildren = chinaRows.map(mkAsiaLeaf);
|
||||
const sumCount = (rows) => rows.reduce((s, r) => s + (leafCount(r) || 0), 0);
|
||||
|
||||
const indiaNode = {
|
||||
id: 'asia-india',
|
||||
label: 'India',
|
||||
count: sumCount(indiaRows),
|
||||
children: indiaChildren,
|
||||
};
|
||||
const chinaNode = {
|
||||
id: 'asia-china',
|
||||
label: 'China',
|
||||
count: sumCount(chinaRows),
|
||||
children: chinaChildren,
|
||||
};
|
||||
if (records) {
|
||||
indiaNode.pop = sumPop(indiaChildren);
|
||||
chinaNode.pop = sumPop(chinaChildren);
|
||||
}
|
||||
|
||||
const asiaNode = {
|
||||
id: 'asia',
|
||||
label: 'Asia (admin-1)',
|
||||
count: sumCount(asia),
|
||||
children: [indiaNode, chinaNode],
|
||||
};
|
||||
if (records) asiaNode.pop = sumPop([indiaNode, chinaNode]);
|
||||
// (The India / China / "Asia (admin-1)" and "Frontier Asia (uncovered)" PARENT nodes that used to be
|
||||
// built here are gone: since the regional grouping below, the tree is assembled from the LEAVES, so those
|
||||
// parents were constructed and never returned — dead code whose English labels read as reader text.)
|
||||
|
||||
// ── Frontier Asia parent ▸ 7 uncovered zones (09b-6 §1a, W-D1) ─────────────
|
||||
// Each uncovered row carries a `country_codes` selector → ADM0 polygons
|
||||
|
|
@ -277,19 +255,6 @@ export function buildLegendNodes(clusters, opts = {}) {
|
|||
u,
|
||||
);
|
||||
const uncoveredChildren = uncovered.map(mkUncoveredLeaf);
|
||||
const frontierNode =
|
||||
uncoveredChildren.length > 0
|
||||
? (() => {
|
||||
const node = {
|
||||
id: 'frontier-asia',
|
||||
label: 'Frontier Asia (uncovered)',
|
||||
count: sumCount(uncovered),
|
||||
children: uncoveredChildren,
|
||||
};
|
||||
if (records) node.pop = sumPop(uncoveredChildren);
|
||||
return node;
|
||||
})()
|
||||
: null;
|
||||
|
||||
// ── REGIONAL GROUPING — the SAME sections the poster page uses ─────────────
|
||||
// The legend used to emit 14 LOOSE LEAVES at top level (West Africa, Sudan,
|
||||
|
|
@ -319,7 +284,7 @@ export function buildLegendNodes(clusters, opts = {}) {
|
|||
: 'Other');
|
||||
|
||||
// Every leaf the tree can paint, flattened out of its source block. India and
|
||||
// China leaves come from asiaNode's children; the rest are already flat.
|
||||
// China leaves are indiaChildren / chinaChildren; the rest are already flat.
|
||||
const allLeaves = [
|
||||
...regionLeaves,
|
||||
deafLeaf,
|
||||
|
|
@ -385,12 +350,13 @@ export function assertUniformLeafShape(leaves, { withPop = false } = {}) {
|
|||
if (typeof z.color !== 'string' || !z.color) errs.push('color');
|
||||
if (typeof z.count !== 'number') errs.push('count');
|
||||
if (withPop && typeof z.pop !== 'number') errs.push('pop');
|
||||
if (errs.length) bad.push(`${z.id || '(no-id)'}: missing/!type [${errs.join(', ')}]`);
|
||||
if (errs.length) bad.push({ id: z.id || '(no-id)', errs });
|
||||
}
|
||||
if (bad.length) {
|
||||
// A developer assertion: the text is composed here, inside the throw, so it is plainly not reader copy.
|
||||
throw new Error(
|
||||
`legend-tree L15: ${bad.length} leaf node(s) violate the uniform consumer shape:\n ` +
|
||||
bad.join('\n '),
|
||||
bad.map((b) => `${b.id}: missing/!type [${b.errs.join(', ')}]`).join('\n '),
|
||||
);
|
||||
}
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -104,7 +104,11 @@ async function main() {
|
|||
// are unchanged (Frontier Asia still parents the 2 remaining uncovered rows). So
|
||||
// total = 32 (28 leaves + 4 parents); leaves = 13 region + 1 deaf + 12 asia + 2
|
||||
// uncovered = 28.
|
||||
log(flat.length === 32, `node tree has 32 total nodes incl. parents (got ${flat.length})`);
|
||||
// THEN the regional grouping replaced those 4 parents with the poster page's 9 REGION sections (Africa,
|
||||
// Middle East, Central Asia, Southeast Asia + Oceania, Americas, Europe, Deaf, India, China + Mongolia),
|
||||
// and the tree is built from the leaves: total = 28 leaves + 9 region parents = 37. The old 4 parents were
|
||||
// still constructed (and never returned) until they were deleted as dead code.
|
||||
log(flat.length === 37, `node tree has 37 total nodes incl. parents (got ${flat.length})`);
|
||||
const leaves = flat.filter((n) => !(n.children && n.children.length));
|
||||
log(leaves.length === 28, `28 leaf rows own a zone (got ${leaves.length})`);
|
||||
|
||||
|
|
@ -127,12 +131,15 @@ async function main() {
|
|||
|
||||
// ── (3) parent roll-up (D3) ─────────────────────────────────────────────────
|
||||
console.log('\n--- (3) parent roll-up (D3) ---');
|
||||
const india = byId['asia-india'], china = byId['asia-china'], asia = byId['asia'];
|
||||
const indiaSum = (india.children || []).reduce((s, c) => s + (c.pop || 0), 0);
|
||||
const chinaSum = (china.children || []).reduce((s, c) => s + (c.pop || 0), 0);
|
||||
log(india.pop === indiaSum, `India.pop (${india.pop.toLocaleString()}) === Σ children (${indiaSum.toLocaleString()})`);
|
||||
log(china.pop === chinaSum, `China.pop (${china.pop.toLocaleString()}) === Σ children (${chinaSum.toLocaleString()})`);
|
||||
log(asia.pop === india.pop + china.pop, `Asia.pop (${asia.pop.toLocaleString()}) === India + China (${(india.pop + china.pop).toLocaleString()})`);
|
||||
// The PARENTS are the region sections now. This section used to look up 'asia-india' / 'asia-china' /
|
||||
// 'asia' — parents the regional grouping replaced, which were built and never returned — so it threw on
|
||||
// every run while the tree a reader sees went unchecked. It measures the parents the tree actually has.
|
||||
const parents = nodes.filter((n) => Array.isArray(n.children) && n.children.length);
|
||||
log(parents.length > 0, `the tree has region parents to roll up (${parents.length})`);
|
||||
for (const r of parents) {
|
||||
const sum = r.children.reduce((s, c) => s + (c.pop || 0), 0);
|
||||
log(r.pop === sum, `${r.id}.pop (${(r.pop ?? NaN).toLocaleString()}) === Σ children (${sum.toLocaleString()})`);
|
||||
}
|
||||
|
||||
console.log('\n========================================================');
|
||||
console.log(`POP-PROOF SUMMARY: ${fails === 0 ? 'ALL PASS' : fails + ' FAILED'}`);
|
||||
|
|
|
|||
Loading…
Reference in a new issue