Astronomy Event Calendar Generator
Compile confirmed meteor showers, conjunctions, and eclipses for your location.
Search Parameters
Searching real-time astronomical databases...
Celestial Event Calendar
Location: N/A | Period: N/A
Please configure the start date, end date, and location to generate the calendar.
Retrieving live astronomical data...
'; try { const systemPrompt = "You are a professional astronomical events curator. Search for meteor showers, planetary conjunctions, and major astronomical events occurring between the given dates, specifically considering visibility for the specified location. Output the results as a concise, structured JSON array."; const userQuery = `Find major visible astronomical events (e.g., meteor showers, conjunctions, eclipses, lunar phases) for the period between ${startMonth} and ${endMonth} observable from ${location}. Provide Date, Event Name, and Key Detail/Visibility.`; const apiKey = ""; const apiUrl = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-preview-09-2025:generateContent?key=${apiKey}`; const payload = { contents: [{ parts: [{ text: userQuery }] }], tools: [{ "google_search": {} }], // Use Google Search for grounded, current data systemInstruction: { parts: [{ text: systemPrompt }] }, generationConfig: { responseMimeType: "application/json", responseSchema: { type: "ARRAY", items: { type: "OBJECT", properties: { date: { type: "STRING", description: "Date of peak event (YYYY-MM-DD)." }, event: { type: "STRING", description: "Common name of the event (e.g., Perseids Meteor Shower)." }, detail: { type: "STRING", description: "Brief visibility notes (e.g., Best seen after midnight, Peak Zenith Hourly Rate (ZHR))." } }, required: ["date", "event", "detail"] } } } }; const response = await fetchWithRetry(apiUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); const result = await response.json(); const jsonText = result.candidates?.[0]?.content?.parts?.[0]?.text; if (jsonText) { const eventData = JSON.parse(jsonText); aecgRenderCalendar(eventData, location, startMonth, endMonth); } else { console.error("Gemini response structure error:", result); outputDiv.innerHTML = 'Error: Could not parse event data. Check inputs or try again later.
'; } } catch (error) { console.error("API Error:", error); outputDiv.innerHTML = 'API Connection Error. Please verify network access and try again.
'; } finally { loadingMsg.classList.add('hidden'); generateBtn.disabled = false; } } // --- Rendering and PDF --- function aecgRenderCalendar(events, location, start, end) { const outputDiv = document.getElementById('aecg-event-output'); const list = document.createElement('ul'); list.className = 'aecg-event-list'; // Update Header document.getElementById('disp-location').textContent = escapeHtml(location); document.getElementById('disp-period').textContent = `${escapeHtml(start)} to ${escapeHtml(end)}`; if (events.length === 0) { outputDiv.innerHTML = 'No major celestial events found for this period and location.
'; return; } // Sort by date ascending events.sort((a, b) => new Date(a.date) - new Date(b.date)); events.forEach(e => { const item = document.createElement('li'); item.className = 'aecg-event-item'; // Format date for display const eventDate = new Date(e.date).toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' }); item.innerHTML = ` ${escapeHtml(e.event)} Date: ${eventDate} Visibility Notes: ${escapeHtml(e.detail)} `; list.appendChild(item); }); outputDiv.innerHTML = ''; outputDiv.appendChild(list); } window.aecgDownloadPDF = function() { const element = document.getElementById('aecg-output-area'); const locationName = document.getElementById('aecg-location').value.split(',')[0] || "Calendar"; document.body.classList.add('aecg-generating-pdf'); const opt = { margin: [0.75, 0.75], filename: `${locationName.replace(/\s+/g, '_')}_Astronomy_Calendar.pdf`, image: { type: 'jpeg', quality: 0.98 }, html2canvas: { scale: 2, useCORS: true }, jsPDF: { unit: 'in', format: 'letter', orientation: 'portrait' } }; html2pdf().set(opt).from(element).save().then(() => { document.body.classList.remove('aecg-generating-pdf'); }); }; // Utility: HTML Escape function escapeHtml(text) { if (!text) return ''; return text.replace(/[&<>"']/g, function(m) { switch (m) { case '&': return '&'; case '<': return '<'; case '>': return '>'; case '"': return '"'; case "'": return '''; default: return m; } }); }