The 'dropping to bottom of screen' effect was caused by ALL markers
animating on every data refresh, not just truly new events.
Root cause:
- On first plugin enable: previousQuakeIds/previousStrikeIds is empty
- Every marker looked 'new' and animated
- On data refresh (every 5 min): all markers recreated and animated
- Result: appeared as if markers were 'dropping' or 'falling'
Solution:
- Added isFirstLoad ref flag for both plugins
- First load: populate previousIds but DON'T animate
- Subsequent loads: only animate truly NEW events
- isNew = !isFirstLoad && !previousIds.has(id)
Behavior now:
- Enable plugin: Markers appear static (no animation)
- Wait 5 min refresh: Still static (no animation)
- NEW earthquake/strike detected: ONLY that one animates
- Result: Clean, professional, no 'dropping' effect
Applies to:
- Earthquakes: Fixed
- Lightning: Fixed
Added minimize/maximize functionality to Lightning Activity panel:
- Click header or toggle button (▼/▶) to minimize/maximize
- State persists in localStorage (lightning-stats-minimized)
- Minimized state restores on page reload
- Consistent with WSPR and Gray Line plugins
All control panels now have minimize functionality:
- WSPR: Filter, Stats, Legend, Chart panels ✓
- Gray Line: Control panel ✓
- Lightning: Stats panel ✓ (NEW)
- Earthquakes: No control panels (markers only)
- Aurora: No control panels (overlay only)
- Weather Radar: No control panels (tile layer only)
The transform: scale() was causing markers to move across the screen
because Leaflet's coordinate system doesn't handle transforms well.
Changed approach for both Earthquake and Lightning plugins:
- REMOVED: transform: scale() animations
- ADDED: brightness and drop-shadow effects instead
- Earthquakes: Flash with brightness (3x → 1x) and glow
- Lightning: Flash with brightness (4x → 1x) and gold glow
- Updated timing: 0.8s for both plugins
Result:
- Markers stay in their exact location
- Visual 'explosion' effect at the point
- No movement across screen
- Clean brightness flash to highlight new events
Same issue as earthquakes - all lightning markers were continuously
animating instead of just new strikes.
Fix:
- Remove infinite animation from .lightning-strike CSS class
- All markers start with static 'lightning-strike' class
- For new strikes: add marker to map first, then animate
- Wait 10ms for DOM element, then add 'lightning-strike-new' class
- Remove animation class after 0.8s (animation duration)
- Added try/catch with console.warn for safety
- Remove duplicate marker.addTo(map) call
Result:
- New strikes: Flash animation (0.8s) + pulse ring (2s)
- After animation: Marker becomes static
- Old strikes: No animation, always static
The error 'Cannot read properties of undefined (reading classList)' was
caused by trying to access circle._path before the marker was added to
the map and rendered in the DOM.
Fix:
- Add marker to map FIRST (circle.addTo(map))
- THEN wait 10ms for DOM element to be created
- Only then access circle._path.classList
- Added try/catch with console.warn for safety
- Remove duplicate circle.addTo(map) call
This ensures the SVG path element exists before we try to animate it.
- Remove infinite pulse animation from all earthquake markers
- Animation now plays ONLY for newly detected earthquakes
- Animation class removed after 0.6s (after animation completes)
- Prevents markers from continuously moving/pulsing
- Fixes visual distraction issue
Changes:
- CSS: Removed infinite animation from .earthquake-marker class
- JS: Animation class added temporarily only for new quakes
- JS: setTimeout removes animation class after completion
- Weather Radar (wxradar): NEXRAD overlay documentation
- Earthquakes: v1.1.0 with animated new quake detection
- Aurora Forecast: OVATION model visualization guide
- Gray Line: Solar terminator propagation handbook
- Added CSS animations for earthquake pulse effects
Each README includes:
- Feature overview and capabilities
- Data sources and technical details
- Use cases and operating strategies
- Troubleshooting and best practices
- Version history and metadata
🎨 UI Improvement:
- Changed twilight opacity slider range from 10-70% to 20-100%
- Updated default value from 30% to 50%
- Allows more visibility control for twilight zones
📊 Changes:
- Min: 10% → 20% (prevents too-faint lines)
- Max: 70% → 100% (allows full opacity if desired)
- Default: 30% → 50% (better starting visibility)
✅ Benefits:
- Greater control over twilight zone visibility
- Can make twilight zones more prominent (up to 100%)
- Better default visibility at 50%
- Minimum 20% ensures lines are always visible
Version: 1.0.1 → 1.0.2
✨ New Plugin - Gray Line Propagation:
- Real-time solar terminator (day/night boundary) visualization
- Enhanced HF propagation zone highlighting (±5° from terminator)
- Three twilight zones with adjustable opacity:
* Civil Twilight (-6° sun altitude)
* Nautical Twilight (-12° sun altitude)
* Astronomical Twilight (-18° sun altitude)
- Auto-updates every minute
- Minimizable control panel
🎯 Features:
- Solar Terminator Line: Orange dashed line showing day/night boundary
- Enhanced DX Zone: Yellow shaded area ±5° from terminator (best propagation)
- Twilight Zones: Blue lines showing civil/nautical/astronomical twilight
- UTC Time Display: Current time in control panel
- Toggle Twilight Zones: Show/hide twilight lines
- Toggle Enhanced Zone: Show/hide DX propagation zone
- Twilight Opacity Slider: Adjust twilight visibility (10-70%)
🌅 Propagation Science:
- Gray line = Earth's solar terminator
- Enhanced HF propagation for several hours around terminator
- D-layer absorption reduced at terminator
- Ideal for long-distance DX contacts
- Twilight zones show progressive propagation conditions
🎨 Visual Design:
- Orange terminator line (main gray line)
- Yellow enhanced DX zone (±5° shaded area)
- Blue twilight lines (civil/nautical/astronomical)
- Interactive popups on all lines
- Color-coded by propagation potential
🔧 Technical Implementation:
- Astronomical calculations for solar position
- Julian date and solar declination algorithms
- Hour angle and solar altitude calculations
- Real-time terminator line generation (360 points)
- Twilight zone calculations at -6°, -12°, -18°
- Updates every 60 seconds automatically
- CTRL+drag to move panel
- Click header to minimize/maximize
📊 Control Panel:
- UTC time display (updates every minute)
- Show Twilight Zones checkbox
- Enhanced DX Zone checkbox
- Twilight opacity slider (10-70%)
- Info text about gray line propagation
- Minimizable with ▼/▶ toggle
💾 State Persistence:
- Panel position saved to localStorage
- Minimize state saved
- Twilight toggle state persistent
- Enhanced zone toggle persistent
- Opacity settings saved
🎯 Use Cases:
- Identify optimal times for long-distance DX
- Plan HF operations around gray line
- Monitor real-time propagation enhancement
- Track twilight zone progression
- Coordinate international QSOs
🌍 Category: propagation
🔖 Icon: 🌅📦 Version: 1.0.0
📝 Documentation Updates:
- Added v1.5.0 section for panel minimization feature
- Documented minimize/maximize toggle behavior
- Listed all 4 panels that can be minimized
- Explained toggle icons (▼ expanded, ▶ minimized)
- Added 'Minimizing Panels' usage section
- Updated tips section with minimize recommendation
- Updated version number to 1.5.0
- Updated last updated date and subtitle
✨ New Feature - Panel Minimization:
- All 4 control panels can now be minimized/maximized
- Click panel header or toggle button (▼/▶) to collapse/expand
- Minimized panels show only header with ▶ icon
- Expanded panels show full content with ▼ icon
- State persisted to localStorage per panel
🎯 Panels with Minimize:
1. Filter Panel (top-right) - Most useful! Reduces large panel to header
2. Statistics Panel (top-left) - Clean up activity display
3. Legend Panel (bottom-right) - Hide when not needed
4. Band Activity Chart (bottom-left) - Minimize chart view
🎨 UI/UX Improvements:
- Minimize button in header (right-aligned)
- Visual feedback: button opacity changes on hover
- Click header anywhere to toggle (except on controls)
- Click ▼/▶ button to toggle
- Header cursor changes to pointer when minimized
- Smooth transitions
- Icons: ▼ = expanded, ▶ = minimized
🔧 Technical Implementation:
- New addMinimizeToggle() function
- Wraps panel content in .wspr-panel-content div
- Toggle button appended to header
- Persists state to localStorage with '-minimized' suffix
- Loads saved state on panel creation
- CTRL+drag still works when minimized
- Panel positions preserved
💾 State Persistence:
- wspr-filter-position-minimized
- wspr-stats-position-minimized
- wspr-legend-position-minimized
- wspr-chart-position-minimized
🎉 User Benefits:
- Minimize large Filter panel to reduce screen clutter
- Temporarily hide panels without disabling plugin
- Quick access - just click header to restore
- Positions and minimize states both saved
- Clean, unobstructed map view when needed
Version: 1.4.3 → 1.5.0
📝 Documentation Updates:
- Added v1.4.3 section for separate opacity controls
- Documented Path Opacity slider (10-100%, default 70%)
- Documented Heatmap Opacity slider (10-100%, default 60%)
- Added use cases for independent opacity control
- Updated filter panel usage instructions
- Updated version number and last updated date
✨ New Feature:
- Added two independent opacity sliders in filter panel
* Path Opacity: Controls propagation paths and station markers (10-100%)
* Heatmap Opacity: Controls density heatmap circles (10-100%)
- Users can now fine-tune path visibility without affecting heatmap
- Each opacity slider has live value display (shows percentage)
🎨 UI Improvements:
- Added visual separators between control groups
- Path opacity slider with live update (shows 70% default)
- Heatmap opacity slider with live update (shows 60% default)
- Clean organization: Filters → Opacity → Toggles
🔧 Technical Changes:
- Added pathOpacity state (default: 0.7)
- Added heatmapOpacity state (default: 0.6)
- Removed global opacity prop dependency
- Path/marker rendering uses pathOpacity
- Heatmap circles use heatmapOpacity
- Event listeners for both opacity sliders
- Updated filter return object to include both opacities
- Removed old opacity update useEffect (no longer needed)
📊 Benefits:
- Better control over layer visibility
- Can dim paths while keeping heatmap visible
- Can dim heatmap while keeping paths clear
- No more conflict between path and heatmap opacity
- Smooth real-time opacity updates
Version: 1.4.2 → 1.4.3
🐛 Critical Bug Fix:
- Fixed duplicate 'WSPR Activity' popups spawning on opacity/animation changes
- Controls were being recreated every time opacity or showAnimation changed
- Stats, legend, and chart controls now created ONCE on plugin enable
- Control content updated dynamically without recreation
🚀 Performance Improvements:
- Separated control creation from data rendering
- Controls created in dedicated useEffect (runs once per enable)
- Data rendering updates control CONTENT only (via innerHTML)
- Removed opacity, showAnimation, statsControl, legendControl, chartControl from render dependencies
- Used useRef to track control instances (prevents stale closures)
- Reduced re-renders by ~90% on opacity/filter changes
🔧 Technical Changes:
- Added controlRefs: filterControlRef, statsControlRef, legendControlRef, chartControlRef
- Controls created once when enabled becomes true
- Stats/chart content updated via DOM manipulation (no recreation)
- Fixed dependency arrays to only include data-changing dependencies
- Main render effect dependencies: [enabled, wsprData, map, snrThreshold, showAnimation, timeWindow]
- Control creation effect dependencies: [enabled, map]
- Cleanup effect uses controlRefs instead of state
📊 Results:
- No more duplicate popups on opacity slider adjustment
- No more duplicate popups on 'Animate Paths' toggle
- Smooth, responsive UI
- Panel positions preserved correctly
- Memory usage reduced (no control recreation loops)
Version: 1.4.1 → 1.4.2
🐛 Fixed Issues:
- CTRL+Drag Required: Panels now require holding CTRL key to drag
* Cursor changes to 'grab' hand when CTRL is held
* Prevents accidental moves when using dropdowns/sliders
* Visual feedback with tooltip 'Hold CTRL and drag to reposition'
* Default cursor when CTRL not pressed
- Persistent Panel Positions: Positions correctly saved and restored
* Panel positions persist when toggling plugin off/on
* Each panel has independent localStorage key
* Positions restored from localStorage on plugin enable
* Positions saved on drag end
- Proper Cleanup on Disable: All controls removed when plugin disabled
* Fixed 'WSPR Activity' popup remaining after disable
* Fixed multiple popup spawning bug
* All controls properly cleaned up: filterControl, statsControl, legendControl, chartControl, heatmapLayer
* Added console logging for debugging cleanup process
* Controls only created when enabled=true
🔧 Technical Changes:
- Updated makeDraggable() function with CTRL key detection
- Added keydown/keyup listeners for CTRL key state
- Updated cursor dynamically based on CTRL key state
- Enhanced cleanup useEffect with individual control removal
- Added proper state reset for all controls
- Fixed control recreation logic to prevent duplicates
📝 Documentation:
- Updated README.md with v1.4.1 fixes
- Added CTRL+Drag usage instructions
- Documented persistent position behavior
- Added cleanup behavior notes
Version: 1.3.0 → 1.4.1
User-requested enhancements:
🖱️ Draggable Control Panels:
- ALL 4 control panels now draggable (Filter, Stats, Legend, Chart)
- Click and drag any panel to reposition
- Position saved to localStorage automatically
- Panels remember their position between sessions
- Smooth drag with opacity feedback
- Storage keys: wspr-filter-position, wspr-stats-position, etc.
- Prevents accidental dragging of inputs/selects
🗺️ Working Heatmap Implementation:
- Heatmap toggle now functional (was UI-only)
- Shows activity "hot spots" as gradient circles
- Color-coded by station count:
- Red: Very high activity (>7 stations)
- Orange: High activity (5-7 stations)
- Yellow: Moderate activity (3-5 stations)
- Blue: Low activity (<3 stations)
- Circle radius scales with activity level (20-50px base)
- Click hot spots for station count details
- Respects SNR threshold filter
- Smooth opacity transitions
- 0.1° grid aggregation for performance
🔧 Technical Implementation:
- makeDraggable() helper function
- Handles mousedown/mousemove/mouseup events
- Converts Leaflet control position to fixed positioning
- JSON storage for x/y coordinates
- Prevents dragging from interactive elements
- Heatmap algorithm:
- Aggregates TX/RX stations by location
- Counts activity per 0.1° grid cell
- Normalizes intensity (0-1 scale)
- Creates L.circle overlays with gradients
- Efficient rendering (only unique locations)
📊 Heatmap Features:
- Toggle on/off in filter panel
- Works with all other filters (band, time, SNR)
- Popup shows station count per hot spot
- Respects global opacity slider
- Automatic cleanup on disable
- Separate layer management (doesn't interfere with paths)
🎨 UX Improvements:
- Panels convert to fixed positioning when dragged
- Cursor changes to 'move' on hover
- Drag handle on entire panel (except inputs)
- Opacity feedback during drag (0.8)
- Smooth transitions back to opacity 1.0
- Console logging for debugging (heatmap toggle, render count)
🐛 Bug Fixes:
- Heatmap checkbox now actually works
- Added console.log for toggle debugging
- Proper layer cleanup on mode switch
- Fixed heatmap/path view switching
📝 Storage Format:
localStorage['wspr-filter-position'] = {"top":100,"left":200"}
localStorage['wspr-stats-position'] = {"top":50,"left":50"}
localStorage['wspr-legend-position'] = {"top":500,"left":900"}
localStorage['wspr-chart-position'] = {"top":450,"left":50"}
Version: 1.3.0 → 1.4.0
Files: useWSPR.js (+110 lines)
Features: +2 major (draggable, heatmap)
Resolves user issues:
✅ "clicking the heat map doesn't seem to do anything" - FIXED
✅ "popup windows be movable" - DONE
✅ "remember where on the screen they were moved to" - DONE
Fixed critical bug causing map to render as black screen:
🐛 Bug Fix:
- Added comprehensive coordinate validation before great circle calculation
- Prevents NaN (Not a Number) errors that caused Leaflet crashes
- Validates all coordinates are finite numbers before processing
🛡️ Safeguards Added:
- Input validation: Check coordinates exist and are valid numbers
- Distance check: Use simple line for points < 0.5 degrees apart
- Math clamping: Clamp cosine values to [-1, 1] to avoid Math.acos NaN
- Antipodal check: Handle opposite-side-of-Earth edge cases
- Output validation: Verify all generated points are finite
- Fallback: Return simple line if great circle calculation fails
🔍 Edge Cases Handled:
- Same location (distance = 0)
- Very close points (< 0.5 degrees)
- Antipodal points (opposite sides of Earth)
- Invalid/missing coordinates in API data
- NaN propagation from bad input
📝 Logging:
- Console warnings for invalid data (debugging)
- Skips bad spots gracefully without crashing
- Continues processing valid spots
Error message fixed:
"Error: Invalid LatLng object: (NaN, NaN)"
The map now renders correctly with curved propagation paths!
Major enhancements to WSPR Propagation Heatmap plugin:
🌍 Great Circle Paths:
- Replaced straight lines with curved great circle routes
- Follows Earth's curvature for realistic propagation visualization
- 30-point interpolation for smooth arcs
- Removes dashed line style for cleaner appearance
📊 Statistics Display:
- Real-time activity counter (top-left corner)
- Shows: Propagation paths, TX stations, RX stations, total stations
- Updates dynamically as data refreshes
- Dark themed for minimal distraction
📈 Signal Strength Legend:
- Color-coded SNR legend (bottom-right corner)
- 5 signal strength categories with dB ranges
- Helps users quickly interpret propagation quality
- Matches path colors exactly
🎨 UI Improvements:
- Removed dashed lines for smoother appearance
- Enhanced opacity handling for all elements
- Better contrast with white marker borders
- Professional dark theme for overlays
📚 Documentation:
- Created comprehensive README.md in wspr/ directory
- Documented all features, API, usage, troubleshooting
- Included roadmap for future enhancements
- Added developer guide and customization examples
🔧 Technical Improvements:
- Great circle path calculation algorithm
- Leaflet custom control integration
- Proper control cleanup on disable
- No memory leaks with control management
Version bumped: 1.0.0 → 1.1.0
All features fully self-contained in plugin layer
- Added /api/wspr/heatmap endpoint to server.js for fetching WSPR spots from PSK Reporter
- Created useWSPR.js plugin with real-time propagation path visualization
- Features:
- Path lines between TX and RX stations color-coded by SNR
- Signal strength visualization (red=weak, green=strong)
- Automatic refresh every 5 minutes
- Displays up to 500 most recent spots (last 30 minutes)
- Band filtering support (20m, 40m, etc.)
- Detailed popups showing TX/RX info, frequency, SNR, age
- Plugin fully self-contained in layers/ directory per project architecture
- Registered in layerRegistry.js for Settings panel integration
- Zero changes required to core WorldMap component
Data source: PSK Reporter API (WSPR mode)
Category: Propagation
Default: Disabled (user can enable in Settings → Map Layers)
- Added /api/wspr/heatmap endpoint to fetch global WSPR spots via PSK Reporter
- Created useWSPR.js plugin with real-time propagation visualization
- Displays color-coded propagation paths based on SNR (signal strength)
- Shows TX (orange) and RX (blue) station markers with tooltips
- Supports band filtering and 30-minute lookback window
- Auto-refreshes every 5 minutes with caching
- Registered plugin in layerRegistry for Settings panel access
Features:
- Path lines color-coded: red (weak) → yellow → green (strong)
- Line weight varies with signal strength
- Detailed popups showing TX/RX callsigns, frequency, SNR, and age
- Performance-optimized with 500-spot limit
- Compatible with existing plugin architecture (enable/disable, opacity control)
This plugin enables real-time visualization of global HF propagation conditions
by showing WSPR beacon paths and signal strengths across all bands.