Add persistent location controls to astronomy page (ZIP, geolocation, manual)
This commit is contained in:
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user