| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| import type { AssetKind, RegisterData, RegisterRow } from '$lib/types/states'; |
|
|
| interface BaseFinding { |
| [k: string]: unknown; |
| } |
|
|
| const SOURCE_LABEL: Record<AssetKind, string> = { |
| subway: 'MTA · USGS · FEMA · NYC OEM · NYC DEP', |
| nycha: 'NYC HA · USGS · NYC OEM · NYC DEP', |
| school: 'NYC DOE · USGS · NYC OEM · NYC DEP', |
| hospital: 'NYS DOH · USGS · NYC OEM · NYC DEP' |
| }; |
|
|
| const TYPE_LABEL: Record<AssetKind, string> = { |
| subway: 'subway entrances', |
| nycha: 'NYCHA developments', |
| school: 'public schools', |
| hospital: 'hospitals' |
| }; |
|
|
| function metersToLabel(m: number | undefined): string { |
| if (!m || !Number.isFinite(m)) return '—'; |
| return `${Math.round(m)}m`; |
| } |
|
|
| function feetLabel(elev_m: number | null | undefined): string { |
| if (elev_m == null || !Number.isFinite(elev_m)) return '—'; |
| return `${(elev_m * 3.28084).toFixed(1)} ft`; |
| } |
|
|
| function inundLabel(inside: boolean | undefined, pct?: number | null | undefined): string { |
| if (typeof pct === 'number') { |
| if (pct >= 0.5) return `Inundated 2012 (${Math.round(pct * 100)}%)`; |
| if (pct > 0) return `Edge (${Math.round(pct * 100)}%)`; |
| return '—'; |
| } |
| return inside ? 'Inundated 2012' : '—'; |
| } |
|
|
| function depLabel(label: string | null | undefined, classNum?: number | null | undefined, |
| pct?: number | null | undefined): string { |
| if (typeof pct === 'number') { |
| if (pct >= 0.5) return `≥${Math.round(pct * 100)}% in scenario`; |
| if (pct > 0) return `${Math.round(pct * 100)}% edge`; |
| return 'minimal'; |
| } |
| if (label && label.length) return label; |
| if (classNum && classNum > 0) return `class ${classNum}`; |
| return 'minimal'; |
| } |
|
|
| function adaFromEntranceType(t: string | undefined): boolean { |
| if (!t) return false; |
| |
| return /elevator|easement|stair.*ramp/i.test(t); |
| } |
|
|
| |
|
|
| function adaptSubway(s: BaseFinding): RegisterData | null { |
| if (!s.available) return null; |
| const list = (s.entrances ?? []) as BaseFinding[]; |
| const rows: RegisterRow[] = list.map((e) => { |
| const ada = adaFromEntranceType(e.entrance_type as string | undefined); |
| return { |
| name: `${e.station_name ?? '?'}${e.daytime_routes ? ` (${String(e.daytime_routes).split(/\s+/).slice(0, 3).join('/')})` : ''}`, |
| elev: feetLabel(e.elev_m as number | null | undefined), |
| ada, |
| fema: 'Zone X', |
| sandy: inundLabel(e.inside_sandy_2012 as boolean | undefined), |
| dep: depLabel( |
| e.dep_extreme_2080_label as string | null | undefined, |
| e.dep_extreme_2080_class as number | null | undefined |
| ), |
| asset: 'subway', |
| primaryTier: e.inside_sandy_2012 ? 'empirical' : 'modeled' |
| }; |
| }); |
| return { |
| type: TYPE_LABEL.subway, |
| radius: metersToLabel(s.radius_m as number | undefined), |
| count: (s.n_entrances as number | undefined) ?? rows.length, |
| rows, |
| sourceLabel: SOURCE_LABEL.subway |
| }; |
| } |
|
|
| function adaptNycha(s: BaseFinding): RegisterData | null { |
| if (!s.available) return null; |
| const list = (s.developments ?? []) as BaseFinding[]; |
| const rows: RegisterRow[] = list.map((d) => { |
| const inSandy = d.inside_sandy_2012 as boolean | undefined; |
| const depLbl = d.dep_extreme_2080_label as string | null | undefined; |
| const depCls = d.dep_extreme_2080_class as number | null | undefined; |
| return { |
| name: `${d.development ?? '?'}${d.borough ? ` · ${d.borough}` : ''}`, |
| elev: feetLabel(d.rep_elevation_m as number | null | undefined), |
| ada: false, |
| fema: '—', |
| sandy: inundLabel(inSandy), |
| dep: depLabel(depLbl, depCls), |
| asset: 'nycha', |
| primaryTier: inSandy ? 'empirical' : 'modeled' |
| }; |
| }); |
| return { |
| type: TYPE_LABEL.nycha, |
| radius: metersToLabel(s.radius_m as number | undefined), |
| count: (s.n_developments as number | undefined) ?? rows.length, |
| rows, |
| sourceLabel: SOURCE_LABEL.nycha |
| }; |
| } |
|
|
| function adaptSchools(s: BaseFinding): RegisterData | null { |
| if (!s.available) return null; |
| const list = (s.schools ?? []) as BaseFinding[]; |
| const rows: RegisterRow[] = list.map((sc) => ({ |
| name: `${sc.loc_name ?? sc.school_name ?? sc.name ?? '?'}${sc.borough ? ` · ${sc.borough}` : ''}`, |
| elev: feetLabel((sc.elevation_m ?? sc.elev_m) as number | null | undefined), |
| ada: false, |
| fema: '—', |
| sandy: inundLabel(sc.inside_sandy_2012 as boolean | undefined), |
| dep: depLabel( |
| sc.dep_extreme_2080_label as string | null | undefined, |
| sc.dep_extreme_2080_class as number | null | undefined |
| ), |
| asset: 'school', |
| primaryTier: sc.inside_sandy_2012 ? 'empirical' : 'modeled' |
| })); |
| return { |
| type: TYPE_LABEL.school, |
| radius: metersToLabel(s.radius_m as number | undefined), |
| count: (s.n_schools as number | undefined) ?? rows.length, |
| rows, |
| sourceLabel: SOURCE_LABEL.school |
| }; |
| } |
|
|
| function adaptHospitals(s: BaseFinding): RegisterData | null { |
| if (!s.available) return null; |
| const list = (s.hospitals ?? []) as BaseFinding[]; |
| const rows: RegisterRow[] = list.map((h) => ({ |
| name: `${h.facility_name ?? h.name ?? '?'}${h.borough ? ` · ${h.borough}` : ''}`, |
| elev: feetLabel((h.elevation_m ?? h.elev_m) as number | null | undefined), |
| ada: true, |
| fema: '—', |
| sandy: inundLabel(h.inside_sandy_2012 as boolean | undefined), |
| dep: depLabel( |
| h.dep_extreme_2080_label as string | null | undefined, |
| h.dep_extreme_2080_class as number | null | undefined |
| ), |
| asset: 'hospital', |
| primaryTier: h.inside_sandy_2012 ? 'empirical' : 'modeled' |
| })); |
| return { |
| type: TYPE_LABEL.hospital, |
| radius: metersToLabel(s.radius_m as number | undefined), |
| count: (s.n_hospitals as number | undefined) ?? rows.length, |
| rows, |
| sourceLabel: SOURCE_LABEL.hospital |
| }; |
| } |
|
|
| |
| |
| |
| |
| |
| export function extractRegisters(final: Record<string, unknown> | null): RegisterData[] { |
| if (!final) return []; |
| const out: RegisterData[] = []; |
| const mta = adaptSubway(final.mta_entrances as BaseFinding | null ?? {}); |
| if (mta && mta.rows.length) out.push(mta); |
| const nycha = adaptNycha(final.nycha_developments as BaseFinding | null ?? {}); |
| if (nycha && nycha.rows.length) out.push(nycha); |
| const schools = adaptSchools(final.doe_schools as BaseFinding | null ?? {}); |
| if (schools && schools.rows.length) out.push(schools); |
| const hospitals = adaptHospitals(final.doh_hospitals as BaseFinding | null ?? {}); |
| if (hospitals && hospitals.rows.length) out.push(hospitals); |
| return out; |
| } |
|
|