doxa-maps/library/api
doxa-maps-88655c70 1449d47573 Add the Gospel Access filter, the ladder the binary flag was flattening
The Driver asked for "the phases of engagement, measured in the live data."
engagement_status cannot express them: over 2,108 live rows it is 2,081
Unengaged / 27 Engaged, so filtering on it separates 1.3% of the corpus and
tells a researcher almost nothing about where a group actually sits.

imb_gsec is the ordinal ladder underneath that flag, present on 2,106/2,108:

    0   No Evangelicals, Churches or Resources ........... 225
    1   Less than 2% Evangelical, No Active CP Activity . 1,853
    2   Less than 2% Evangelical, Concentrated CP ......... 23
    4   2% or Greater but Less than 5% Evangelical ......... 4
    6   10% or Greater Evangelical ......................... 1

Cross-tabbed against the flag, ALL 225 groups with no evangelicals, churches or
resources -- the most lost category in the data -- are merely "Unengaged",
indistinguishable from the 1,841 groups that have some evangelical presence.
That distinction is the one the map exists to show.

Verified safe to filter on before wiring, the same way cluster/people_group
were: 5 distinct {value,label} pairs and NO value-vs-label cross-collision, so
valueMatches() selects the same rows whichever vocabulary arrives. It is a real
row field, unlike the derived language_family/religion_family facets, so it is
mapped in FACET_TO_PROP from the day it lands rather than joining the
dashboard-only class.

Wired through every layer that would otherwise silently drop it: the shared
sources.json defaultFields (the dashboard reads rows through that seam), the
grid's own fetch field list, the prop, the API-field-name filter key (imb_gsec,
not gospelAccess), the re-filter watch list, and the composite's prop binding.

Verified in a browser on the built tree with live data: the section renders all
five rungs with counts matching the API exactly; clicking "No Evangelicals"
takes dashboard scope 2,108 -> 225 and the grid to "Showing 225"; clicking
"Concentrated CP" gives 23 and "Showing 23"; deselecting returns both to
baseline. 0 failed requests.

Gates: facet-one-field, facet-cascade, facet-changes-map, csv-shape,
label-joins, one-palette, sfc-parse, parse all pass. verify:gate1 still 13/13
against 2,108 live records.
2026-09-23 13:19:22 +00:00
..
_registry.js doxa-maps — portable bundler for embeddable JAMstack maps 2026-09-10 01:53:07 +00:00
apiBaseUrl.js doxa-maps — portable bundler for embeddable JAMstack maps 2026-09-10 01:53:07 +00:00
DataSourceManager.js doxa-maps — portable bundler for embeddable JAMstack maps 2026-09-10 01:53:07 +00:00
README.md consolidate docs into documentation/ and deployment shells into deployment/ 2026-09-21 23:23:23 +00:00
sources.json Add the Gospel Access filter, the ladder the binary flag was flattening 2026-09-23 13:19:22 +00:00

library/api/ — where map data comes from

One of the three customization seams (colors/, api/, i18n/). This one decides where people-group data comes from and how its raw fields map onto the field names the map reads. The components are source-agnostic — only the connection knows the source's shape.

  sources.json            DataSourceManager.js         the map
  ┌──────────────────┐    ┌───────────────────┐        ┌──────────────┐
  │ your connection   │──►│ fetch → normalize  │──────►│ map · legend  │
  │ (type, endpoint,  │    │ → cache            │        │ · detail card │
  │  fieldMappings)   │    └───────────────────┘        └──────────────┘
  └──────────────────┘     maps raw rows → system field names
  • sources.json is your surface — add a connection (csv, api, or rest-api) and set activeSource. No code.
  • DataSourceManager.js is shared machinery — you never edit it.
  • Import through _registry.js (SOURCES, getSourceConfig(), listSources(), …).

Adding or overriding a connection? The full contract (required fields, checklist, integration) is in ../../documentation/architecture/data-sources.md.