One thing worth exploring is to change the projection from Mercator to something like Albers Equal Area Conic projection (EPSG:9822).
It will reduce the size of Finnmark somewhat and bring it in line with the rest of the country. It can also be fun to experiment with some of the options.
Here are some AI suggestions, but I have no verified or tested it.
- Central Meridian: 15.0° E (Centers the projection along Norway's longitudinal axis).
- Standard Parallel 1: 59.0° N (Focuses on Southern Norway).
- Standard Parallel 2: 69.0° N (Focuses on Northern Norway).
- Latitude of Origin: 63.0° N (The approximate geographic center).
- False Easting: 0.0
- False Northing: 0.0
Description
Interaktivt koroplettkart over Norge med gjennomsnittlig boligpris per m² for alle kommuner. Data hentes automatisk fra SSB og oppdateres via en Novem-jobb.
Pek på en kommune for å se navn og pris. Klikk for å flytte zoom-utsnittet til den valgte kommunen.
Datainnhenting
Data hentes av fetch_data.py fra to SSB-endepunkter:
- SSB tabell 06035 (PxWebAPI) — gjennomsnittlig kvadratmeterpris per kommune, siste tilgjengelige kvartal
- SSB KLASS 131 (KLASS API) — kommunenavn på bokmål
Spørringen bruker JSON-stat2-formatet og henter alle 4-sifrede kommunekoder samtidig:
query = {
"query": [
{"code": "Region", "selection": {"filter": "item", "values": all_codes}},
{"code": "Boligtype", "selection": {"filter": "item", "values": [alle_code]}},
{"code": "ContentsCode", "selection": {"filter": "item", "values": [price_code]}},
{"code": "Tid", "selection": {"filter": "item", "values": [latest_tid]}},
],
"response": {"format": "json-stat2"}
}
Kommunegrenser som SVG-stier
Grensedataene kommer fra kommuner_S.topojson (Kartverket/GeoNorge). I stedet for å prosessere TopoJSON i nettleseren, er alle grensene forhånds-projisert til SVG-stier av generate_municipality_paths.py.
Koordinatene dekodes fra delta-kodet TopoJSON og projiseres til et 760×900 px referanserom med Web Mercator:
def project(lon, lat):
x = (lon - LON_MIN) / (LON_MAX - LON_MIN) * REF_W
y = (1 - (merc_y(lat) - Y_MIN) / (Y_MAX - Y_MIN)) * REF_H
return round(x), round(y)
fetch_data.py slår sammen SVG-stier, kommunenavn og SSB-priser til én CSV som pushes til plottet:
| Kolonne | Eksempel |
|---|---|
name |
Oslo |
kommunenummer |
0301 |
pris_kr_m2 |
72 850 |
svg_path |
M180 777L181 776…Z |
Rendering (D3.js)
Siden SVG-stiene allerede er projisert, skaleres de bare til tilgjengelig plass i nettleseren — ingen re-projeksjon nødvendig:
const scale = Math.min(plotW / REF_W, plotH / REF_H);
const mapG = svg.append("g")
.attr("transform", `translate(${offsetX},${offsetY}) scale(${scale})`);
mapG.selectAll("path")
.data(pathData)
.join("path")
.attr("d", d => d[2]) // SVG-sti direkte fra CSV
.attr("fill", d => colorScale(priceMap.get(d[0])));
Fargene bruker kvadratrot-normalisering for å gi bedre kontrast i det vanlige prisintervallet:
function colorScale(price) {
const t = (Math.sqrt(price) - sqrtMin) / (sqrtMax - sqrtMin);
return interp(Math.max(0.08, Math.min(1, t)));
}
Klikk på en kommune beregner midtpunktet til <path>-elementet og animerer zoom-utsnittet dit:
.on("click", function(event, d) {
const bbox = this.getBBox();
const cx = bbox.x + bbox.width / 2;
const cy = bbox.y + bbox.height / 2;
zoomG.transition().duration(450)
.attr("transform", `translate(${ZOOM_SIZE/2 - cx * ZOOM_DEFAULT},
${ZOOM_SIZE/2 - cy * ZOOM_DEFAULT})
scale(${ZOOM_DEFAULT})`);
});
Tilpasning via CSS
Farger og størrelser styres av CSS-variabler i custom.css — ingen endringer i JS nødvendig:
--color-scheme-light: blues; /* blues, plasma, viridis, inferno … */
--color-scheme-dark: plasma;
--zoom-level: 3;
--bg-light: #ffffff;
--bg-dark: #0d1117;
Datakilder: SSB PxWebAPI (tabell 06035) · SSB KLASS (klassifikasjon 131) · Kartverket/GeoNorge via AnalyseABO