Build geography plugins for new countries so PxWeb/SDMX data joins to map geometry. Reads geometry files + API responses, generates a plugin following the Norway SSB pattern. Use when saying "geography plugin", "connect geometry", "ny geo-plugin", or "lägg till land".
Build a GeographyPlugin for a new country so data from its statistical API can join to map geometry.
Read these files before starting:
apps/web/lib/ai/tools/geography-plugins.ts — interface + existing plugins (Sweden SCB, Norway SSB, Denmark DST are the templates)apps/web/public/geo/{iso2}/admin1.geojson (or municipalities.geojson if available)For each country, the agent MUST gather:
Read the GeoJSON file and extract:
# Example: inspect Estonia geometry
node -e "const g=require('./apps/web/public/geo/ee/admin1.geojson'); console.log(g.features[0].properties); console.log('count:', g.features.length)"
Fetch a table listing and a sample table's metadata from the API:
# Example: fetch Estonia PxWeb table metadata
curl -s "https://andmed.stat.ee/api/v1/en/stat/RV/RV04/RV042/RV0422.px" | node -e "process.stdin.on('data',d=>console.log(JSON.parse(d).variables?.map(v=>({code:v.code,text:v.text,values:v.values?.slice(0,5)}))))"
Determine the join strategy:
These patterns were discovered empirically. Use them to skip research for common cases.
| Property | Found in | Meaning |
|---|---|---|
iso_3166_2 | Almost all admin1 files | Best join key — use direct_code strategy with ISO prefix (e.g., "IS-1", "EE-44", "SI03") |
name | All files | Fallback — use alias_crosswalk or label_match. Unreliable for non-English names. |
admin_code | Some admin2 files | Numeric code — check if it matches PxWeb codes directly |
| (no numeric code) | Most admin2/municipality files | Common case — must use label crosswalk. This is the norm, not the exception. |
| Language | Region dimension name | Municipality dimension name |
|---|---|---|
| English | "Region" | "Municipality" |
| Icelandic | "Landshluti" | "Sveitarfélag" |
| Estonian | "Maakond" | "Omavalitsus" |
| Slovenian | "Kohezijska regija" / "Statistična regija" | "Občine" |
| Norwegian | "Region" | "Region" (disambiguated by code pattern) |
| Swedish | "Region" | "Kommun" |
| Country | Admin1 codes | Join strategy |
|---|---|---|
| Iceland | Single digit "1"–"8" | Prefix with "IS-" → match iso_3166_2 |
| Estonia | 2-digit EHAK "37"–"87" | Hardcoded crosswalk to ISO (EHAK ≠ ISO suffix) |
| Slovenia | NUTS1 "SI03"/"SI04" or NUTS3 "SI011"–"SI044" | Direct match on iso_3166_2 |
| Norway | 2-digit "03"–"56" | Crosswalk to county names |
| Sweden | 2-digit SCB "01"–"25" | Crosswalk to county names or scb_code |
Municipality/admin2 geometry files typically only have name. This means:
alias_crosswalk via dimension labelsFollow the Norway SSB plugin pattern exactly. Each plugin exports:
export const {country}Plugin: GeographyPlugin = {
id: "pxweb-{iso2}-{agency}",
name: "{Country} {Agency} (PxWeb)",
family: "pxweb_country",
priority: 10,
appliesTo(source) { /* match by sourceId or countryHints */ },
matchCodes(codes, dimension) { /* recognize code patterns */ },
knownDimensions() { /* return dimension ID → level mappings */ },
joinKeyFamilies() { /* return source→target code family mappings */ },
aliasNormalizers() { /* return code transformation functions */ },
knownTables() { /* optional: hardcoded table IDs for common topics */ },
};
After creating the plugin, add it to the ALL_PLUGINS array at the bottom of geography-plugins.ts:
export const ALL_PLUGINS: GeographyPlugin[] = [
swedenScbPlugin,
norwaySsbPlugin,
denmarkDstPlugin,
// ... existing plugins
{country}Plugin, // ← add here
];
pnpm typecheck — must passpnpm test — must pass (especially geography-plugins.test.ts)When adding plugins for multiple countries, use /subagent-tasks:
| File | Purpose |
|---|---|
apps/web/lib/ai/tools/geography-plugins.ts | Plugin interface + all plugins |
apps/web/public/geo/{iso2}/admin1.geojson | Country admin1 boundaries |
apps/web/public/geo/{iso2}/municipalities.geojson | Municipality boundaries (if available) |
apps/web/lib/ai/tools/geography-detector.ts | Generic detector (plugins enrich, not replace) |
apps/web/lib/ai/tools/join-planner.ts | Join strategy selection |
matchCodes — this is how the detector recognizes the country's codesaliasNormalizers if codes don't directly match geometry propertiesdirect_code join strategy over alias_crosswalk over label_matchknownTables only if the country's PxWeb search is known to be unreliable