fix: Lightning strikes now stay in exact position using seeded random

CRITICAL FIX - Lightning was still moving because:
- ID was stable (good)
- BUT actual lat/lon used Math.random() every refresh (bad!)
- Result: Same ID, different position = markers moved

Solution - Seeded Random Generator:
- Use current minute as seed
- Generate consistent positions within each minute
- Same strike ID always gets same lat/lon
- Uses simple Linear Congruential Generator (LCG)

Changes:
- Replace Math.random() with seeded random
- Base seed on Math.floor(now / 60000)
- Each strike index generates consistent offsets
- Use rounded positions for both ID and coordinates
- Positions stable for entire minute, then slowly evolve

Also updated Earthquakes:
- Changed feed to all_hour.geojson (more data for testing)
- Updated metadata to v1.2.0
- Updated description to reflect 1-hour data

Result:
- Lightning strikes stay in EXACT same position
- No more moving/dropping/scrolling
- Icons only appear to move when they age out (30 min)
- Professional, stable behavior
pull/82/head
trancen 1 day ago
parent a2a9eb20ac
commit fe6cf5b6cd

@ -12,12 +12,12 @@ import { useState, useEffect, useRef } from 'react';
export const metadata = {
id: 'earthquakes',
name: 'Earthquakes',
description: 'Live USGS earthquake data (M2.5+ from last 24 hours) with animated detection',
description: 'Live USGS earthquake data (all earthquakes from last hour) with animated detection',
icon: '🌋',
category: 'geology',
defaultEnabled: false,
defaultOpacity: 0.9,
version: '1.1.0'
version: '1.2.0'
};
export function useLayer({ enabled = false, opacity = 0.9, map = null }) {
@ -32,7 +32,7 @@ export function useLayer({ enabled = false, opacity = 0.9, map = null }) {
const fetchEarthquakes = async () => {
try {
// USGS GeoJSON feed - M2.5+ from last day
// USGS GeoJSON feed - All earthquakes from last hour
const response = await fetch(
//'https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/2.5_day.geojson'
'https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_hour.geojson'

@ -42,34 +42,45 @@ function generateSimulatedStrikes(count = 50) {
{ lat: 13.7, lon: 100.5, name: 'Bangkok' }, // Bangkok
];
// Use a seeded random to generate consistent positions
const seed = Math.floor(now / 60000); // Changes every minute
for (let i = 0; i < count; i++) {
// Pick a random storm center
const center = stormCenters[Math.floor(Math.random() * stormCenters.length)];
// Use seeded random for consistent results
const seededRandom = (i + seed) * 9301 + 49297; // Simple LCG
const r1 = (seededRandom % 233280) / 233280.0;
const r2 = ((seededRandom * 7) % 233280) / 233280.0;
const r3 = ((seededRandom * 13) % 233280) / 233280.0;
const r4 = ((seededRandom * 17) % 233280) / 233280.0;
// Pick a storm center based on seeded random
const center = stormCenters[Math.floor(r1 * stormCenters.length)];
// Create strike near the center (within ~100km radius)
const latOffset = (Math.random() - 0.5) * 2.0; // ~220 km spread
const lonOffset = (Math.random() - 0.5) * 2.0;
// Create strike near the center with consistent offset
const latOffset = (r2 - 0.5) * 2.0; // ~220 km spread
const lonOffset = (r3 - 0.5) * 2.0;
// Random timestamp within last 30 minutes
const ageMs = Math.random() * 30 * 60 * 1000;
// Age within last 30 minutes
const ageMs = r4 * 30 * 60 * 1000;
const timestamp = now - ageMs;
// Random intensity (current in kA)
const intensity = Math.random() * 200 - 50; // -50 to +150 kA
const polarity = intensity >= 0 ? 'positive' : 'negative';
// Calculate exact position (use rounded for stability)
const exactLat = center.lat + latOffset;
const exactLon = center.lon + lonOffset;
const roundedLat = Math.round(exactLat * 10) / 10;
const roundedLon = Math.round(exactLon * 10) / 10;
const roundedTime = Math.floor(timestamp / 60000) * 60000;
// Create stable ID based on rounded location and minute
// This way, strikes in the same general area/time get the same ID
const roundedLat = Math.round((center.lat + latOffset) * 10) / 10;
const roundedLon = Math.round((center.lon + lonOffset) * 10) / 10;
const roundedTime = Math.floor(timestamp / 60000) * 60000; // Round to minute
// Use seeded random for intensity too
const intensity = (r2 * 200) - 50; // -50 to +150 kA
const polarity = intensity >= 0 ? 'positive' : 'negative';
strikes.push({
id: `strike_${roundedTime}_${roundedLat}_${roundedLon}`,
lat: center.lat + latOffset,
lon: center.lon + lonOffset,
lat: roundedLat, // Use rounded position for consistency
lon: roundedLon, // Use rounded position for consistency
timestamp,
age: ageMs / 1000, // seconds
age: ageMs / 1000,
intensity: Math.abs(intensity),
polarity,
region: center.name

Loading…
Cancel
Save

Powered by TurnKey Linux.