Add star-map Angular app, ETL pipeline, and caveman plugin Angular 3D star map (galaxy/system/body views, Three.js rendering, navigation store) plus the NASA ETL tooling that builds the star, exoplanet and solar-system datasets, Playwright e2e suite, and the cs:caveman Claude Code plugin (command, agent, skill). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> @
82 lines
3.6 KiB
TypeScript
82 lines
3.6 KiB
TypeScript
import { writeFileSync } from 'node:fs';
|
|
|
|
import { BodyRecord } from '../../src/app/shared/models/body.model';
|
|
import { SUN_STAR_ID } from '../../src/app/shared/models/star.model';
|
|
import { fetchHorizonsBody } from './lib/horizons';
|
|
import { dataPath, ensureDataDir } from './lib/paths';
|
|
|
|
interface BodySpec {
|
|
id: string;
|
|
name: string;
|
|
kind: BodyRecord['kind'];
|
|
horizonsCommand: string;
|
|
center: string;
|
|
parentBodyId?: string;
|
|
}
|
|
|
|
// Sun-centered planets/dwarf, then their major moons (planetocentric elements).
|
|
const BODY_SPECS: BodySpec[] = [
|
|
{ id: 'mercury', name: 'Mercury', kind: 'planet', horizonsCommand: '199', center: '500@10' },
|
|
{ id: 'venus', name: 'Venus', kind: 'planet', horizonsCommand: '299', center: '500@10' },
|
|
{ id: 'earth', name: 'Earth', kind: 'planet', horizonsCommand: '399', center: '500@10' },
|
|
{ id: 'mars', name: 'Mars', kind: 'planet', horizonsCommand: '499', center: '500@10' },
|
|
{ id: 'jupiter', name: 'Jupiter', kind: 'planet', horizonsCommand: '599', center: '500@10' },
|
|
{ id: 'saturn', name: 'Saturn', kind: 'planet', horizonsCommand: '699', center: '500@10' },
|
|
{ id: 'uranus', name: 'Uranus', kind: 'planet', horizonsCommand: '799', center: '500@10' },
|
|
{ id: 'neptune', name: 'Neptune', kind: 'planet', horizonsCommand: '899', center: '500@10' },
|
|
{ id: 'pluto', name: 'Pluto', kind: 'dwarf', horizonsCommand: '999', center: '500@10' },
|
|
{ id: 'moon', name: 'Moon', kind: 'moon', horizonsCommand: '301', center: '500@399', parentBodyId: 'earth' },
|
|
{ id: 'phobos', name: 'Phobos', kind: 'moon', horizonsCommand: '401', center: '500@499', parentBodyId: 'mars' },
|
|
{ id: 'deimos', name: 'Deimos', kind: 'moon', horizonsCommand: '402', center: '500@499', parentBodyId: 'mars' },
|
|
{ id: 'io', name: 'Io', kind: 'moon', horizonsCommand: '501', center: '500@599', parentBodyId: 'jupiter' },
|
|
{ id: 'europa', name: 'Europa', kind: 'moon', horizonsCommand: '502', center: '500@599', parentBodyId: 'jupiter' },
|
|
{ id: 'ganymede', name: 'Ganymede', kind: 'moon', horizonsCommand: '503', center: '500@599', parentBodyId: 'jupiter' },
|
|
{ id: 'callisto', name: 'Callisto', kind: 'moon', horizonsCommand: '504', center: '500@599', parentBodyId: 'jupiter' },
|
|
{ id: 'titan', name: 'Titan', kind: 'moon', horizonsCommand: '606', center: '500@699', parentBodyId: 'saturn' },
|
|
{ id: 'triton', name: 'Triton', kind: 'moon', horizonsCommand: '801', center: '500@899', parentBodyId: 'neptune' }
|
|
];
|
|
|
|
/**
|
|
* Queries JPL Horizons for the osculating orbital elements (and mean radius, where
|
|
* reported) of the major planets, Pluto, and a curated set of major moons, and writes
|
|
* `bodies.json`.
|
|
*/
|
|
export async function fetchSolarSystem(): Promise<BodyRecord[]> {
|
|
console.log(`Fetching ${BODY_SPECS.length} solar-system bodies from JPL Horizons...`);
|
|
const bodies: BodyRecord[] = [];
|
|
|
|
for (const spec of BODY_SPECS) {
|
|
const result = await fetchHorizonsBody({
|
|
command: spec.horizonsCommand,
|
|
center: spec.center,
|
|
cacheKey: `horizons-${spec.id}.txt`
|
|
});
|
|
|
|
if (result.radiusKm === undefined) {
|
|
console.warn(` no physical radius found for ${spec.name}; defaulting to 0.`);
|
|
}
|
|
|
|
bodies.push({
|
|
id: spec.id,
|
|
systemStarId: SUN_STAR_ID,
|
|
name: spec.name,
|
|
kind: spec.kind,
|
|
radiusKm: result.radiusKm ?? 0,
|
|
orbit: result.orbit,
|
|
...(spec.parentBodyId ? { parentBodyId: spec.parentBodyId } : {})
|
|
});
|
|
}
|
|
|
|
ensureDataDir();
|
|
writeFileSync(dataPath('bodies.json'), JSON.stringify(bodies, null, 2));
|
|
console.log(` wrote ${bodies.length} bodies.`);
|
|
return bodies;
|
|
}
|
|
|
|
if (require.main === module) {
|
|
fetchSolarSystem().catch((error) => {
|
|
console.error(error);
|
|
process.exitCode = 1;
|
|
});
|
|
}
|