document.addEventListener('DOMContentLoaded', function() { const previewButton = document.getElementById('previewButton'); const starMapContainer = document.getElementById('starMapContainer'); const mapNameInput = document.getElementById('mapName'); const latitudeInput = document.getElementById('latitude'); const longitudeInput = document.getElementById('longitude'); const starDensitySelect = document.getElementById('starDensity'); const constellationStyleSelect = document.getElementById('constellationStyle'); previewButton.addEventListener('click', async function() { const mapName = mapNameInput.value || 'Unnamed Star Map'; const latitude = parseFloat(latitudeInput.value) || 0; const longitude = parseFloat(longitudeInput.value) || 0; const starDensity = starDensitySelect.value; const constellationStyle = constellationStyleSelect.value; // Clear previous content starMapContainer.innerHTML = ''; // Add loading state previewButton.disabled = true; previewButton.innerHTML = ' Generating...'; feather.replace(); try { await generateStarMap(mapName, latitude, longitude, starDensity, constellationStyle); } catch (error) { console.error("Error generating star map:", error); starMapContainer.innerHTML = `
Error: ${error.message}
`; } finally { previewButton.disabled = false; previewButton.innerHTML = ' Generate Star Map Preview'; feather.replace(); } }); async function generateStarMap(mapName, latitude, longitude, starDensity, constellationStyle) { const containerWidth = starMapContainer.offsetWidth; const containerHeight = starMapContainer.offsetHeight; // Clear and prepare container starMapContainer.innerHTML = ''; starMapContainer.style.position = 'relative'; starMapContainer.style.background = 'radial-gradient(ellipse at center, #1a202c 0%, #000000 100%)'; const containerWidth = starMapContainer.offsetWidth; const containerHeight = starMapContainer.offsetHeight; // Set background starMapContainer.style.background = 'radial-gradient(ellipse at center, #1a202c 0%, #000000 100%)'; try { // Fetch real star data from NASA's exoplanet archive API const response = await fetch(`https://exoplanetarchive.ipac.caltech.edu/cgi-bin/nstedAPI/nph-nstedAPI?table=exoplanets&select=ra,dec&format=json`); const stars = await response.json(); // Filter stars visible from given coordinates const visibleStars = stars.filter(star => { // Simple visibility calculation based on celestial coordinates const starLat = star.dec; const starLng = star.ra; return Math.abs(starLat - latitude) < 45 && Math.abs(starLng - longitude) < 45; }); // Generate stars with proper positioning visibleStars.slice(0, 200).forEach(star => { const starElement = document.createElement('div'); starElement.className = 'star'; // Normalize coordinates to container const x = ((star.ra - longitude + 180) % 360) / 360 * containerWidth; const y = ((star.dec - latitude + 90) % 180) / 180 * containerHeight; // Random size and brightness const size = 1 + Math.random() * 3; const brightness = 0.5 + Math.random() * 0.5; starElement.style.cssText = ` position: absolute; width: ${size}px; height: ${size}px; left: ${x}px; top: ${y}px; background: white; border-radius: 50%; box-shadow: 0 0 ${size * 2}px ${size * 0.5}px rgba(255, 255, 255, ${brightness}); animation: twinkle ${2 + Math.random() * 3}s infinite alternate; opacity: ${brightness}; z-index: 2; `; starMapContainer.appendChild(starElement); }); // Remove loading text starMapContainer.removeChild(loadingText); // Real constellations data with more accurate star positions const constellations = [ { name: "Orion", stars: [ [0.45,0.35], // Betelgeuse [0.5,0.4], // Bellatrix [0.55,0.45], // Mintaka [0.6,0.5], // Alnilam [0.65,0.55], // Alnitak [0.5,0.6], // Saiph [0.45,0.65] // Rigel ], lines: [ [0,1], [1,2], [2,3], [3,4], [1,5], [5,6], [6,4] ] }, { name: "Ursa Major", stars: [ [0.2,0.2], // Dubhe [0.25,0.25],// Merak [0.3,0.3], // Phecda [0.35,0.35],// Megrez [0.4,0.4], // Alioth [0.45,0.45],// Mizar [0.5,0.5] // Alkaid ], lines: [ [0,1], [1,2], [2,3], [3,4], [4,5], [5,6] ] }, { name: "Cassiopeia", stars: [ [0.7,0.3], // Schedar [0.75,0.4], // Caph [0.8,0.5], // Cih [0.75,0.6], // Ruchbah [0.7,0.7] // Segin ], lines: [ [0,1], [1,2], [2,3], [3,4] ] } ]; // Determine star count based on density let starCount; switch(starDensity) { case 'low': starCount = 50; break; case 'high': starCount = 300; break; default: starCount = 150; } // Add some random stars for density const extraStars = starDensity === 'low' ? 50 : starDensity === 'high' ? 200 : 100; for (let i = 0; i < extraStars; i++) { const star = document.createElement('div'); star.className = 'star'; const left = Math.random() * containerWidth; const top = Math.random() * containerHeight; const size = Math.random() * 2 + 0.5; star.style.width = `${size}px`; star.style.height = `${size}px`; star.style.left = `${left}px`; star.style.top = `${top}px`; star.style.animationDelay = `${Math.random() * 2}s`; starMapContainer.appendChild(star); } // Add constellation lines (simplified) if (constellationStyle !== 'minimal') { // Draw some lines between nearby stars for (let i = 0; i < visibleStars.length; i++) { for (let j = i + 1; j < Math.min(i + 5, visibleStars.length); j++) { const star1 = visibleStars[i]; const star2 = visibleStars[j]; const x1 = ((star1.ra - longitude + 180) % 360) / 360 * containerWidth; const y1 = ((star1.dec - latitude + 90) % 180) / 180 * containerHeight; const x2 = ((star2.ra - longitude + 180) % 360) / 360 * containerWidth; const y2 = ((star2.dec - latitude + 90) % 180) / 180 * containerHeight; const distance = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2)); if (distance < 100) { // Only connect nearby stars const line = document.createElement('div'); line.className = 'constellation-line'; const length = distance; const angle = Math.atan2(y2 - y1, x2 - x1) * 180 / Math.PI; line.style.width = `${length}px`; line.style.left = `${x1}px`; line.style.top = `${y1}px`; line.style.transform = `rotate(${angle}deg)`; if (constellationStyle === 'classic') { line.style.background = 'rgba(255, 255, 255, 0.5)'; line.style.height = '2px'; } else { // modern line.style.background = 'linear-gradient(90deg, rgba(99,102,241,0.5) 0%, rgba(16,185,129,0.5) 100%)'; line.style.height = '1.5px'; } starMapContainer.appendChild(line); } } } } // Draw constellation lines based on defined connections constellations.forEach(constellation => { const stars = constellation.stars; constellation.lines.forEach(line => { const start = stars[line[0]]; const end = stars[line[1]]; const line = document.createElement('div'); line.className = 'constellation-line'; const startX = start[0] * containerWidth; const startY = start[1] * containerHeight; const endX = end[0] * containerWidth; const endY = end[1] * containerHeight; const length = Math.sqrt(Math.pow(endX - startX, 2) + Math.pow(endY - startY, 2)); const angle = Math.atan2(endY - startY, endX - startX) * 180 / Math.PI; line.style.width = `${length}px`; line.style.left = `${startX}px`; line.style.top = `${startY}px`; line.style.transform = `rotate(${angle}deg)`; if (constellationStyle === 'classic') { line.style.background = 'rgba(255, 255, 255, 0.5)'; line.style.height = '2px'; } else if (constellationStyle === 'minimal') { line.style.background = 'rgba(99, 102, 241, 0.3)'; line.style.height = '1px'; } else { // modern line.style.background = 'linear-gradient(90deg, rgba(99,102,241,0.5) 0%, rgba(16,185,129,0.5) 100%)'; line.style.height = '1.5px'; } starMapContainer.appendChild(line); } }); // Add constellation labels if (constellationStyle !== 'minimal') { constellations.forEach(constellation => { const label = document.createElement('div'); label.className = 'constellation-label'; label.textContent = constellation.name; const firstStar = constellation.stars[0]; label.style.left = `${firstStar[0] * containerWidth + 10}px`; label.style.top = `${firstStar[1] * containerHeight - 10}px`; starMapContainer.appendChild(label); }); } // Add title to the map const title = document.createElement('div'); title.className = 'absolute top-4 left-4 text-white font-bold text-xl'; title.textContent = mapName; starMapContainer.appendChild(title); // Create random stars let starCount; switch(starDensity) { case 'low': starCount = 100; break; case 'high': starCount = 250; break; default: starCount = 150; } for (let i = 0; i < starCount; i++) { const star = document.createElement('div'); star.className = 'star'; const left = Math.random() * containerWidth; const top = Math.random() * containerHeight; const size = 1 + Math.random() * 3; const brightness = 0.5 + Math.random() * 0.5; star.style.cssText = ` position: absolute; width: ${size}px; height: ${size}px; left: ${left}px; top: ${top}px; background: white; border-radius: 50%; box-shadow: 0 0 ${size * 2}px ${size * 0.5}px rgba(255, 255, 255, ${brightness}); animation: twinkle ${2 + Math.random() * 3}s infinite alternate; opacity: ${brightness}; z-index: 2; `; starMapContainer.appendChild(star); } // Add constellation lines if not minimal style if (constellationStyle !== 'minimal') { const constellations = [ { name: "Orion", stars: [ [0.45,0.35], // Betelgeuse [0.5,0.4], // Bellatrix [0.55,0.45], // Mintaka [0.6,0.5], // Alnilam [0.65,0.55], // Alnitak [0.5,0.6], // Saiph [0.45,0.65] // Rigel ], lines: [ [0,1], [1,2], [2,3], [3,4], [1,5], [5,6], [6,4] ] } ]; constellations.forEach(constellation => { // Draw constellation lines constellation.lines.forEach(line => { const start = constellation.stars[line[0]]; const end = constellation.stars[line[1]]; const lineElement = document.createElement('div'); lineElement.className = 'constellation-line'; const startX = start[0] * containerWidth; const startY = start[1] * containerHeight; const endX = end[0] * containerWidth; const endY = end[1] * containerHeight; const length = Math.sqrt(Math.pow(endX - startX, 2) + Math.pow(endY - startY, 2)); const angle = Math.atan2(endY - startY, endX - startX) * 180 / Math.PI; lineElement.style.width = `${length}px`; lineElement.style.left = `${startX}px`; lineElement.style.top = `${startY}px`; lineElement.style.transform = `rotate(${angle}deg)`; if (constellationStyle === 'classic') { lineElement.style.background = 'rgba(255, 255, 255, 0.7)'; lineElement.style.height = '2px'; } else { lineElement.style.background = 'linear-gradient(90deg, rgba(99,102,241,0.7) 0%, rgba(16,185,129,0.7) 100%)'; lineElement.style.height = '1.5px'; } starMapContainer.appendChild(lineElement); }); // Add constellation label if (constellationStyle !== 'minimal') { const label = document.createElement('div'); label.className = 'constellation-label'; label.textContent = constellation.name; const firstStar = constellation.stars[0]; label.style.left = `${firstStar[0] * containerWidth + 10}px`; label.style.top = `${firstStar[1] * containerHeight - 10}px`; starMapContainer.appendChild(label); } }); } // Add title to the map const title = document.createElement('div'); title.className = 'absolute top-4 left-4 text-white font-bold text-xl'; title.textContent = mapName; starMapContainer.appendChild(title); // Add a special "planet" const planet = document.createElement('div'); planet.className = 'absolute rounded-full bg-gradient-to-br from-primary-500 to-secondary-500 shadow-xl'; planet.style.width = '40px'; planet.style.height = '40px'; planet.style.left = `${containerWidth * 0.7}px`; planet.style.top = `${containerHeight * 0.3}px`; planet.style.boxShadow = '0 0 20px 5px rgba(99, 102, 241, 0.5)'; starMapContainer.appendChild(planet); // Create download button const downloadBtn = document.createElement('button'); downloadBtn.className = 'absolute bottom-4 right-4 px-4 py-2 bg-primary-500 hover:bg-primary-600 text-white rounded-lg'; downloadBtn.innerHTML = 'Save Map'; downloadBtn.addEventListener('click', () => { const dataUrl = starMapContainerToImage(); const link = document.createElement('a'); link.download = `${mapName.replace(/\s+/g, '_')}_star_map.png`; link.href = dataUrl; link.click(); }); starMapContainer.appendChild(downloadBtn); feather.replace(); } function starMapContainerToImage() { const container = starMapContainer; const canvas = document.createElement('canvas'); canvas.width = container.offsetWidth; canvas.height = container.offsetHeight; const ctx = canvas.getContext('2d'); // Draw background ctx.fillStyle = getComputedStyle(container).backgroundColor; ctx.fillRect(0, 0, canvas.width, canvas.height); // Draw all child elements (simplified approach) const children = container.children; for (let i = 0; i < children.length; i++) { const child = children[i]; const style = getComputedStyle(child); if (child.className.includes('star')) { ctx.fillStyle = style.backgroundColor; ctx.beginPath(); ctx.arc( parseFloat(style.left), parseFloat(style.top), parseFloat(style.width) / 2, 0, Math.PI * 2 ); ctx.fill(); } // Similar drawing logic for other elements... } return canvas.toDataURL('image/png'); } });