diff --git a/src/plugins/layers/useEarthquakes.js b/src/plugins/layers/useEarthquakes.js
index 5a84ae3..5e18220 100644
--- a/src/plugins/layers/useEarthquakes.js
+++ b/src/plugins/layers/useEarthquakes.js
@@ -35,7 +35,7 @@ export function useLayer({ enabled = false, opacity = 0.9, map = null }) {
// USGS GeoJSON feed - M2.5+ from last day
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/1.0_hour.geojson'
+ 'https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_hour.geojson'
);
const data = await response.json();
setEarthquakeData(data.features || []);
@@ -88,8 +88,8 @@ export function useLayer({ enabled = false, opacity = 0.9, map = null }) {
// Check if this is a new earthquake (but not on first load)
const isNew = !isFirstLoad.current && !previousQuakeIds.current.has(quakeId);
- // Calculate marker size based on magnitude (M2.5 = 8px, M7+ = 40px)
- const size = Math.min(Math.max(mag * 4, 8), 40);
+ // Calculate marker size based on magnitude (M2.5 = 12px, M7+ = 36px)
+ const size = Math.min(Math.max(mag * 5, 12), 36);
// Color based on magnitude
let color;
@@ -100,16 +100,15 @@ export function useLayer({ enabled = false, opacity = 0.9, map = null }) {
else if (mag < 7) color = '#cc0000'; // Dark red - major
else color = '#990000'; // Very dark red - great
- // Create circle marker - start with static class
- const circle = L.circleMarker([lat, lon], {
- radius: size / 2,
- fillColor: color,
- color: '#fff',
- weight: 2,
- opacity: opacity,
- fillOpacity: opacity * 0.7,
- className: 'earthquake-marker'
+ // Create earthquake icon marker (using circle with waves emoji or special char)
+ const icon = L.divIcon({
+ className: 'earthquake-icon',
+ html: `
🌋
`,
+ iconSize: [size, size],
+ iconAnchor: [size/2, size/2]
});
+
+ const circle = L.marker([lat, lon], { icon, opacity });
// Add to map first
circle.addTo(map);
@@ -119,17 +118,19 @@ export function useLayer({ enabled = false, opacity = 0.9, map = null }) {
// Wait for DOM element to be created, then add animation class
setTimeout(() => {
try {
- if (circle._path) {
- circle._path.classList.add('earthquake-pulse-new');
-
- // Remove animation class after it completes (0.8s)
- setTimeout(() => {
- try {
- if (circle._path) {
- circle._path.classList.remove('earthquake-pulse-new');
- }
- } catch (e) {}
- }, 800);
+ const iconElement = circle.getElement();
+ if (iconElement) {
+ const iconDiv = iconElement.querySelector('div');
+ if (iconDiv) {
+ iconDiv.classList.add('earthquake-pulse-new');
+
+ // Remove animation class after it completes (0.8s)
+ setTimeout(() => {
+ try {
+ iconDiv.classList.remove('earthquake-pulse-new');
+ } catch (e) {}
+ }, 800);
+ }
}
} catch (e) {
console.warn('Could not animate earthquake marker:', e);
diff --git a/src/plugins/layers/useLightning.js b/src/plugins/layers/useLightning.js
index c1f27b0..74236f9 100644
--- a/src/plugins/layers/useLightning.js
+++ b/src/plugins/layers/useLightning.js
@@ -58,8 +58,14 @@ function generateSimulatedStrikes(count = 50) {
const intensity = Math.random() * 200 - 50; // -50 to +150 kA
const polarity = intensity >= 0 ? 'positive' : 'negative';
+ // 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
+
strikes.push({
- id: `strike_${timestamp}_${i}`,
+ id: `strike_${roundedTime}_${roundedLat}_${roundedLon}`,
lat: center.lat + latOffset,
lon: center.lon + lonOffset,
timestamp,
@@ -141,20 +147,19 @@ export function useLayer({ enabled = false, opacity = 0.9, map = null }) {
const ageMinutes = age / 60;
const color = getStrikeColor(ageMinutes);
- // Size based on intensity (5-20px)
- const size = Math.min(Math.max(intensity / 10, 5), 20);
+ // Size based on intensity (12-32px)
+ const size = Math.min(Math.max(intensity / 8, 12), 32);
- // Create lightning bolt marker - start with static class
- const marker = L.circleMarker([lat, lon], {
- radius: size / 2,
- fillColor: color,
- color: '#fff',
- weight: isNew ? 3 : 1,
- opacity: opacity,
- fillOpacity: opacity * (isNew ? 1.0 : 0.7),
- className: 'lightning-strike'
+ // Create lightning bolt icon marker
+ const icon = L.divIcon({
+ className: 'lightning-strike-icon',
+ html: `âš¡
`,
+ iconSize: [size, size],
+ iconAnchor: [size/2, size/2]
});
+ const marker = L.marker([lat, lon], { icon, opacity });
+
// Add to map first
marker.addTo(map);
@@ -163,17 +168,19 @@ export function useLayer({ enabled = false, opacity = 0.9, map = null }) {
// Wait for DOM element to be created, then add animation class
setTimeout(() => {
try {
- if (marker._path) {
- marker._path.classList.add('lightning-strike-new');
-
- // Remove animation class after it completes (0.8s)
- setTimeout(() => {
- try {
- if (marker._path) {
- marker._path.classList.remove('lightning-strike-new');
- }
- } catch (e) {}
- }, 800);
+ const iconElement = marker.getElement();
+ if (iconElement) {
+ const iconDiv = iconElement.querySelector('div');
+ if (iconDiv) {
+ iconDiv.classList.add('lightning-strike-new');
+
+ // Remove animation class after it completes (0.8s)
+ setTimeout(() => {
+ try {
+ iconDiv.classList.remove('lightning-strike-new');
+ } catch (e) {}
+ }, 800);
+ }
}
} catch (e) {
console.warn('Could not animate lightning marker:', e);
diff --git a/src/styles/main.css b/src/styles/main.css
index 6db4714..7848ec0 100644
--- a/src/styles/main.css
+++ b/src/styles/main.css
@@ -760,23 +760,45 @@ body::before {
animation: earthquake-pulse 3s ease-out;
}
-/* Flash/fade animation for new earthquakes - no transform */
+/* Flash/fade animation for new earthquakes - with shake */
@keyframes earthquake-flash {
0% {
opacity: 0;
filter: brightness(3) drop-shadow(0 0 8px currentColor);
+ transform: translate(0, 0);
+ }
+ 10% {
+ transform: translate(-2px, 1px) rotate(-5deg);
+ }
+ 20% {
+ transform: translate(2px, -1px) rotate(5deg);
}
30% {
opacity: 1;
filter: brightness(2) drop-shadow(0 0 6px currentColor);
+ transform: translate(-1px, 2px) rotate(-3deg);
+ }
+ 40% {
+ transform: translate(1px, -2px) rotate(3deg);
+ }
+ 50% {
+ transform: translate(-1px, 1px) rotate(-2deg);
}
60% {
opacity: 1;
filter: brightness(1.5) drop-shadow(0 0 4px currentColor);
+ transform: translate(1px, -1px) rotate(2deg);
+ }
+ 70% {
+ transform: translate(0, 1px) rotate(-1deg);
+ }
+ 80% {
+ transform: translate(0, -1px) rotate(1deg);
}
100% {
opacity: 1;
filter: brightness(1) drop-shadow(0 0 0px transparent);
+ transform: translate(0, 0) rotate(0);
}
}