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');
}
});