import { ComposedChart, Bar, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, Cell, } from 'recharts'; interface YearPoint { date: string; alt_at_midnight: number; transit_alt: number; usable_min: number; moon_illumination: number; } interface Props { points: YearPoint[]; } const MONTH_ABBR = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; function altColor(alt: number): string { if (alt >= 50) return 'var(--good)'; if (alt >= 30) return '#2ab8a0'; if (alt >= 15) return 'var(--warn)'; return 'var(--muted)'; } export default function YearlyVisibility({ points }: Props) { if (!points.length) return null; // Sample to ~52 weekly points for readability const stride = Math.max(1, Math.floor(points.length / 52)); const sampled = points.filter((_, i) => i % stride === 0); const data = sampled.map(p => { const d = new Date(p.date + 'T00:00:00Z'); return { label: `${MONTH_ABBR[d.getUTCMonth()]} ${d.getUTCDate()}`, month: d.getUTCMonth(), alt: Math.round(p.alt_at_midnight * 10) / 10, transit_alt: Math.round(p.transit_alt), usable: Math.round(p.usable_min / 60 * 10) / 10, moon: Math.round(p.moon_illumination * 100), }; }); return (
ALTITUDE AT MIDNIGHT — next 12 months (varies as transit shifts through seasons)
`${v}°`} width={32} /> `${v}%`} width={28} /> { if (name === 'alt') return [`${value}°`, 'Alt at midnight']; if (name === 'moon') return [`${value}%`, 'Moon']; return [value, name]; }} /> {data.map((entry, i) => ( ))}
{[ { color: 'var(--good)', label: '≥50° excellent' }, { color: '#2ab8a0', label: '30–50° good' }, { color: 'var(--warn)', label: '15–30° marginal' }, { color: 'var(--muted)', label: '<15° poor' }, { color: '#4d9de0', label: 'Moon %' }, ].map(({ color, label }) => (
{label}
))}
); }