spb/satelliteindex
Public
TypeScript 66.5%
Python 30.9%
JavaScript 1.4%
CSS 0.7%
1import Link from 'next/link';2import { Bars, StackedBars } from '@/components/charts/charts';3import { fmtInt, num } from '@/lib/format';4import { routes } from '@/lib/site';5import type { StatsSnapshot } from '@/lib/types';6import { ChartBlock, Derived, Note, TwoCol } from './shared';78/** Full-history charts (since 1957) + derived growth-rate table. */9export function StatsHistory({ snapshot }: { snapshot: StatsSnapshot }) {10 const launches = [...snapshot.launches_by_year].sort((a, b) => a.year - b.year);11 const payloads = [...snapshot.payloads_by_launch_year].sort((a, b) => a.year - b.year);12 const decays = [...snapshot.decays_by_year].sort((a, b) => a.year - b.year);13 const first = launches[0]?.year;14 const last = launches[launches.length - 1]?.year;15 const span = first && last ? `${first}–${last}` : undefined;1617 const launchSeries = launches.map((r) => ({ x: r.year, y: num(r.launches) ?? 0 }));18 const payloadStack = payloads.map((r) => {19 const p = num(r.payloads) ?? 0;20 const a = num(r.still_active) ?? 0;21 return { x: r.year, still_active: a, retired: Math.max(0, p - a) };22 });23 const decaySeries = decays.map((r) => ({ x: r.year, y: num(r.decayed) ?? 0 }));2425 // Derived: year-over-year change of payloads launched (last 10 complete + current year).26 const growth = payloads27 .map((r, i) => {28 const cur = num(r.payloads) ?? 0;29 const prev = i > 0 ? (num(payloads[i - 1]?.payloads) ?? 0) : null;30 const pct = prev && prev > 0 ? ((cur - prev) / prev) * 100 : null;31 return { year: r.year, payloads: cur, prev, pct, active: num(r.still_active) ?? 0 };32 })33 .slice(-10)34 .reverse();35 const currentYear = new Date().getUTCFullYear();3637 return (38 <div className="space-y-12">39 <ChartBlock title="Orbital launches per year" hint={span}>40 <Bars data={launchSeries} title="Orbital launches per year" height={190} xTicks={7} color="var(--series-1)" highlightLast />41 <Note className="mt-2">Launches with at least one catalogued object (CelesTrak SATCAT, grouped by COSPAR launch id). The last bar is the current, incomplete year.</Note>42 </ChartBlock>4344 <TwoCol>45 <ChartBlock title="Payloads by launch year" hint="Still active vs retired">46 <StackedBars data={payloadStack} keys={['still_active', 'retired']} labels={{ still_active: 'Still active', retired: 'Retired / decayed' }} title="Payloads by launch year, still active vs retired" height={200} xTicks={7} />47 <Note className="mt-2">Retired = payloads catalogued for that launch year − payloads still active today (derived from the two snapshot series).</Note>48 </ChartBlock>49 <ChartBlock title="Decays per year" hint="All object types">50 <Bars data={decaySeries} title="Objects decayed per year" height={200} xTicks={7} color="var(--series-4)" highlightLast />51 <Note className="mt-2">52 Objects with a published decay date in the SATCAT. Debris-only breakdown on <Link href={routes.debris()} className="text-accent hover:underline">/debris</Link>; recent reentries on{' '}53 <Link href={routes.reentries()} className="text-accent hover:underline">/reentries</Link>.54 </Note>55 </ChartBlock>56 </TwoCol>5758 <ChartBlock title="Satellite growth rate" hint="Last 10 years" derived>59 <table className="data-table stack text-sm">60 <thead>61 <tr>62 <th>Year</th>63 <th className="num">Payloads launched</th>64 <th className="num">Previous year</th>65 <th className="num">YoY change</th>66 <th className="num">Still active</th>67 </tr>68 </thead>69 <tbody>70 {growth.map((g) => (71 <tr key={g.year}>72 <td className="primary mono" data-label="Year">73 {g.year}74 {g.year === currentYear && <span className="ml-2 text-[11px] text-ink-3">year to date</span>}75 </td>76 <td className="num tnum" data-label="Payloads launched">{fmtInt(g.payloads)}</td>77 <td className="num tnum text-ink-3" data-label="Previous year">{g.prev === null ? '—' : fmtInt(g.prev)}</td>78 <td className="num tnum" data-label="YoY change">79 {g.pct === null ? <span className="text-ink-3">—</span> : <span className={g.pct >= 0 ? 'text-active' : 'text-danger'}>{g.pct >= 0 ? '+' : ''}{g.pct.toFixed(1)}%</span>}80 </td>81 <td className="num tnum" data-label="Still active">{fmtInt(g.active)}</td>82 </tr>83 ))}84 </tbody>85 </table>86 <Note className="mt-3">87 <Derived className="mr-2" /> YoY change = (payloads launched in year − payloads launched the previous year) ÷ previous year, computed from the snapshot series. The current year is partial, so its change is not comparable.88 </Note>89 </ChartBlock>90 </div>91 );92}93