// Builds the FactHub dataset "king-county-homeless-count-beds": King County PIT counts and // HUD Housing Inventory Count beds by type, plus cross-CoC capacity comparisons. // Inputs: ../../data/hic/hic_panel.csv. Outputs (../out/): king_count_beds.csv, count_beds_content.json // Usage: node build_count_beds.js const fs = require('fs'); const path = require('path'); const crypto = require('crypto'); const ROOT = path.join(__dirname, '..', '..'); const OUT = path.join(__dirname, '..', 'out'); const NOW = '2026-10-10T21:30:00Z'; const KEY = 'king-county-homeless-count-beds'; function parseCsv(text) { const rows = []; for (const line of text.split(/\r?\n/)) { if (!line) continue; const cells = []; let cur = '', q = false; for (let i = 0; i < line.length; i++) { const c = line[i]; if (q) { if (c === '"') { if (line[i + 1] === '"') { cur += '"'; i++; } else q = false; } else cur += c; } else if (c === '"') q = true; else if (c === ',') { cells.push(cur); cur = ''; } else cur += c; } cells.push(cur); rows.push(cells); } const [h, ...body] = rows; return body.map(r => Object.fromEntries(h.map((k, i) => [k, r[i]]))); } const num = v => (v === '' || v == null ? null : +v); const r1 = v => (v == null ? null : Math.round(v * 10) / 10); const r2 = v => (v == null ? null : Math.round(v * 100) / 100); const hic = parseCsv(fs.readFileSync(path.join(ROOT, 'data/hic/hic_panel.csv'), 'utf8')); const king = hic.filter(r => r.coc_code === 'WA-500').sort((a, b) => a.year - b.year); const sheltOnly = t => /Sheltered-Only/i.test(t); // King County derived table, 2012-2025 from HUD, plus KCRHA's 2026 initial release (counts only). const kRows = king.map(r => ({ year: +r.year, source: 'HUD', count_type: r.pit_count_type, pit_total: num(r.pit_total), pit_sheltered: num(r.pit_sheltered), pit_unsheltered: num(r.pit_unsheltered), unsheltered_carried_forward: sheltOnly(r.pit_count_type) ? 1 : 0, es_beds: num(r.es_beds), th_beds: num(r.th_beds), sh_beds: num(r.sh_beds), rrh_beds: num(r.rrh_beds), psh_beds: num(r.psh_beds), oph_beds: num(r.oph_beds), temp_beds: num(r.temp_beds), perm_beds: num(r.perm_beds), temp_beds_per_homeless: sheltOnly(r.pit_count_type) ? null : num(r.temp_beds_per_homeless), })); kRows.push({ year: 2026, source: 'KCRHA initial release', count_type: 'Sheltered and Unsheltered Count', pit_total: 18365, pit_sheltered: 6536, pit_unsheltered: 11829, unsheltered_carried_forward: 0, es_beds: null, th_beds: null, sh_beds: null, rrh_beds: null, psh_beds: null, oph_beds: null, temp_beds: null, perm_beds: null, temp_beds_per_homeless: null }); const cols = Object.keys(kRows[0]); fs.writeFileSync(path.join(OUT, 'king_count_beds.csv'), cols.join(',') + '\n' + kRows.map(r => cols.map(c => r[c] == null ? '' : (/,/.test(r[c]) ? `"${r[c]}"` : r[c])).join(',')).join('\n') + '\n'); // Cross-CoC 2024 capacity ranks (full-count CoCs for per-homeless; all 67 for per-resident). const y = (yr) => hic.filter(r => +r.year === yr); function rankOf(rows, field, desc) { const v = rows.filter(r => r[field] !== '').sort((a, b) => desc ? +b[field] - +a[field] : +a[field] - +b[field]); const vals = v.map(r => +r[field]).sort((a, b) => a - b); const med = vals.length % 2 ? vals[(vals.length - 1) / 2] : (vals[vals.length / 2 - 1] + vals[vals.length / 2]) / 2; const k = v.find(r => r.coc_code === 'WA-500'); return { value: r2(+k[field]), rank: v.indexOf(k) + 1, of: v.length, median: r2(med) }; } const full24 = y(2024).filter(r => !sheltOnly(r.pit_count_type)); const ranks = [ { measure: 'Temporary beds (ES+TH+SH) per 10,000 residents, 2024', ...rankOf(y(2024), 'temp_beds_per_10k', true), rankDirection: '1 = most beds', scope: 'All 67 CoCs' }, { measure: 'Temporary beds per person counted homeless, 2024', ...rankOf(full24, 'temp_beds_per_homeless', true), rankDirection: '1 = most beds', scope: 'CoCs with a full 2024 count' }, { measure: 'Unsheltered share of people counted, 2024', ...rankOf(full24.map(r => ({ ...r, us: String(+r.unsheltered_share * 100) })), 'us', true), rankDirection: '1 = highest share', scope: 'CoCs with a full 2024 count' }, { measure: 'Permanent beds (RRH+PSH+OPH) per 10,000 residents, 2024', ...rankOf(y(2024), 'perm_beds_per_10k', true), rankDirection: '1 = most beds', scope: 'All 67 CoCs' }, ]; // Permanent beds per 10k, 2015 vs 2024, selected CoCs and the median. const pick = ['WA-500', 'NY-600', 'MA-500', 'TX-700', 'CA-601', 'CO-503', 'TX-600', 'CA-600', 'CA-501', 'OR-501']; const p15 = Object.fromEntries(y(2015).map(r => [r.coc_code, r])), p24 = Object.fromEntries(y(2024).map(r => [r.coc_code, r])); const permRows = pick.map(c => ({ coc: c, name: p24[c].short_name, perm2015: r1(+p15[c].perm_beds_per_10k), perm2024: r1(+p24[c].perm_beds_per_10k), change: r1(+p24[c].perm_beds_per_10k - +p15[c].perm_beds_per_10k), temp2015: r1(+p15[c].temp_beds_per_10k), temp2024: r1(+p24[c].temp_beds_per_10k) })); const ch = y(2024).map(r => +r.perm_beds_per_10k - +(p15[r.coc_code] || {}).perm_beds_per_10k).filter(v => !isNaN(v)).sort((a, b) => a - b); permRows.push({ coc: 'median', name: 'Median of 67 CoCs', perm2015: 16.9, perm2024: 27.9, change: r1(ch[(ch.length - 1) / 2] ?? (ch[ch.length / 2 - 1] + ch[ch.length / 2]) / 2), temp2015: 14.5, temp2024: 15.0 }); const k15 = kRows.find(r => r.year === 2015), k24 = kRows.find(r => r.year === 2024); const chg = (label, a, b) => ({ bedType: label, beds2015: a, beds2024: b, change: b - a, pctChange: Math.round((b - a) / a * 1000) / 10 }); const capChange = [ chg('Emergency shelter (ES)', k15.es_beds, k24.es_beds), chg('Transitional housing (TH)', k15.th_beds, k24.th_beds), chg('Safe haven (SH)', k15.sh_beds, k24.sh_beds), chg('Temporary beds total (ES+TH+SH)', k15.temp_beds, k24.temp_beds), chg('Rapid rehousing (RRH)', k15.rrh_beds, k24.rrh_beds), chg('Permanent supportive housing (PSH)', k15.psh_beds, k24.psh_beds), chg('Other permanent housing (OPH)', k15.oph_beds, k24.oph_beds), chg('Permanent beds total (RRH+PSH+OPH)', k15.perm_beds, k24.perm_beds), chg('People counted homeless (PIT total)', k15.pit_total, k24.pit_total), ]; function art(id, rel, role, mediaType, description) { const buf = fs.readFileSync(path.join(ROOT, rel)); return { id, role, fileName: path.basename(rel), mediaType, byteLength: buf.length, contentDigest: 'sha256:' + crypto.createHash('sha256').update(buf).digest('hex'), description, access: 'public' }; } const artifacts = [ art('hic-panel', 'data/hic/hic_panel.csv', 'curatedTable', 'text/csv', 'HUD Housing Inventory Count year-round beds by type joined to PIT counts and ACS population, 67 large CoCs, 2012-2025.'), art('king-count-beds', 'facthub/out/king_count_beds.csv', 'derivedTable', 'text/csv', 'Seattle/King County (WA-500) PIT counts and beds by type, 2012-2025 (HUD), plus KCRHA\'s 2026 initial count.'), art('hic-build-script', 'data/hic/scripts/build_hic.js', 'transformationManifest', 'text/javascript', 'Node.js script that builds hic-panel from the HUD HIC workbook sheets (exported to CSV) and the PIT/ACS panel.'), art('hic-analyze-script', 'data/hic/scripts/analyze_hic.js', 'transformationManifest', 'text/javascript', 'Node.js script for the cross-CoC bed comparisons in the method notes.'), art('csv-lib', 'data/hic/scripts/csvlib.js', 'transformationManifest', 'text/javascript', 'CSV helper used by the HIC scripts.'), art('count-beds-script', 'facthub/scripts/build_count_beds.js', 'transformationManifest', 'text/javascript', 'Node.js script that builds king-count-beds and the saved analyses in this dataset.'), art('hic-notes', 'data/hic/findings-hic.md', 'documentation', 'text/markdown', 'Method notes, King County and cross-CoC results, and caveats for the bed-capacity data.'), ]; const sources = [ { id: 'hud-hic-by-coc', name: 'HUD, 2007-2025 Housing Inventory Count by CoC (AHAR Part 1 data)', url: 'https://www.huduser.gov/portal/sites/default/files/xls/2007-2025-HIC-Counts-by-CoC.xlsx', retrievedAt: '2026-10-10', role: 'Year-round, seasonal and overflow beds by project type for each CoC and year. Downloaded from the Internet Archive because huduser.gov blocks scripted requests; contentDigest is the SHA-256 of the downloaded workbook (2,598,803 bytes).', licenseName: 'U.S. government work (public domain)', licenseUrl: null, redistribution: 'Public domain; not rehosted here.', artifactId: null, archiveUrl: 'https://web.archive.org/web/20261010211214/https://www.huduser.gov/portal/sites/default/files/xls/2007-2025-HIC-Counts-by-CoC.xlsx', archivedAt: '2026-10-10T21:12:14Z', contentDigest: 'sha256:d4dd3a9dc84384976fa2efcde2d89081a4deeea57be43c73eb587c77ed527bc1' }, { id: 'hud-pit-by-coc', name: 'HUD, 2007-2025 Point-in-Time Counts by CoC (AHAR Part 1 data)', url: 'https://www.huduser.gov/portal/sites/default/files/xls/2007-2025-PIT-Counts-by-CoC.xlsb', retrievedAt: '2026-10-10', role: 'Total, sheltered and unsheltered counts and count type. contentDigest is the SHA-256 of the workbook downloaded from the archived copy.', licenseName: 'U.S. government work (public domain)', licenseUrl: null, redistribution: 'Public domain; not rehosted here.', artifactId: null, archiveUrl: 'https://web.archive.org/web/20260530191417/https://www.huduser.gov/portal/sites/default/files/xls/2007-2025-PIT-Counts-by-CoC.xlsb', archivedAt: '2026-05-30T19:14:17Z', contentDigest: 'sha256:8deeb058001d6ecc0dfba0587baafc5e82eb46354e724c7e83a2a75eaa86e2f3' }, { id: 'acs-1yr', name: 'U.S. Census Bureau, American Community Survey 1-year estimates, table B01003 (population)', url: 'https://data.census.gov/table/ACSDT1Y2024.B01003', retrievedAt: '2026-10-10', role: 'Resident population used for beds per 10,000 residents (via dataset homelessness-rate-vs-rent).', licenseName: 'U.S. government work (public domain)', licenseUrl: null, redistribution: 'Public domain.', artifactId: null }, { id: 'kcrha-pit-2026', name: 'KCRHA, 2026 Point-in-Time Count FAQs (initial release), June 23, 2026', url: 'https://kcrha.org/wp-content/uploads/2026/06/2026-KCRHA-PIT-FAQ-Initial-Release.pdf', retrievedAt: '2026-10-10', role: 'King County 2026 count (18,365 total; 6,536 sheltered; 11,829 unsheltered), table on p.3. HUD had not yet published 2026 data.', licenseName: 'Public agency document', licenseUrl: null, redistribution: 'Linked, not rehosted.', artifactId: null, contentDigest: 'sha256:934cbdd84765044586ca23bc4637f73bce9bbca3078bf0a8771ef5d1665513c3' }, { id: 'kcrha-pit-faq-2024', name: 'KCRHA, 2024 Point-in-Time Count FAQ v2', url: 'https://kcrha.org/wp-content/uploads/2025/04/KCRHA-2024-PIT-Count-FAQ-v2.pdf', retrievedAt: '2026-10-10', role: 'Documents the respondent-driven sampling method used since 2022.', licenseName: 'Public agency document', licenseUrl: null, redistribution: 'Linked, not rehosted.', artifactId: null, archiveUrl: 'https://web.archive.org/web/20261010190248/https://kcrha.org/wp-content/uploads/2025/04/KCRHA-2024-PIT-Count-FAQ-v2.pdf', archivedAt: '2026-10-10T19:02:48Z', contentDigest: 'sha256:fb65daba6c654fcaa753e086768ce10fdc20b0ec0ff0e9d2f5b98fa267db1e4f' }, ]; const f = (name, type, description, units, nullable, valueType, derivation, sourceIds) => ({ name, type, description, units, nullable, valueType, derivation, sourceIds }); const bedField = (name, label) => f(name, 'integer', `HUD year-round ${label} beds`, 'beds', true, 'observed', null, ['hud-hic-by-coc']); const tables = [ { id: 'hic-panel', title: 'Beds by type and homeless counts, 67 large CoCs', description: 'HUD Housing Inventory Count beds by project type for each CoC and year, joined to the PIT count and population.', artifactId: 'hic-panel', unitOfObservation: 'CoC-year', fields: [ f('coc_code', 'string', 'HUD CoC code', null, false, 'observed', null, ['hud-hic-by-coc']), f('coc_name', 'string', 'CoC name as HUD listed it', null, false, 'observed', null, ['hud-hic-by-coc']), f('short_name', 'string', 'Short display name', null, false, 'derived', 'Assigned by the curator', []), f('hud_category_2025', 'string', 'HUD 2025 CoC category', null, true, 'observed', null, ['hud-pit-by-coc']), f('year', 'integer', 'HIC/PIT year (January count night)', null, false, 'observed', null, ['hud-hic-by-coc']), bedField('es_beds', 'emergency shelter'), bedField('th_beds', 'transitional housing'), bedField('sh_beds', 'safe haven'), bedField('rrh_beds', 'rapid re-housing (2014-2016 includes RRH demonstration projects; blank in 2012)'), bedField('psh_beds', 'permanent supportive housing'), bedField('oph_beds', 'other permanent housing (blank in 2012-2013)'), f('es_seasonal_beds', 'integer', 'Seasonal emergency shelter beds (not in es_beds)', 'beds', true, 'observed', null, ['hud-hic-by-coc']), f('es_overflow_beds', 'integer', 'Overflow/voucher emergency shelter beds (not in es_beds)', 'beds', true, 'observed', null, ['hud-hic-by-coc']), f('temp_beds', 'integer', 'ES + TH + SH year-round beds', 'beds', true, 'derived', 'es_beds + th_beds + sh_beds', ['hud-hic-by-coc']), f('perm_beds', 'integer', 'RRH + PSH + OPH beds (only types reported that year)', 'beds', true, 'derived', 'rrh_beds + psh_beds + oph_beds', ['hud-hic-by-coc']), f('pit_count_type', 'string', 'HUD PIT count type', null, false, 'observed', null, ['hud-pit-by-coc']), f('pit_total', 'integer', 'People counted homeless', 'people', false, 'observed', null, ['hud-pit-by-coc']), f('pit_sheltered', 'integer', 'People counted in shelter', 'people', false, 'observed', null, ['hud-pit-by-coc']), f('pit_unsheltered', 'integer', 'People counted unsheltered (carried forward in sheltered-only years)', 'people', false, 'observed', null, ['hud-pit-by-coc']), f('population', 'integer', 'Resident population (ACS B01003); blank 2012-2014 and 2020', 'people', true, 'observed', null, ['acs-1yr']), f('temp_beds_per_homeless', 'number', 'temp_beds / pit_total', 'beds per person', true, 'derived', 'temp_beds / pit_total', ['hud-hic-by-coc', 'hud-pit-by-coc']), f('unsheltered_share', 'number', 'pit_unsheltered / pit_total; blank in sheltered-only years', 'fraction', true, 'derived', null, ['hud-pit-by-coc']), f('temp_beds_per_10k', 'number', 'Temporary beds per 10,000 residents', 'beds per 10,000 residents', true, 'derived', 'temp_beds / population * 10,000', ['hud-hic-by-coc', 'acs-1yr']), f('perm_beds_per_10k', 'number', 'Permanent beds per 10,000 residents', 'beds per 10,000 residents', true, 'derived', 'perm_beds / population * 10,000', ['hud-hic-by-coc', 'acs-1yr']), ] }, { id: 'king-count-beds', title: 'Seattle/King County homeless count and beds by type, 2012-2026', description: 'King County (WA-500) rows from hic-panel, plus KCRHA\'s initial 2026 count (no 2026 bed data).', artifactId: 'king-count-beds', unitOfObservation: 'Year', fields: [ f('year', 'integer', 'Count year', null, false, 'observed', null, ['hud-pit-by-coc']), f('source', 'string', 'HUD, or KCRHA initial release for 2026', null, false, 'derived', null, []), f('count_type', 'string', 'HUD PIT count type', null, false, 'observed', null, ['hud-pit-by-coc', 'kcrha-pit-2026']), f('pit_total', 'integer', 'People counted homeless', 'people', false, 'observed', null, ['hud-pit-by-coc', 'kcrha-pit-2026']), f('pit_sheltered', 'integer', 'People counted in shelter', 'people', false, 'observed', null, ['hud-pit-by-coc', 'kcrha-pit-2026']), f('pit_unsheltered', 'integer', 'People counted unsheltered', 'people', false, 'observed', null, ['hud-pit-by-coc', 'kcrha-pit-2026']), f('unsheltered_carried_forward', 'integer', '1 if the unsheltered figure is not from a new count that year: HUD carried it forward from the previous count (2014, 2023, 2025) or recorded 0 because no unsheltered count was done (2021)', null, false, 'derived', 'From count_type', ['hud-pit-by-coc']), bedField('es_beds', 'emergency shelter'), bedField('th_beds', 'transitional housing'), bedField('sh_beds', 'safe haven'), bedField('rrh_beds', 'rapid re-housing'), bedField('psh_beds', 'permanent supportive housing'), bedField('oph_beds', 'other permanent housing'), f('temp_beds', 'integer', 'ES + TH + SH', 'beds', true, 'derived', null, ['hud-hic-by-coc']), f('perm_beds', 'integer', 'RRH + PSH + OPH', 'beds', true, 'derived', null, ['hud-hic-by-coc']), f('temp_beds_per_homeless', 'number', 'temp_beds / pit_total; blank in sheltered-only years', 'beds per person', true, 'derived', null, ['hud-hic-by-coc', 'hud-pit-by-coc']), ] }, ]; const countRows = kRows.map(r => ({ year: r.year, total: r.pit_total, sheltered: r.pit_sheltered, unsheltered: r.pit_unsheltered, unshelteredCarriedForward: !!r.unsheltered_carried_forward, source: r.source })); const bedLong = []; for (const r of kRows.filter(r => r.temp_beds != null)) for (const [k, lbl] of [['temp_beds', 'Temporary beds (ES+TH+SH)'], ['perm_beds', 'Permanent beds (RRH+PSH+OPH)'], ['pit_total', 'People counted homeless']]) bedLong.push({ year: r.year, series: lbl, value: r[k] }); const commonLim = ['HIC counts beds that exist on the count night, not whether they were staffed or occupied.', 'King County switched its unsheltered count to respondent-driven sampling in 2022; counts before and after are not directly comparable.']; const analyses = [ { id: 'king-count-series', title: 'Seattle/King County homeless count, sheltered vs. unsheltered, 2012-2026', question: 'How did the number of people counted homeless in King County, sheltered and unsheltered, change from 2012 to 2026?', operation: 'Extract WA-500 rows; append KCRHA 2026 initial release', parameters: { coc: 'WA-500' }, scope: 'King County, 2012-2026', resultShape: 'timeSeries', structuredResult: { rows: countRows }, displayResult: 'Total 8,899 (2012) to 16,868 (2024) and 18,365 (2026, KCRHA initial). Sheltered stayed between about 5,700 and 7,100 in full-count years; unsheltered rose from 2,618 to 9,810 (2024) and 11,829 (2026).', methodologicalNotes: ['2014, 2023 and 2025 HUD figures carry the unsheltered count forward from the previous count; 2021 had no unsheltered count.', 'Count method breaks: 2015 count area expanded; 2017 Count Us In canvassed all census tracts; 2022 switched to respondent-driven sampling; 2026 added survey hub sites.'], limitations: [...commonLim, 'The 2026 figure is KCRHA\'s initial release; HUD had not published it as of retrieval.', 'Point-in-time counts are single nights in late January.'], executedAt: NOW }, { id: 'king-beds-vs-count', title: 'King County temporary and permanent beds vs. people counted homeless, 2012-2025', question: 'Did King County\'s shelter and housing capacity keep pace with the number of people counted homeless?', operation: 'Extract WA-500 temp_beds, perm_beds and pit_total by year', parameters: { coc: 'WA-500' }, scope: 'King County, 2012-2025', resultShape: 'timeSeries', structuredResult: { rows: bedLong }, displayResult: 'Temporary beds stayed between 6,038 and 7,746 from 2012 to 2025 (6,452 in 2015, 6,991 in 2024). Permanent beds rose from 7,421 (2015) to 11,263 (2024). People counted homeless rose from 10,122 to 16,868. Temporary beds per person counted fell from 0.64 to 0.41.', methodologicalNotes: ['Permanent beds (RRH, PSH, OPH) house people who are no longer counted as homeless, so they are not shelter capacity for the people in the PIT count.', 'RRH and OPH were not reported in 2012-2013.'], limitations: [...commonLim, 'RRH "beds" are rental-assistance slots that swing with grant funding.', 'PSH and OPH include tenant-based vouchers, not only dedicated buildings.'], executedAt: NOW }, { id: 'king-capacity-change-2015-2024', title: 'Change in King County beds by type, 2015 to 2024', question: 'Which kinds of shelter and housing capacity grew or shrank in King County between 2015 and 2024?', operation: 'Difference and percent change, WA-500, 2015 vs 2024', parameters: { years: [2015, 2024] }, scope: 'King County', resultShape: 'table', structuredResult: { rows: capChange }, displayResult: capChange.map(r => `${r.bedType}: ${r.beds2015} to ${r.beds2024} (${r.pctChange > 0 ? '+' : ''}${r.pctChange}%)`).join('; '), methodologicalNotes: ['2015 is the first year with all bed types and a full count on the current count geography.'], limitations: [...commonLim, 'Project-type classification can shift between years (TH vs. ES, PSH vs. OPH).'], executedAt: NOW }, { id: 'king-capacity-ranks-2024', title: 'King County capacity compared with 67 large CoCs, 2024', question: 'How does King County compare with other large CoCs on shelter capacity per resident and per person counted homeless?', operation: 'Rank WA-500 among the 67 CoCs (or the 60 with a full 2024 count)', parameters: { year: 2024 }, scope: '67 large CoCs', resultShape: 'table', structuredResult: { rows: ranks }, displayResult: ranks.map(r => `${r.measure}: ${r.value}, rank ${r.rank} of ${r.of} (median ${r.median})`).join('; '), methodologicalNotes: ['Unsheltered share vs. beds per person counted is close to tautological (sheltered people occupy the beds), so this dataset reports ranks rather than that fit.'], limitations: [...commonLim, 'CoC boundaries and populations follow dataset homelessness-rate-vs-rent.', 'Right-to-shelter places (NYC, Boston) shelter people outside the year-round inventory.'], executedAt: NOW }, { id: 'perm-beds-change-selected', title: 'Permanent and temporary beds per 10,000 residents, 2015 vs. 2024, selected CoCs', question: 'How did King County\'s growth in permanent and temporary capacity per resident compare with other large CoCs?', operation: 'perm_beds_per_10k and temp_beds_per_10k, 2015 and 2024', parameters: { cocs: pick }, scope: 'Selected large CoCs and the 67-CoC median', resultShape: 'table', structuredResult: { rows: permRows }, displayResult: permRows.map(r => `${r.name}: permanent ${r.perm2015} to ${r.perm2024} (${r.change > 0 ? '+' : ''}${r.change}); temporary ${r.temp2015} to ${r.temp2024}`).join('; '), limitations: [...commonLim, 'Comparison set chosen to include the regions named in the accompanying article; the full panel is in hic-panel.'], executedAt: NOW }, ]; const content = { key: KEY, status: 'Draft', rulesVersion: '2.14', title: 'Seattle/King County homeless count and shelter and housing beds, 2012-2026', description: 'King County Point-in-Time homeless counts with HUD Housing Inventory Count beds by type, 2012-2026, and how King County\'s capacity compares with 66 other large Continuums of Care.', selectionRationale: 'Shows what shelter and housing capacity King County added while its homeless count grew, using HUD\'s own inventory, and puts King County\'s capacity in context with the same 67 large CoCs used in homelessness-rate-vs-rent.', topics: ['homelessness', 'shelter', 'supportive housing', 'Seattle', 'King County', 'Washington'], geography: ['King County, WA', 'Seattle, WA', 'United States'], temporalCoverage: '2012-2025 (HUD); 2026 count from KCRHA initial release', unitOfObservation: 'CoC by count year', updateCadence: 'Annually, after HUD releases PIT and HIC data by CoC', curator: 'FactHub', reproducibilityStatus: 'Reproducible from public sources', reproducibilityExplanation: 'Export the HUD HIC workbook sheets to CSV, run hic-build-script against them and the panel from dataset homelessness-rate-vs-rent, then run count-beds-script (Node.js).', limitations: ['HIC beds are inventory on the count night, not occupancy.', 'King County\'s count method changed in 2015, 2017, 2022 and 2026; HUD carried unsheltered counts forward in 2014, 2023 and 2025.', 'Permanent housing beds serve people who are no longer counted as homeless.', 'Rapid re-housing beds depend on grant funding and swing year to year.', 'Descriptive data; no causal claims.'], sources, artifacts, tables, events: [ { id: 'kc-area-2015', eventType: 'methodChange', date: '2015-01-22', label: 'Count area expanded', description: 'King County\'s count added southwest King County and Vashon Island.', sourceIds: ['hud-pit-by-coc'] }, { id: 'kc-count-us-in-2017', eventType: 'methodChange', date: '2017-01-26', label: 'Count Us In canvasses all census tracts', description: 'Volunteers canvassed every census tract with a survey-based multiplier, replacing the One Night Count of known areas.', sourceIds: ['hud-pit-by-coc'] }, { id: 'kc-no-unsheltered-2021', eventType: 'dataGap', date: '2021-01-28', label: 'No unsheltered count (COVID-19)', description: 'King County conducted a sheltered-only count in 2021.', sourceIds: ['hud-pit-by-coc'] }, { id: 'kc-rds-2022', eventType: 'methodChange', date: '2022-03-01', label: 'Respondent-driven sampling', description: 'KCRHA estimated the unsheltered population with respondent-driven sampling instead of a visual count.', sourceIds: ['kcrha-pit-faq-2024'] }, { id: 'kc-hubs-2026', eventType: 'methodChange', date: '2026-01-26', label: 'More survey hub sites', description: 'The 2026 count used more survey hub sites than 2024.', sourceIds: ['kcrha-pit-2026'] }, ], derivedFrom: [{ nodeHandle: 'dataset:homelessness-rate-vs-rent:1', contentDigest: 'sha256:ffda4fdcf8d8d3a1a736d70bfdc991598150be8d9f146043b686d6b5d03fe01c' }], analyses, changelog: [{ date: '2026-10-10', summary: 'Initial version.' }], }; fs.writeFileSync(path.join(OUT, 'count_beds_content.json'), JSON.stringify(content)); console.log('bytes', JSON.stringify(content).length); console.log(JSON.stringify(ranks, null, 0)); console.log(JSON.stringify(capChange)); console.log(JSON.stringify(permRows));