diff --git a/astro.html b/astro.html index ff8e2d6..4788f57 100644 --- a/astro.html +++ b/astro.html @@ -117,6 +117,44 @@ font-size: 0.77rem; } .event-dual span:first-child { color: var(--text); } + .loc-wrap { + border: 1px solid var(--border); + border-radius: 10px; + padding: 0.7rem; + margin: 0.2rem 0 1rem; + background: #171717; + } + .loc-head { + font-size: 0.72rem; + text-transform: uppercase; + letter-spacing: 0.05em; + color: var(--accent); + margin-bottom: 0.45rem; + font-weight: 700; + } + .loc-row { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 0.4rem; + margin-bottom: 0.4rem; + } + .loc-input { + background: #111; + border: 1px solid var(--border); + border-radius: 6px; + color: var(--text); + padding: 0.35rem 0.5rem; + font-size: 0.82rem; + min-width: 0; + } + .loc-status { + font-size: 0.76rem; + color: var(--muted); + min-height: 1.1em; + } + @media (max-width: 640px) { + .loc-row { grid-template-columns: 1fr 1fr; } + } @@ -132,6 +170,22 @@ +
+
Astronomy Location
+
+ + + +
+
+ + + + +
+
Using default location.
+
+
🌑
—
diff --git a/astro.js b/astro.js index 470394c..bcfa344 100644 --- a/astro.js +++ b/astro.js @@ -261,15 +261,25 @@ function formatDate(date) { return date.toLocaleDateString("en-US", { weekday: "long", year: "numeric", month: "long", day: "numeric", timeZone: "UTC" }); } -// Default lat/lon: roughly central US (no geolocation, just reasonable default) -// Will use user's timezone offset to pick a reasonable longitude -function guessLon() { - const offsetHours = -new Date().getTimezoneOffset() / 60; - return offsetHours * 15; // rough: UTC offset * 15° per hour +function loadLocation() { + try { + const raw = localStorage.getItem("astro_location"); + if (!raw) return { label: "Home", lat: 40.0, lon: -75.0 }; + const d = JSON.parse(raw); + const lat = Number(d.lat); + const lon = Number(d.lon); + if (!Number.isFinite(lat) || !Number.isFinite(lon)) throw new Error("bad loc"); + return { label: d.label || "Home", lat, lon }; + } catch { + return { label: "Home", lat: 40.0, lon: -75.0 }; + } } -const LAT = 40.0; // roughly mid-US latitude -const LON = guessLon(); +function saveLocation(loc) { + localStorage.setItem("astro_location", JSON.stringify(loc)); +} + +let locationState = loadLocation(); let currentDate = parseDate(); @@ -281,6 +291,7 @@ function render() { history.replaceState(null, "", newUrl); document.getElementById("dateTitle").textContent = formatDate(date); + document.getElementById("locStatus").textContent = `${locationState.label}: ${locationState.lat.toFixed(4)}, ${locationState.lon.toFixed(4)}`; // Moon const { age, illumination } = moonPhase(date); @@ -304,7 +315,7 @@ function render() { document.getElementById("nextFullGreg").textContent = nextFullDate.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric", timeZone: "UTC" }); // Sun - const sun = sunriseSunset(date, LAT, LON); + const sun = sunriseSunset(date, locationState.lat, locationState.lon); document.getElementById("sunriseSeth").textContent = sun.riseJD ? jdToDecimalTime(sun.riseJD) : "—"; document.getElementById("sunriseGreg").textContent = sun.rise || (sun.alwaysUp ? "Midnight sun" : "Below horizon"); document.getElementById("sunsetSeth").textContent = sun.setJD ? jdToDecimalTime(sun.setJD) : "—"; @@ -360,6 +371,71 @@ function render() { }); } +function initLocationUI() { + const labelEl = document.getElementById("locLabel"); + const zipEl = document.getElementById("locZip"); + const latEl = document.getElementById("locLat"); + const lonEl = document.getElementById("locLon"); + const statusEl = document.getElementById("locStatus"); + + labelEl.value = locationState.label || "Home"; + latEl.value = String(locationState.lat); + lonEl.value = String(locationState.lon); + + document.getElementById("locSave").addEventListener("click", () => { + const lat = Number(latEl.value); + const lon = Number(lonEl.value); + if (!Number.isFinite(lat) || !Number.isFinite(lon) || lat < -90 || lat > 90 || lon < -180 || lon > 180) { + statusEl.textContent = "Invalid latitude/longitude"; + return; + } + locationState = { label: labelEl.value.trim() || "Home", lat, lon }; + saveLocation(locationState); + statusEl.textContent = `Saved: ${locationState.label}`; + render(); + }); + + document.getElementById("locUseZip").addEventListener("click", async () => { + const zip = (zipEl.value || "").trim(); + if (!/^\d{5}(-\d{4})?$/.test(zip)) { + statusEl.textContent = "Enter valid US ZIP"; + return; + } + statusEl.textContent = "Looking up ZIP..."; + try { + const r = await fetch(`https://api.zippopotam.us/us/${encodeURIComponent(zip)}`); + if (!r.ok) throw new Error("zip"); + const data = await r.json(); + const p = data?.places?.[0]; + if (!p) throw new Error("zip"); + latEl.value = String(Number(p.latitude)); + lonEl.value = String(Number(p.longitude)); + if (!labelEl.value.trim()) labelEl.value = `${p["place name"]}, ${p["state abbreviation"]}`; + statusEl.textContent = "ZIP loaded"; + } catch { + statusEl.textContent = "ZIP lookup failed"; + } + }); + + document.getElementById("locGeo").addEventListener("click", () => { + if (!navigator.geolocation) { + statusEl.textContent = "Geolocation unsupported"; + return; + } + statusEl.textContent = "Getting location..."; + navigator.geolocation.getCurrentPosition( + (pos) => { + latEl.value = String(pos.coords.latitude.toFixed(6)); + lonEl.value = String(pos.coords.longitude.toFixed(6)); + if (!labelEl.value.trim()) labelEl.value = "Current Location"; + statusEl.textContent = "Location loaded"; + }, + () => { statusEl.textContent = "Location denied/unavailable"; }, + { enableHighAccuracy: true, timeout: 10000 } + ); + }); +} + document.getElementById("prevDay").addEventListener("click", () => { currentDate = stepDate(currentDate, -1); render(); @@ -369,4 +445,5 @@ document.getElementById("nextDay").addEventListener("click", () => { render(); }); +initLocationUI(); render();