Loading documentation...
ETL contract for fire_type, building_type, building_age, and region native_value ↔ filters rows.
This file is the canonical filter map used across adapters.
NULL fact FK — never a filter row. The upstream "select nothing" request (empty BRASK form field, empty BRIS *.ids array, SSB Bygningsbranner dataset) produces a fact with NULL on that axis column. There is no ALL / ALL_BUILDINGS filter standard_code; total slices are read with an omitted get_fire_data axis param (→ <axis>_code IS NULL). The dimension-total category (cross-tab ALL_BUILDINGS / ALL_AGES on categories.standard_code, dim 5/6) is a separate concept — see category-native-to-cat-code.md.CONFINED / DEVELOPEDRESIDENTIAL (the only cross-source building_type filter code; "all buildings" is NULL, not a code)0-5 / 6-10 / 11-20 / 21-30 / 31-50 / 51+ / UNKNOWN — BRASK only, no cross-source equivalenthttps://brask.finansnorge.no/. An empty string value means "Alle" (all values selected). Specific selections pass the numeric option id./aggregate ("Alle branner og ulykker") — supports Fire Type, Building Type, Region./restrictedaggregation ("Hvordan bygningsbranner starter") — supports Fire Type and Region only. Building Type is not available.KOKkommuneregion0000 dimension (national EAK or kommune codes) — see §4. SSB has no Fire Type or Building Age.get_fire_data) vs ingest-time requestsCharts: Passing standard_code strings such as RESIDENTIAL or CONFINED into get_fire_data resolves to all matching filters.id rows on that axis for the requested source(s) (postgres-contract-rls-and-keys.md). Passing multiple tokens in the same axis argument (e.g. two native_code values or both a standard_code and a leaf code) resolves to the union of matching ids (OR within that axis). Omitted filter-axis arguments mean only facts where that axis FK is NULL (upstream unfiltered slice). Different axes are combined with AND.
ETL: Adapter POST bodies use upstream-native identifiers (form ids, Matrikkel codes, BRIS mission-type ids, etc.). On ingest, adapters resolve each applied native to filters.id and write that integer onto fact FK columns — never the native string. filters.native_code and standard_code live on the catalog row only.
ETL rule — one native per slice: Each ingest slice resolves to exactly one filters.id per applied axis; the adapter POST body uses that row's native_code (Matrikkel leaf id, BRIS mission-type id, kommune code, etc.), not a rollup standard_code. When the UI selects a parent standard_code (e.g. RESIDENTIAL, COMMERCIAL), resolveFilterAxisTokens expands it to all matching leaf filters.id values before ingest; the BRIS adapter then passes buildingTypeIdsOverride: [native] with one Matrikkel leaf per slice (policeAggregateBuildingParams in lib/data-model/adapters/bris.ts). Fire type and region follow the same rule (toMissionTypeIds, counties.ids). Building age is BRASK-only (filterNativeCode on the form field). BRIS does not expose building age.
Catalog labels: filters.name is a shortened per-leaf UI label (typically the text before the first comma in native_name). filters.standard_name labels standard_code rollups in the UI (e.g. Bolig for RESIDENTIAL). Neither field is an ingest axis token.
BRIS precision: Examples below sometimes show several mission-type ids in one JSON body (e.g. all CONFINED natives). The HeatWaves BRIS adapter ingests one native mission type id per slice and stamps the matching filters.id into fire_type_code — not the BRIS numeric id itself.
| Filter | /aggregate | /restrictedaggregation | Request body field |
|---|---|---|---|
| Fire Type | ✓ | ✓ | revisedMissionTypes.ids |
| Building Type | ✓ | ✗ | buildingTypes.ids |
| Region (county) | ✓ | ✓ | counties.ids |
| Region (municipality) | ✓ | ✗ | municipalities.ids |
| Building Age | ✗ | ✗ | — |
standard_codes: CONFINED | DEVELOPED
Supported by: BRASK, BRIS (all sub-sources). Not supported by SSB.
Definition: CONFINED — fire contained to the object/item of origin (Begrenset branntilløp). DEVELOPED — fire spread beyond it (Utviklet brann).
| name | source | native_param | native_value | native_name | standard_code |
|---|---|---|---|---|---|
| Alle branntyper | — | (empty request) | — | — | NULL FK (no filter row) |
| Begrenset | BRASK | ctl00$Innhold$lbType | 2 | Kald | CONFINED |
| Utviklet | BRASK | ctl00$Innhold$lbType | 1 | Varm | DEVELOPED |
| Begrenset (komfyr) | BRIS — Police Sub-A | revisedMissionTypes.ids | 10086 | Branntilløp komfyr | CONFINED |
| Begrenset (bygg annet) | BRIS — Police Sub-A | revisedMissionTypes.ids | 10212 | Branntilløp i bygg annet | CONFINED |
| Begrenset (skorstein) | BRIS — Police Sub-A | revisedMissionTypes.ids | 10175 | Brann i skorstein | CONFINED |
| Utviklet | BRIS — Police Sub-A | revisedMissionTypes.ids | 10026 | Brann i bygning | DEVELOPED |
| Begrenset (komfyr) | BRIS — Police Sub-B | revisedMissionTypes.ids | 10086 | Branntilløp komfyr | CONFINED |
| Begrenset (bygg annet) | BRIS — Police Sub-B | revisedMissionTypes.ids | 10212 | Branntilløp i bygg annet | CONFINED |
| Utviklet | BRIS — Police Sub-B | revisedMissionTypes.ids | 10026 | Brann i bygning | DEVELOPED |
| Begrenset (komfyr) | BRIS — Fire Brigade | revisedMissionTypes.ids | 10086 | Branntilløp komfyr | CONFINED |
| Begrenset (bygg annet) | BRIS — Fire Brigade | revisedMissionTypes.ids | 10212 | Branntilløp i bygg annet | CONFINED |
| Utviklet | BRIS — Fire Brigade | revisedMissionTypes.ids | 10026 | Brann i bygning | DEVELOPED |
| Begrenset (komfyr) | BRIS — DSB | revisedMissionTypes.ids | 10086 | Branntilløp komfyr | CONFINED |
| Begrenset (bygg annet) | BRIS — DSB | revisedMissionTypes.ids | 10212 | Branntilløp i bygg annet | CONFINED |
| Utviklet | BRIS — DSB | revisedMissionTypes.ids | 10026 | Brann i bygning | DEVELOPED |
Brann i skorstein(10175) exists only in the "Alle branner og ulykker" dataset (Police Sub-A). It does not appear in "Hvordan bygningsbranner starter". It is a CONFINED-class chimney fire unique to that dataset.
BRASK — ASP.NET form POST field. One fetch per fire type:
# CONFINED (Kald)
ctl00$Innhold$lbType = 2
# DEVELOPED (Varm)
ctl00$Innhold$lbType = 1
# All fire types (empty = Alle)
ctl00$Innhold$lbType =
BRIS /aggregate — revisedMissionTypes.ids array in the JSON body. Fetch one slice per Oppdragstype value, then standardize at ingest:
// DEVELOPED only { "revisedMissionTypes": { "ids": [10026], "isMissingValue": false } } // CONFINED only (Police Sub-A — all three confined types) { "revisedMissionTypes": { "ids": [10086, 10212, 10175], "isMissingValue": false } } // CONFINED only (Police Sub-B, Brigade, DSB — no skorstein) { "revisedMissionTypes": { "ids": [10086, 10212], "isMissingValue": false } }
BRIS /restrictedaggregation — same revisedMissionTypes.ids field, restricted body:
{ "revisedMissionTypes": { "ids": [10026], "isMissingValue": false }, "counties": { "ids": [], "isMissingValue": false }, "hundredAndTenCentrals": { "ids": [], "isMissingValue": false }, "includePoliceCauseWithNoMission": false, "aggregationQuery": { ... } }
Ingest iterates all relevant Oppdragstype values, tags each row with the standardized fire type code, and aggregates the slices.
standard_codes: RESIDENTIAL ("all buildings" is the unfiltered slice — NULL building_type FK, not a code)
Supported by: SSB (dataset selection only), BRASK (næring), BRIS /aggregate only. Not supported by BRIS /restrictedaggregation.
| name | source | native_param | native_value | native_name | standard_code |
|---|---|---|---|---|---|
| Bolig | SSB | dataset | Boligbranner | Boligbranner | RESIDENTIAL |
| Alle bygg | SSB | dataset | Bygningsbranner | — | NULL FK (no filter row) |
| Bolig | BRASK | ctl00$Innhold$lbNæring | 0 | Beboelse | RESIDENTIAL |
| Alle bygg | BRASK | ctl00$Innhold$lbNæring | (empty) | — | NULL FK (no filter row) |
| Bolig (rollup) | BRIS /aggregate | buildingTypes.ids | 30 Matrikkel leaf-IDs (one per ingest slice) | — | RESIDENTIAL (expands to all 1xx leaves) |
| Bolig (leaf) | BRIS /aggregate | buildingTypes.ids | one 3-digit leaf id | e.g. Enebolig (111) | RESIDENTIAL |
| Non-residential (leaf) | BRIS /aggregate | buildingTypes.ids | one 3-digit leaf id | e.g. Fabrikkbygning (211) | INDUSTRY / COMMERCIAL / OTHER / … |
| Alle bygg | BRIS /aggregate | buildingTypes.ids | (empty array) | — | NULL FK (no filter row) |
| — | BRIS /restrictedaggregation | — | — | Not supported | — |
SSB — no filter parameter. Two separate datasets are queried depending on the desired scope:
Boligbranner = RESIDENTIALBygningsbranner = all building fires (including residential) → stored with building_type_code NULL (the unfiltered total), not a filter rowBRASK — ASP.NET form POST field using the næring (industry) dimension. Only the Beboelse value is used for the building type split:
# RESIDENTIAL (Beboelse)
ctl00$Innhold$lbNæring = 0
# All buildings (empty = Alle næringer) → stored as NULL building_type FK
ctl00$Innhold$lbNæring =
Note: næring has 22 industry sector values in total. For the Building Type filter, only 0 (Beboelse) and empty (all) are relevant. The full næring taxonomy is not used as a cross-source filter — it is a BRASK-specific analytical dimension.
BRIS /aggregate — buildingTypes.ids array of Matrikkel leaf-level 3-digit string codes. Only leaf-level IDs work; parent IDs (e.g. "1" or "11") return zero results.
Explore / Utforsk ingest: When the user selects Bolig (or individual leaves), Explore expands to one ingest slice per leaf filters.id, each calling the API with that leaf's native_code only — not the 30-id Bolig list and not standard_code=RESIDENTIAL. The whole-Bolig JSON below is the upstream shape for a single parent rollup request (native_code 0 or non-leaf RESIDENTIAL parent), not what each leaf slice sends.
// RESIDENTIAL (Bolig — 30 leaf IDs from category "1") { "buildingTypes": { "ids": [ "111","112","113","121","122","123","124","131","133","135","136", "141","142","143","144","145","146","151","152","159","161","162", "163","171","172","181","182","183","193","199" ], "isMissingValue": false } } // All buildings (no filter) → stored as NULL building_type FK { "buildingTypes": { "ids": [], "isMissingValue": false } }
Records without building type data are excluded when a filter is applied. To also include records with missing building type, set isMissingValue: true.
Source of truth: scripts/dsb_detailed_site_map.md ("Bolig Leaf IDs").
| native_value | native_name |
|---|---|
111 | Enebolig |
112 | Enebolig med hybelleilighet, sokkelleilighet o.l. |
113 | Våningshus |
121 | Tomannsbolig, vertikaldelt |
122 | Tomannsbolig, horisontaldelt |
123 | Våningshus, tomannsbolig, vertikaldelt |
124 | Våningshus, tomannsbolig, horisontaldelt |
131 | Rekkehus |
133 | Kjedehus inkl. atriumhus |
135 | Terrassehus |
136 | Andre småhus med 3 boliger eller flere |
141 | Store frittliggende boligbygg på 2 etasjer |
142 | Store frittliggende boligbygg på 3 og 4 etasjer |
143 | Store frittliggende boligbygg på 5 etasjer eller over |
144 | Store sammenbygde boligbygg på 2 etasjer |
145 | Store sammenbygde boligbygg på 3 og 4 etasjer |
146 | Store sammenbygde boligbygg på 5 etasjer og over |
151 | Bo- og servicesenter |
152 | Studenthjem/studentboliger |
159 | Annen bygning for bofellesskap |
161 | Fritidsbygning (hytter, sommerhus o.l.) |
162 | Helårsbolig benyttet som fritidsbolig |
163 | Våningshus benyttet som fritidsbolig |
171 | Seterhus, sel, rorbu o.l. |
172 | Koie, gamme |
181 | Garasje, uthus, anneks til bolig |
182 | Boligbrakke |
183 | Naust, båthus, sjøbu |
193 | Bolig med forretning, kontor e.l. |
199 | Annen boligbygning (uklassifisert) |
Not a filter row or filters.name. «Ikke bolig» is a UI/analytics shorthand: compare the all-buildings total (building_type_code IS NULL) against RESIDENTIAL, or sum non-residential leaf facts (COMMERCIAL, INDUSTRY, OTHER, PUBLIC, UNKNOWN). See building-type-semantics.ts.
Upstream enumeration reference: NON_RESIDENTIAL_MATRIKKEL_LEAF_IDS in _brisUpstream.constants.ts (derived from https://brannstatistikk.no/api/v1/buildinginfo/types). Each leaf is a separate filters row with its own standard_code bucket — ingest uses one row per slice, never the full list in one POST.
211: Fabrikkbygning 212: Verkstedbygning 214: Bygning for renseanlegg 216: Bygning for vannforsyning, bl.a. pumpestasjon 219: Annen industribygning 221: Kraftstasjon (>15 000 kVA) 223: Transformatorstasjon (>10 000 kVA) 229: Annen energiforsyningsbygning 231: Lagerhall 232: Kjøle- og fryselager 233: Silobygning 239: Annen lagerbygning 241: Hus for dyr, fôrlager, strølager, frukt- og grønnsakslager, landbrukssilo, høy-/korntørke 243: Veksthus 244: Driftsbygning for fiske og fangst, inkl. oppdrettsanlegg 245: Naust/redskapshus for fiske 248: Annen fiskeri- og fangstbygning 249: Annen landbruksbygning 311: Kontor- og administrasjonsbygning, rådhus 312: Bankbygning, posthus 313: Mediebygning 319: Annen kontorbygning 321: Kjøpesenter, varehus 322: Butikkbygning 323: Bensinstasjon 329: Annen forretningsbygning 330: Messe- og kongressbygning 411: Ekspedisjonsbygning, flyterminal, kontrolltårn 412: Jernbane- og T-banestasjon 415: Godsterminal 416: Postterminal 419: Annen ekspedisjons- og terminalbygning 429: Telekommunikasjonsbygning 431: Parkeringshus 439: Annen garasje- hangarbygning 441: Trafikktilsynsbygning 449: Annen veg- og trafikktilsynsbygning 511: Hotellbygning 512: Motellbygning 519: Annen hotellbygning 521: Hospits, pensjonat 522: Vandrerhjem, feriehjem/-koloni, turisthytte 523: Appartement 524: Campinghytte/utleiehytte 529: Annen bygning for overnatting 531: Restaurantbygning, kafébygning 532: Sentralkjøkken, kantinebygning 533: Gatekjøkken, kioskbygning 539: Annen restaurantbygning 611: Lekepark 612: Barnehage 613: Barneskole 614: Ungdomsskole 615: Kombinert barne- og ungdomsskole 616: Videregående skole 619: Annen skolebygning 621: Universitets- og høgskolebygning med integrerte funksjoner, auditorium, lesesal o.a. 623: Laboratoriebygning 629: Annen universitets-, høgskole- og forskningsbygning 641: Museum, kunstgalleri 642: Bibliotek, mediatek 643: Zoologisk og botanisk hage 649: Annen museums- og bibliotekbygning 651: Idrettshall 652: Ishall 653: Svømmehall 654: Tribune og idrettsgarderobe 655: Helsestudio 659: Annen idrettsbygning 661: Kinobygning, teaterbygning, opera/konserthus 662: Samfunnshus, grendehus 663: Diskotek 669: Annet kulturhus 671: Kirke, kapell 672: Bedehus, menighetshus 673: Krematorium, gravkapell, bårehus 674: Synagoge, moské 675: Kloster 679: Annen bygning for religiøse aktiviteter 719: Sykehus 721: Sykehjem 722: Bo- og behandlingssenter, aldershjem 723: Rehabiliteringsinstitusjon, kurbad 729: Annet sykehjem 731: Klinikk, legekontor/-senter/-vakt 732: Helse- og sosialsenter, helsestasjon 739: Annen primærhelsebygning 819: Fengselsbygning 821: Politistasjon 822: Brannstasjon, ambulansestasjon 823: Fyrstasjon, losstasjon 824: Stasjon for radarovervåkning av fly- og/eller skipstrafikk 825: Tilfluktsrom/bunker 829: Annen beredskapsbygning 830: Monument 840: Offentlig toalett
standard_codes: 0-5 | 6-10 | 11-20 | 21-30 | 31-50 | 51+ | UNKNOWN
Supported by: BRASK only. Not supported by SSB or BRIS.
BRASK exposes 13 fine-grained age buckets. HeatWaves normalizes these into 6 coarser buckets for storage.
| name | source | native_param | native_value | native_name | normalized_bucket |
|---|---|---|---|---|---|
| Alle aldersgrupper | BRASK | — | (empty) | — | NULL FK (no filter row) |
| 0–5 år | BRASK | ctl00$Innhold$lbAldersgruppe | 1 | Ny | 0-5 |
| 0–5 år | BRASK | ctl00$Innhold$lbAldersgruppe | 2 | 1-5 | 0-5 |
| 6–10 år | BRASK | ctl00$Innhold$lbAldersgruppe | 3 | 6-10 | 6-10 |
| 11–20 år | BRASK | ctl00$Innhold$lbAldersgruppe | 4 | 11-15 | 11-20 |
| 11–20 år | BRASK | ctl00$Innhold$lbAldersgruppe | 5 | 16-20 | 11-20 |
| 21–30 år | BRASK | ctl00$Innhold$lbAldersgruppe | 6 | 21-25 | 21-30 |
| 21–30 år | BRASK | ctl00$Innhold$lbAldersgruppe | 7 | 26-30 | 21-30 |
| 31–50 år | BRASK | ctl00$Innhold$lbAldersgruppe | 8 | 31-40 | 31-50 |
| 31–50 år | BRASK | ctl00$Innhold$lbAldersgruppe | 9 | 41-50 | 31-50 |
| 51+ år | BRASK | ctl00$Innhold$lbAldersgruppe | 10 | 51-75 | 51+ |
| 51+ år | BRASK | ctl00$Innhold$lbAldersgruppe | 11 | 76-100 | 51+ |
| 51+ år | BRASK | ctl00$Innhold$lbAldersgruppe | 12 | Over 100 | 51+ |
| Ukjent | BRASK | ctl00$Innhold$lbAldersgruppe | 99 | Ukjent | UNKNOWN |
Building Age is a BRASK-specific dimension that can be used in two ways:
BRASK — ASP.NET form POST field:
# Single age bucket (6-10 år)
ctl00$Innhold$lbAldersgruppe = 3
# Multiple buckets (select both 6-10 and 11-15)
ctl00$Innhold$lbAldersgruppe = 3
ctl00$Innhold$lbAldersgruppe = 4
# All age groups (empty = Alle)
ctl00$Innhold$lbAldersgruppe =
Normalization note: two BRASK native values (Ny and 1-5) both map to the 0-5 normalized bucket. When querying by normalized bucket, the adapter must send both native ids to cover the full range.
BRIS — not supported on any endpoint.
SSB — not supported on this axis.
No unified standard code — geographic systems are incompatible between sources.
Supported by: BRASK (cresta-sone, pre-2020 counties), BRIS /aggregate and /restrictedaggregation (counties.ids and municipalities.ids), SSB table 12058 (KOKkommuneregion0000 — national EAK or per-municipality codes; see ssb-pxwebapi-tables.md and pipeline §4.3).
KOKkommuneregion0000)EAK → stored as region_code IS NULL (not a filters.id).{ "filter": "item", "values": ["<4-digit code>", …] } → region_code = resolved filters.id (same spine as BRIS municipality ids where codes align).BRASK uses Cresta-sone ids on form field ctl00$Innhold$lbFylke (empty = Alle). Native values and Cresta → canonical 2024 county mapping: regions-and-cresta.md §1.
Note: Cresta id
13is absent in upstream numbering (see regions doc).
BRIS uses current Norwegian administrative geographic units. County IDs and municipality IDs come from the national geographic registry (SSR/Kartverket). These do not correspond to BRASK's Cresta-sone ids.
Examples:
3, municipality id 30130 (post-2020 merged county, contains old Akershus, Buskerud, Østfold)The full county and municipality ID lists are fetched from GET /api/v1/buildinginfo/nace (or via the Pinia store where-data-options) at runtime and are not hardcoded in HeatWaves.
BRASK — ASP.NET form POST field. Empty = all regions. Single or multiple values:
# Oslo only
ctl00$Innhold$lbFylke = 3
# Oslo and Akershus
ctl00$Innhold$lbFylke = 3
ctl00$Innhold$lbFylke = 2
# All regions (empty = Alle)
ctl00$Innhold$lbFylke =
BRIS /aggregate and /restrictedaggregation — counties.ids (coarse) or municipalities.ids (fine) array in the JSON body. Both endpoints accept this field. Empty array = all regions:
// Single county (Oslo = 3) { "counties": { "ids": [3], "isMissingValue": false } } // Single municipality (Oslo = 301) { "municipalities": { "ids": [301], "isMissingValue": false } } // All regions { "counties": { "ids": [], "isMissingValue": false } }
| Filter | Cross-source comparable? | Notes |
|---|---|---|
| Fire Type | Yes | Both BRASK and BRIS expose it natively. HeatWaves standardizes to CONFINED/DEVELOPED. The only gap is Brann i skorstein (Police Sub-A only) — it contributes to CONFINED totals but has no BRASK or restricted-dataset equivalent. |
| Building Type | Partial | Comparable between BRASK and BRIS /aggregate (Police Sub-A). Not available in BRIS /restrictedaggregation (Police Sub-B, Fire Brigade, DSB) — those sub-sources always include all building types. |
| Building Age | No | BRASK-only. No equivalent in BRIS or SSB. |
| Region | No | BRASK uses pre-2020 Cresta-sone county ids. BRIS uses current post-2020 administrative ids. Direct comparison requires a manual lookup table between the two systems, which HeatWaves does not currently implement. |
When changing BRASK form fields or BRIS county/municipality filters, update this file and regions-and-cresta.md. Adapter code: lib/data-model/adapters/_registry.ts, _braskWebForms.constants.ts, bris.ts.