fix(storm-intel): map shows only selected/pinned events; add Show all toggle + auto-zoom to pins

- Location pins filter the sidebar only; map no longer auto-renders all
  nearby polygons (NWS warnings span entire counties, cluttering the view)
- Map shows event polygons only for explicitly selected or event-pinned items
- "Show all on map" toggle in sidebar renders the full sidebar event list
- FitToPins auto-zooms to the pin radius circle when a new pin is dropped;
  clears back to Plano center when all pins are removed
This commit is contained in:
Satyam-Rastogi
2026-05-19 00:56:37 +05:30
parent 0b8229d46f
commit b9450dc7b4
+54 -8
View File
@@ -242,6 +242,35 @@ const PinDropLayer = ({ onDrop }) => {
return null; return null;
}; };
const FitToPins = ({ pins, pinRadius }) => {
const map = useMap();
const prevLen = useRef(0);
useEffect(() => {
if (pins.length === 0) {
if (prevLen.current > 0) map.flyTo(PLANO_CENTER, DEFAULT_ZOOM, { animate: true, duration: 0.6 });
prevLen.current = 0;
return;
}
// Only zoom when a pin is added (length increased)
if (pins.length <= prevLen.current) { prevLen.current = pins.length; return; }
prevLen.current = pins.length;
try {
const radiusM = pinRadius * 1609.34;
const latOffset = radiusM / 111319;
let bounds = null;
pins.forEach(([lat, lng]) => {
const lngOffset = latOffset / Math.cos(lat * Math.PI / 180);
const pb = L.latLngBounds([lat - latOffset, lng - lngOffset], [lat + latOffset, lng + lngOffset]);
bounds = bounds ? bounds.extend(pb) : pb;
});
if (bounds) map.fitBounds(bounds, { padding: [50, 50], maxZoom: 12, animate: true, duration: 0.6 });
} catch { /* ignore */ }
}, [pins, pinRadius, map]);
return null;
};
const StormMap = memo(({ const StormMap = memo(({
events, selectedId, pinnedIds, onSelect, theme, flyCount, selectedStorm, events, selectedId, pinnedIds, onSelect, theme, flyCount, selectedStorm,
stormZones, isDrawing, draftPolygon, onAddPoint, onFinishPolygon, stormZones, isDrawing, draftPolygon, onAddPoint, onFinishPolygon,
@@ -265,8 +294,9 @@ const StormMap = memo(({
/> />
<MapFlyTo storm={selectedStorm} flyCount={flyCount} /> <MapFlyTo storm={selectedStorm} flyCount={flyCount} />
<FitToPins pins={pins} pinRadius={pinRadius} />
{/* Storm polygons — only selected + pinned (or all if none active) */} {/* Storm polygons — only selected + pinned events */}
{events.map(storm => { {events.map(storm => {
const sty = SEVERITY_STYLES[storm.severity] ?? SEVERITY_STYLES.trace; const sty = SEVERITY_STYLES[storm.severity] ?? SEVERITY_STYLES.trace;
const isSelected = storm.id === selectedId; const isSelected = storm.id === selectedId;
@@ -993,6 +1023,7 @@ const StormIntelPage = () => {
const [pins, setPins] = useState([]); // array of [lat,lng] const [pins, setPins] = useState([]); // array of [lat,lng]
const [pinRadius, setPinRadius] = useState(5); // applied radius (miles) const [pinRadius, setPinRadius] = useState(5); // applied radius (miles)
const [radiusInput, setRadiusInput] = useState('5'); // draft value in input const [radiusInput, setRadiusInput] = useState('5'); // draft value in input
const [showAllOnMap, setShowAllOnMap] = useState(false);
const MAX_PINS = 5; const MAX_PINS = 5;
const applyRadius = useCallback(() => { const applyRadius = useCallback(() => {
@@ -1042,12 +1073,15 @@ const StormIntelPage = () => {
// Sidebar shows nearby events when pins exist, otherwise full filtered list // Sidebar shows nearby events when pins exist, otherwise full filtered list
const sidebarEvents = pins.length > 0 ? nearbyEvents : displayEvents; const sidebarEvents = pins.length > 0 ? nearbyEvents : displayEvents;
// Map shows: when pins exist → all nearby; else → selected+pinned only (or none until pins placed) // Map shows: if showAllOnMap → all sidebar events; else → only selected/event-pinned.
// Location pins filter the sidebar list but don't auto-render all nearby polygons —
// NWS warning polygons can be 60-80 mi across and would visually flood the map.
const mapPool = pins.length > 0 ? nearbyEvents : displayEvents;
const mapEvents = useMemo(() => { const mapEvents = useMemo(() => {
if (pins.length > 0) return nearbyEvents; if (showAllOnMap) return mapPool;
if (!selectedStorm && pinnedIds.size === 0) return []; // nothing until pin placed if (!selectedStorm && pinnedIds.size === 0) return [];
return displayEvents.filter(e => pinnedIds.has(e.id) || e.id === selectedStorm?.id); return mapPool.filter(e => pinnedIds.has(e.id) || e.id === selectedStorm?.id);
}, [displayEvents, nearbyEvents, pins, selectedStorm, pinnedIds]); }, [mapPool, showAllOnMap, selectedStorm, pinnedIds]);
const handleDropPin = useCallback((latlng) => { const handleDropPin = useCallback((latlng) => {
setPins(prev => { setPins(prev => {
@@ -1407,8 +1441,8 @@ const StormIntelPage = () => {
</div> </div>
</div> </div>
{/* Search */} {/* Search + Show all toggle */}
<div className="px-3 pt-2 pb-1.5 border-b border-zinc-100 dark:border-white/[0.06] shrink-0"> <div className="px-3 pt-2 pb-1.5 border-b border-zinc-100 dark:border-white/[0.06] shrink-0 space-y-1.5">
<div className="relative"> <div className="relative">
<Search size={12} className="absolute left-2.5 top-1/2 -translate-y-1/2 text-zinc-400 pointer-events-none" /> <Search size={12} className="absolute left-2.5 top-1/2 -translate-y-1/2 text-zinc-400 pointer-events-none" />
<input <input
@@ -1424,6 +1458,18 @@ const StormIntelPage = () => {
</button> </button>
)} )}
</div> </div>
{/* Show all on map toggle */}
<button
onClick={() => setShowAllOnMap(v => !v)}
className={`flex items-center gap-1.5 px-2.5 py-1 rounded-lg text-[11px] font-bold transition-colors w-full justify-center border ${
showAllOnMap
? 'bg-amber-500 border-amber-600 text-white'
: 'border-zinc-200 dark:border-white/[0.07] text-zinc-500 dark:text-zinc-400 hover:bg-zinc-50 dark:hover:bg-white/5'
}`}
>
<Map size={11} />
{showAllOnMap ? 'Showing all on map' : 'Show all on map'}
</button>
</div> </div>
{/* Active pins strip */} {/* Active pins strip */}