diff --git a/public/index.html b/public/index.html
index 60e5ead..0b0db95 100644
--- a/public/index.html
+++ b/public/index.html
@@ -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
${moonEmoji}`)
+ .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
${moonEmoji}`);
+ }
+ };
+
+ 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 @@
● DE
● DX
☀ Sun
+ 🌙 Moon