|
|
|
|
@ -213,6 +213,13 @@
|
|
|
|
|
height: 24px;
|
|
|
|
|
border: 2px solid #ffaa00;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.moon-marker {
|
|
|
|
|
background: radial-gradient(circle, #e8e8f0 0%, #8888aa 100%);
|
|
|
|
|
width: 24px;
|
|
|
|
|
height: 24px;
|
|
|
|
|
border: 2px solid #aaaacc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Map style selector */
|
|
|
|
|
.map-style-control {
|
|
|
|
|
@ -456,6 +463,97 @@
|
|
|
|
|
return { lat: declination, lon: longitude };
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Calculate moon position (sublunar point)
|
|
|
|
|
const getMoonPosition = (date) => {
|
|
|
|
|
// Julian date calculation
|
|
|
|
|
const JD = date.getTime() / 86400000 + 2440587.5;
|
|
|
|
|
const T = (JD - 2451545.0) / 36525; // Julian centuries from J2000
|
|
|
|
|
|
|
|
|
|
// Moon's mean longitude
|
|
|
|
|
const L0 = (218.316 + 481267.8813 * T) % 360;
|
|
|
|
|
|
|
|
|
|
// Moon's mean anomaly
|
|
|
|
|
const M = (134.963 + 477198.8676 * T) % 360;
|
|
|
|
|
const MRad = M * Math.PI / 180;
|
|
|
|
|
|
|
|
|
|
// Moon's mean elongation
|
|
|
|
|
const D = (297.850 + 445267.1115 * T) % 360;
|
|
|
|
|
const DRad = D * Math.PI / 180;
|
|
|
|
|
|
|
|
|
|
// Sun's mean anomaly
|
|
|
|
|
const Ms = (357.529 + 35999.0503 * T) % 360;
|
|
|
|
|
const MsRad = Ms * Math.PI / 180;
|
|
|
|
|
|
|
|
|
|
// Moon's argument of latitude
|
|
|
|
|
const F = (93.272 + 483202.0175 * T) % 360;
|
|
|
|
|
const FRad = F * Math.PI / 180;
|
|
|
|
|
|
|
|
|
|
// Longitude corrections (simplified)
|
|
|
|
|
const dL = 6.289 * Math.sin(MRad)
|
|
|
|
|
+ 1.274 * Math.sin(2 * DRad - MRad)
|
|
|
|
|
+ 0.658 * Math.sin(2 * DRad)
|
|
|
|
|
+ 0.214 * Math.sin(2 * MRad)
|
|
|
|
|
- 0.186 * Math.sin(MsRad)
|
|
|
|
|
- 0.114 * Math.sin(2 * FRad);
|
|
|
|
|
|
|
|
|
|
// Moon's ecliptic longitude
|
|
|
|
|
const moonLon = ((L0 + dL) % 360 + 360) % 360;
|
|
|
|
|
|
|
|
|
|
// Moon's ecliptic latitude (simplified)
|
|
|
|
|
const moonLat = 5.128 * Math.sin(FRad)
|
|
|
|
|
+ 0.281 * Math.sin(MRad + FRad)
|
|
|
|
|
+ 0.278 * Math.sin(MRad - FRad);
|
|
|
|
|
|
|
|
|
|
// Convert ecliptic to equatorial coordinates
|
|
|
|
|
const obliquity = 23.439 - 0.0000004 * (JD - 2451545.0);
|
|
|
|
|
const oblRad = obliquity * Math.PI / 180;
|
|
|
|
|
const moonLonRad = moonLon * Math.PI / 180;
|
|
|
|
|
const moonLatRad = moonLat * Math.PI / 180;
|
|
|
|
|
|
|
|
|
|
// Right ascension
|
|
|
|
|
const RA = Math.atan2(
|
|
|
|
|
Math.sin(moonLonRad) * Math.cos(oblRad) - Math.tan(moonLatRad) * Math.sin(oblRad),
|
|
|
|
|
Math.cos(moonLonRad)
|
|
|
|
|
) * 180 / Math.PI;
|
|
|
|
|
|
|
|
|
|
// Declination
|
|
|
|
|
const dec = Math.asin(
|
|
|
|
|
Math.sin(moonLatRad) * Math.cos(oblRad) +
|
|
|
|
|
Math.cos(moonLatRad) * Math.sin(oblRad) * Math.sin(moonLonRad)
|
|
|
|
|
) * 180 / Math.PI;
|
|
|
|
|
|
|
|
|
|
// Greenwich Mean Sidereal Time
|
|
|
|
|
const GMST = (280.46061837 + 360.98564736629 * (JD - 2451545.0)) % 360;
|
|
|
|
|
|
|
|
|
|
// Sublunar point longitude
|
|
|
|
|
const sublunarLon = ((RA - GMST) % 360 + 540) % 360 - 180;
|
|
|
|
|
|
|
|
|
|
return { lat: dec, lon: sublunarLon };
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Calculate moon phase (0-1, 0=new, 0.5=full)
|
|
|
|
|
const getMoonPhase = (date) => {
|
|
|
|
|
const JD = date.getTime() / 86400000 + 2440587.5;
|
|
|
|
|
const T = (JD - 2451545.0) / 36525;
|
|
|
|
|
const D = (297.850 + 445267.1115 * T) % 360; // Mean elongation
|
|
|
|
|
// Phase angle (simplified)
|
|
|
|
|
const phase = ((D + 180) % 360) / 360;
|
|
|
|
|
return phase;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Get moon phase emoji
|
|
|
|
|
const getMoonPhaseEmoji = (phase) => {
|
|
|
|
|
if (phase < 0.0625) return '🌑'; // New moon
|
|
|
|
|
if (phase < 0.1875) return '🌒'; // Waxing crescent
|
|
|
|
|
if (phase < 0.3125) return '🌓'; // First quarter
|
|
|
|
|
if (phase < 0.4375) return '🌔'; // Waxing gibbous
|
|
|
|
|
if (phase < 0.5625) return '🌕'; // Full moon
|
|
|
|
|
if (phase < 0.6875) return '🌖'; // Waning gibbous
|
|
|
|
|
if (phase < 0.8125) return '🌗'; // Last quarter
|
|
|
|
|
if (phase < 0.9375) return '🌘'; // Waning crescent
|
|
|
|
|
return '🌑'; // New moon
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const calculateSunTimes = (lat, lon, date) => {
|
|
|
|
|
const dayOfYear = Math.floor((date - new Date(date.getFullYear(), 0, 0)) / 86400000);
|
|
|
|
|
const declination = -23.45 * Math.cos((360/365) * (dayOfYear + 10) * Math.PI / 180);
|
|
|
|
|
@ -2155,6 +2253,7 @@
|
|
|
|
|
const deMarkerRef = useRef(null);
|
|
|
|
|
const dxMarkerRef = useRef(null);
|
|
|
|
|
const sunMarkerRef = useRef(null);
|
|
|
|
|
const moonMarkerRef = useRef(null);
|
|
|
|
|
const potaMarkersRef = useRef([]);
|
|
|
|
|
const mySpotsMarkersRef = useRef([]);
|
|
|
|
|
const mySpotsLinesRef = useRef([]);
|
|
|
|
|
@ -2286,6 +2385,7 @@
|
|
|
|
|
if (deMarkerRef.current) map.removeLayer(deMarkerRef.current);
|
|
|
|
|
if (dxMarkerRef.current) map.removeLayer(dxMarkerRef.current);
|
|
|
|
|
if (sunMarkerRef.current) map.removeLayer(sunMarkerRef.current);
|
|
|
|
|
if (moonMarkerRef.current) map.removeLayer(moonMarkerRef.current);
|
|
|
|
|
|
|
|
|
|
// DE Marker
|
|
|
|
|
const deIcon = L.divIcon({
|
|
|
|
|
@ -2321,8 +2421,56 @@
|
|
|
|
|
.bindPopup('Subsolar Point')
|
|
|
|
|
.addTo(map);
|
|
|
|
|
|
|
|
|
|
// Moon marker
|
|
|
|
|
const moonPos = getMoonPosition(new Date());
|
|
|
|
|
const moonPhase = getMoonPhase(new Date());
|
|
|
|
|
const moonEmoji = getMoonPhaseEmoji(moonPhase);
|
|
|
|
|
const moonIcon = L.divIcon({
|
|
|
|
|
className: 'custom-marker moon-marker',
|
|
|
|
|
html: moonEmoji,
|
|
|
|
|
iconSize: [24, 24],
|
|
|
|
|
iconAnchor: [12, 12]
|
|
|
|
|
});
|
|
|
|
|
moonMarkerRef.current = L.marker([moonPos.lat, moonPos.lon], { icon: moonIcon })
|
|
|
|
|
.bindPopup(`Sublunar Point<br><span style="font-size: 24px">${moonEmoji}</span>`)
|
|
|
|
|
.addTo(map);
|
|
|
|
|
|
|
|
|
|
}, [deLocation, dxLocation]);
|
|
|
|
|
|
|
|
|
|
// Update sun and moon positions every minute
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (!mapInstanceRef.current) return;
|
|
|
|
|
const map = mapInstanceRef.current;
|
|
|
|
|
|
|
|
|
|
const updateCelestialBodies = () => {
|
|
|
|
|
// Update sun position
|
|
|
|
|
if (sunMarkerRef.current) {
|
|
|
|
|
const sunPos = getSunPosition(new Date());
|
|
|
|
|
sunMarkerRef.current.setLatLng([sunPos.lat, sunPos.lon]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Update moon position
|
|
|
|
|
if (moonMarkerRef.current) {
|
|
|
|
|
const moonPos = getMoonPosition(new Date());
|
|
|
|
|
const moonPhase = getMoonPhase(new Date());
|
|
|
|
|
const moonEmoji = getMoonPhaseEmoji(moonPhase);
|
|
|
|
|
moonMarkerRef.current.setLatLng([moonPos.lat, moonPos.lon]);
|
|
|
|
|
// Update moon icon to reflect current phase
|
|
|
|
|
const moonIcon = L.divIcon({
|
|
|
|
|
className: 'custom-marker moon-marker',
|
|
|
|
|
html: moonEmoji,
|
|
|
|
|
iconSize: [24, 24],
|
|
|
|
|
iconAnchor: [12, 12]
|
|
|
|
|
});
|
|
|
|
|
moonMarkerRef.current.setIcon(moonIcon);
|
|
|
|
|
moonMarkerRef.current.setPopupContent(`Sublunar Point<br><span style="font-size: 24px">${moonEmoji}</span>`);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const interval = setInterval(updateCelestialBodies, 60000); // Every minute
|
|
|
|
|
return () => clearInterval(interval);
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
// Update POTA markers
|
|
|
|
|
// Update my spots markers and connection lines
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
@ -2763,6 +2911,7 @@
|
|
|
|
|
<span style={{ color: '#00ff00' }}>● DE</span>
|
|
|
|
|
<span style={{ color: '#ff4444' }}>● DX</span>
|
|
|
|
|
<span style={{ color: '#ffff00' }}>☀ Sun</span>
|
|
|
|
|
<span style={{ color: '#aaccff' }}>🌙 Moon</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|