forskjeller.naiv.no/public/boutgifter/app.js

262 lines
8.5 KiB
JavaScript
Raw Normal View History

// SSB data: Boutgifter og boligeierskap etter inntekt
// Tabell 14061: Boligøkonomi etter inntektsgruppe (boutgifter som andel av inntekt)
// Tabell 14067: Boligeierskap etter inntektsgruppe
// Hentet 2026-03-16 via SSB PxWeb API v2
// --- Data ---
const YEARS = ['2015', '2016', '2017', '2018', '2019', '2020', '2021', '2022', '2023', '2024'];
// Boutgifter som andel av inntekt per kvartil, 2024 (tabell 14061)
const QUARTILE_LABELS = ['Laveste kvartil', 'Andre kvartil', 'Tredje kvartil', 'Høyeste kvartil'];
const HOUSING_BURDEN = {
under10: [ 5.2, 10.8, 16.6, 22.0],
from10to19: [12.4, 26.1, 22.8, 24.6],
from20to29: [19.3, 28.2, 29.0, 28.2],
from30to39: [22.4, 19.5, 18.9, 17.7],
from40to59: [23.6, 12.7, 11.5, 6.4],
over60: [17.1, 2.8, 1.2, 1.0]
};
// Andel med boutgifter >40% av inntekt (40-59% + 60%+) over tid (tabell 14061)
const HEAVY_BURDEN = {
q1: [33.6, 33.4, 32.9, 34.5, 33.8, 33.5, 35.6, 41.8, 42.9, 40.7],
q2: [ 7.3, 7.0, 7.0, 6.3, 8.3, 6.0, 7.6, 12.3, 12.5, 15.5],
q3: [ 4.2, 3.1, 4.2, 3.9, 3.2, 3.6, 3.6, 6.0, 9.3, 12.7],
q4: [ 2.5, 1.2, 1.9, 2.5, 2.5, 3.5, 2.5, 4.6, 5.9, 7.4]
};
// Eierskap etter inntektsgruppe og utsatte grupper, 2024 (tabell 14067)
const OWNERSHIP_GROUPS = [
{ name: 'Sosialhjelpsmottakere', selveier: 13.9, andel: 4.6, leier: 81.5 },
{ name: 'Lavinntekt (EU 60 %)', selveier: 21.2, andel: 10.8, leier: 68.0 },
{ name: 'Laveste kvartil', selveier: 33.2, andel: 12.6, leier: 54.3 },
{ name: 'Barnefamilier lav inntekt', selveier: 39.0, andel: 8.5, leier: 52.5 },
{ name: 'Aleneboende eldre lav innt.', selveier: 52.4, andel: 23.9, leier: 23.7 },
{ name: 'Uføretrygdede', selveier: 55.6, andel: 13.7, leier: 30.7 },
{ name: 'Andre kvartil', selveier: 60.3, andel: 18.2, leier: 21.5 },
{ name: 'Tredje kvartil', selveier: 70.2, andel: 15.6, leier: 14.2 },
{ name: 'Høyeste kvartil', selveier: 79.7, andel: 13.6, leier: 6.7 },
];
// --- Formattering ---
function fmtPct(n) {
return n.toLocaleString('nb-NO', { minimumFractionDigits: 1, maximumFractionDigits: 1 }) + '\u202f%';
}
const GRID_COLOR = 'rgba(0,0,0,0.06)';
const TICK_COLOR = '#8a857e';
function baseOpts() {
return {
responsive: true, maintainAspectRatio: false,
plugins: {
legend: { display: false },
tooltip: {
backgroundColor: '#1a1714',
titleColor: '#f5f2eb',
bodyColor: 'rgba(245,242,235,0.7)',
padding: 10,
cornerRadius: 4,
titleFont: { family: 'DM Sans', size: 12 },
bodyFont: { family: 'DM Sans', size: 12 }
}
},
scales: {
x: {
ticks: { color: TICK_COLOR, font: { size: 11, family: 'DM Sans' }, maxRotation: 0 },
grid: { display: false },
border: { color: 'rgba(0,0,0,0.1)' }
},
y: {
ticks: { color: TICK_COLOR, font: { size: 11, family: 'DM Sans' }, callback: v => v + ' %' },
grid: { color: GRID_COLOR },
border: { dash: [3, 3], color: 'transparent' }
}
}
};
}
// --- Diagram 1: Boutgifter som andel av inntekt (stablet horisontal) ---
var chart1Opts = baseOpts();
chart1Opts.indexAxis = 'y';
chart1Opts.scales = {
x: {
stacked: true,
max: 100,
ticks: { color: TICK_COLOR, font: { size: 11, family: 'DM Sans' }, callback: v => v + ' %' },
grid: { color: GRID_COLOR },
border: { dash: [3, 3], color: 'transparent' }
},
y: {
stacked: true,
ticks: { color: TICK_COLOR, font: { size: 11, family: 'DM Sans' }, autoSkip: false },
grid: { display: false },
border: { color: 'rgba(0,0,0,0.1)' }
}
};
chart1Opts.plugins.tooltip = {
mode: 'index',
callbacks: {
label: function(c) {
return ' ' + c.dataset.label + ': ' + fmtPct(c.parsed.x);
}
},
backgroundColor: '#1a1714',
titleColor: '#f5f2eb',
bodyColor: 'rgba(245,242,235,0.7)',
padding: 10,
cornerRadius: 4,
titleFont: { family: 'DM Sans', size: 12 },
bodyFont: { family: 'DM Sans', size: 12 }
};
new Chart(document.getElementById('chart1'), {
type: 'bar',
data: {
labels: QUARTILE_LABELS,
datasets: [
{ label: 'Under 10 %', data: HOUSING_BURDEN.under10, backgroundColor: '#2c6e49cc', borderColor: '#2c6e49', borderWidth: 1, borderRadius: 1 },
{ label: '1019 %', data: HOUSING_BURDEN.from10to19, backgroundColor: '#27ae60cc', borderColor: '#27ae60', borderWidth: 1, borderRadius: 1 },
{ label: '2029 %', data: HOUSING_BURDEN.from20to29, backgroundColor: '#e67e22cc', borderColor: '#e67e22', borderWidth: 1, borderRadius: 1 },
{ label: '3039 %', data: HOUSING_BURDEN.from30to39, backgroundColor: '#e74c3ccc', borderColor: '#e74c3c', borderWidth: 1, borderRadius: 1 },
{ label: '4059 %', data: HOUSING_BURDEN.from40to59, backgroundColor: '#c0392bcc', borderColor: '#c0392b', borderWidth: 1, borderRadius: 1 },
{ label: '60 %+', data: HOUSING_BURDEN.over60, backgroundColor: '#8b1a1acc', borderColor: '#8b1a1a', borderWidth: 1, borderRadius: 1 }
]
},
options: chart1Opts
});
// --- Diagram 2: Tung boutgiftsbyrde over tid (linje) ---
function buildLegend(containerId, items) {
var container = document.getElementById(containerId);
items.forEach(function(item) {
var span = document.createElement('span');
var swatch = document.createElement('span');
swatch.className = 'swatch';
swatch.style.background = item.color;
span.appendChild(swatch);
span.appendChild(document.createTextNode(item.name));
container.appendChild(span);
});
}
var chart2Lines = [
{ name: 'Laveste kvartil', data: HEAVY_BURDEN.q1, color: '#c0392b' },
{ name: 'Andre kvartil', data: HEAVY_BURDEN.q2, color: '#e67e22' },
{ name: 'Tredje kvartil', data: HEAVY_BURDEN.q3, color: '#1a4a8a' },
{ name: 'Høyeste kvartil', data: HEAVY_BURDEN.q4, color: '#2c6e49' }
];
buildLegend('legend2', chart2Lines);
var chart2Opts = baseOpts();
chart2Opts.scales.y.max = 50;
chart2Opts.plugins.tooltip = {
callbacks: {
label: function(c) {
return ' ' + c.dataset.label + ': ' + fmtPct(c.parsed.y);
}
},
backgroundColor: '#1a1714',
titleColor: '#f5f2eb',
bodyColor: 'rgba(245,242,235,0.7)',
padding: 10,
cornerRadius: 4,
titleFont: { family: 'DM Sans', size: 12 },
bodyFont: { family: 'DM Sans', size: 12 }
};
new Chart(document.getElementById('chart2'), {
type: 'line',
data: {
labels: YEARS,
datasets: chart2Lines.map(function(line) {
return {
label: line.name,
data: line.data,
borderColor: line.color,
backgroundColor: line.color + '12',
fill: false,
tension: 0.3,
pointRadius: 0,
pointHoverRadius: 4,
borderWidth: 2.5
};
})
},
options: chart2Opts
});
// --- Diagram 3: Eierskap etter inntektsgruppe (horisontal stablet) ---
var chart3Opts = baseOpts();
chart3Opts.indexAxis = 'y';
chart3Opts.scales = {
x: {
stacked: true,
max: 100,
ticks: { color: TICK_COLOR, font: { size: 11, family: 'DM Sans' }, callback: v => v + ' %' },
grid: { color: GRID_COLOR },
border: { dash: [3, 3], color: 'transparent' }
},
y: {
stacked: true,
ticks: { color: TICK_COLOR, font: { size: 11, family: 'DM Sans' }, autoSkip: false },
grid: { display: false },
border: { color: 'rgba(0,0,0,0.1)' }
}
};
chart3Opts.plugins.tooltip = {
mode: 'index',
callbacks: {
label: function(c) {
return ' ' + c.dataset.label + ': ' + fmtPct(c.parsed.x);
}
},
backgroundColor: '#1a1714',
titleColor: '#f5f2eb',
bodyColor: 'rgba(245,242,235,0.7)',
padding: 10,
cornerRadius: 4,
titleFont: { family: 'DM Sans', size: 12 },
bodyFont: { family: 'DM Sans', size: 12 }
};
new Chart(document.getElementById('chart3'), {
type: 'bar',
data: {
labels: OWNERSHIP_GROUPS.map(function(g) { return g.name; }),
datasets: [
{
label: 'Selveier',
data: OWNERSHIP_GROUPS.map(function(g) { return g.selveier; }),
backgroundColor: '#2c6e49cc',
borderColor: '#2c6e49',
borderWidth: 1,
borderRadius: 2
},
{
label: 'Andelseier',
data: OWNERSHIP_GROUPS.map(function(g) { return g.andel; }),
backgroundColor: '#1a4a8acc',
borderColor: '#1a4a8a',
borderWidth: 1,
borderRadius: 2
},
{
label: 'Leier',
data: OWNERSHIP_GROUPS.map(function(g) { return g.leier; }),
backgroundColor: '#c0392bcc',
borderColor: '#c0392b',
borderWidth: 1,
borderRadius: 2
}
]
},
options: chart3Opts
});