/** * Proxies active NWS severe weather alerts for Texas to the frontend. * No auth required — api.weather.gov is a free public endpoint. * * For historical data beyond 7 days, integrate NOAA Storm Events CSV API * or SWDI (https://www.ncei.noaa.gov/pub/data/swdi/) in a future iteration. * * Response: array of normalised storm event objects (same shape as MOCK_STORM_EVENTS) */ const NWS_HEADERS = { 'User-Agent': 'PlanoRealtyCRM/1.0 (admin@plano-realty.com)', Accept: 'application/geo+json', }; function severityFromSize(inches) { if (!inches || inches < 0.75) return 'none'; if (inches < 1.00) return 'trace'; if (inches < 1.50) return 'moderate'; if (inches < 2.00) return 'significant'; return 'severe'; } function parseHailSize(text = '') { const m = text.match(/(\d+\.?\d*)\s*inch/i) || text.match(/(\d+\.?\d*)"/) ; return m ? parseFloat(m[1]) : null; } export default async function handler(req, res) { res.setHeader('Cache-Control', 's-maxage=3600, stale-while-revalidate=86400'); res.setHeader('Access-Control-Allow-Origin', '*'); if (req.method !== 'GET') { return res.status(405).json({ error: 'Method not allowed' }); } try { // NWS active severe weather alerts for TX — hail + thunderstorm const url = 'https://api.weather.gov/alerts/active?area=TX&event=Hail,Severe+Thunderstorm,Tornado'; const nwsRes = await fetch(url, { headers: NWS_HEADERS }); if (!nwsRes.ok) { console.warn('[storm-events] NWS returned', nwsRes.status); return res.status(200).json([]); } const geojson = await nwsRes.json(); const features = (geojson.features || []); const events = features .filter(f => { const evt = (f.properties?.event || '').toLowerCase(); return evt.includes('hail') || evt.includes('thunderstorm') || evt.includes('tornado'); }) .map((f, i) => { const p = f.properties || {}; const desc = p.description || p.headline || ''; const hailSize = parseHailSize(desc) ?? parseHailSize(p.parameters?.hailSize?.[0] ?? ''); // NWS GeoJSON polygon coordinates are [lng, lat] — swap for Leaflet let polygon = []; const geom = f.geometry; if (geom?.type === 'Polygon' && geom.coordinates?.[0]) { polygon = geom.coordinates[0].map(([lng, lat]) => [lat, lng]); } else if (geom?.type === 'MultiPolygon' && geom.coordinates?.[0]?.[0]) { polygon = geom.coordinates[0][0].map(([lng, lat]) => [lat, lng]); } const typeStr = (p.event || '').toLowerCase(); const type = typeStr.includes('tornado') ? 'tornado' : typeStr.includes('hail') ? 'hail' : 'wind'; return { id: `NWS-${p.id || i}`, date: p.onset || p.sent || new Date().toISOString(), type, severity: severityFromSize(hailSize), maxHailSize: hailSize, areaName: p.areaDesc || 'Unknown Area', county: 'Collin', estimatedHomes: null, description: p.headline || desc.slice(0, 300), source: 'NWS', polygon, }; }) .filter(e => e.polygon.length >= 3); // require a valid polygon return res.status(200).json(events); } catch (err) { console.error('[storm-events] error:', err.message); return res.status(200).json([]); } }