diff --git a/src/hooks/useStormEvents.js b/src/hooks/useStormEvents.js index 42ce19d..49e42a4 100644 --- a/src/hooks/useStormEvents.js +++ b/src/hooks/useStormEvents.js @@ -111,9 +111,7 @@ function featureToEvent(feature, idx) { ? Math.round((new Date(expireTime) - new Date(issueTime)) / 60000) : null; - const areaSqMi = Math.round(polygonAreaSqMi(polygon)); - // Suburban DFW density ~1 800 homes/sq mi — conservative (polygons often include open land) - const estHomes = Math.round(areaSqMi * 1800); + const areaSqMi = Math.round(polygonAreaSqMi(polygon)); const type = ph === 'TO' ? 'tornado' : ph === 'FF' ? 'flood' @@ -128,6 +126,25 @@ function featureToEvent(feature, idx) { : ph === 'FF' ? 'moderate' : 'significant'; + // Damage probability: fraction of homes in the warning zone likely to have claimable damage. + // Tornado path is far narrower than the warning polygon, so we discount heavily. + // Hail probability scales with hail size — golf ball (2"+) damages nearly everything it hits. + const damagePct = + ph === 'TO' ? 20 + : ph === 'FF' ? 25 + : ph === 'WS' || ph === 'BZ' ? 30 + : ph === 'WW' || ph === 'IS' ? 20 + : hailIn === null ? 40 // SV wind only + : hailIn >= 2.0 ? 95 + : hailIn >= 1.75 ? 80 + : hailIn >= 1.5 ? 65 + : hailIn >= 1.25 ? 45 + : hailIn >= 1.0 ? 30 + : 15; // <1" — cosmetic, rarely claimed + + // Homes in zone × suburban DFW density (1 800/sq mi) × damage probability + const estHomes = Math.round(areaSqMi * 1800 * (damagePct / 100)); + return { id: `IEM-${WFO}-${ph}-${p.year ?? ''}-${p.eventid ?? idx}`, date: issueTime || new Date().toISOString(), @@ -142,6 +159,7 @@ function featureToEvent(feature, idx) { areaName: derivedAreaName(rawCoords, ph), county: 'Collin', areaSqMi, + damagePct, estimatedHomes: estHomes > 0 ? estHomes : null, description: `${PH_LABEL[ph] ?? ph} — NWS ${WFO}`, source: 'IEM/NWS', diff --git a/src/pages/StormIntelPage.jsx b/src/pages/StormIntelPage.jsx index d1a1b6f..c255da7 100644 --- a/src/pages/StormIntelPage.jsx +++ b/src/pages/StormIntelPage.jsx @@ -873,9 +873,11 @@ const StormDetailDrawer = ({ storm, open, onClose, onFlyTo, onAssignZone, canMan )} {storm.estimatedHomes > 0 && (
Est. Homes
+Est. Homes Affected
{storm.estimatedHomes.toLocaleString()}
-in warning zone
++ ~{storm.damagePct}% damage probability +
Storms
-{(totalHomes / 1000).toFixed(1)}k
-Homes
++ {pinnedHomes !== null + ? (pinnedHomes >= 1000 ? `${(pinnedHomes / 1000).toFixed(1)}k` : pinnedHomes.toLocaleString()) + : (totalHomes >= 1000 ? `${(totalHomes / 1000).toFixed(1)}k` : totalHomes.toLocaleString()) + } +
++ {pinnedHomes !== null ? `Est. Homes · ${pinnedIds.size} pinned` : 'Est. Homes'} +