List stars in search and routes by the type their colour gives them, and say where archive positions come from
5333be6gave the star card an estimated type for the 383 695 stars the ETL files as "Unknown", but search rows, the route search index and the current-star route option still printed that literal: TRAPPIST-1 read "STAR · UNKNOWN" in search beside "Spectral type ~M8, from colour" on its card, audit #17's own symptom. spectralClassification (spectral.ts) now gives the catalogue's type, else the colour's marked "~", else nothing, and all three and the card's subtitle use it; a search row with nothing to add prints its kind alone rather than a trailing " · ". The neighbourhood note still read "Positions from measured parallaxes" after4c8e4a0placed 3 277 stars at the Exoplanet Archive's sy_dist, 343 of them without a usable parallax and 281 of those past 1 kpc — microlensing hosts such as OGLE-2005-BLG-390L at 6.6 kpc, from a lensing model. positionsNote now says so, with the count. Neither the note nor the census subtitle (audit #16) had a test: putting back "Hipparcos · Yale Bright Star · Gliese" passed all 775. In the app on :4302: the note reads "Positions from measured parallaxes, and for the 3,277 planet hosts only the NASA Exoplanet Archive places, from its distances. Grid marks the galactic plane through the Sun."; search reads "TRAPPIST-1 STAR · ~M8", "OGLE-2005-BLG-390L STAR", "Sirius STAR · A0M"; TRAPPIST-1's route option has subtitle "~M8". The scene spec gains an archive-placed fixture star. Controls, each failing its named test: the search row printing the raw type, keeping the separator with nothing after it, the route index and the current-star option printing the raw type, the note back to parallaxes only, the subtitle back to the old catalogue list (1 of 797 each), the note never counting the archive (2 of 797), and the classification without its estimate (3 of 797). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -14,6 +14,7 @@ import { LinkBudget } from '../../shared/astro/jump-links';
|
|||||||
import { HudDisplay } from '../hud/hud-dock.component';
|
import { HudDisplay } from '../hud/hud-dock.component';
|
||||||
import { GalaxySystemSceneComponent } from './galaxy-system-scene.component';
|
import { GalaxySystemSceneComponent } from './galaxy-system-scene.component';
|
||||||
import { galacticNormal } from './grid-plane';
|
import { galacticNormal } from './grid-plane';
|
||||||
|
import { catalogueCensus, positionsNote } from './star-readouts';
|
||||||
import { closestApproachAu, SUN_RADIUS_AU } from './system-framing';
|
import { closestApproachAu, SUN_RADIUS_AU } from './system-framing';
|
||||||
import { blackbodyColor, SOLAR_EFFECTIVE_TEMPERATURE_K } from '../../shared/astro/stellar';
|
import { blackbodyColor, SOLAR_EFFECTIVE_TEMPERATURE_K } from '../../shared/astro/stellar';
|
||||||
import { JumpLinkRenderer } from './jump-link-renderer';
|
import { JumpLinkRenderer } from './jump-link-renderer';
|
||||||
@@ -38,7 +39,10 @@ const PROXIMA: StarRecord = { id: 42, name: 'Proxima Centauri', x: 0, y: 1.3, z:
|
|||||||
const ANTARES: StarRecord = { id: 80519, name: 'Antares', x: -58.54, y: -140.31, z: -75.57, magnitude: 1.06, magnitudeBand: 'V', spectralType: 'M1Ib + B2.5V', colorIndex: 1.865, colorSystem: 'B-V' };
|
const ANTARES: StarRecord = { id: 80519, name: 'Antares', x: -58.54, y: -140.31, z: -75.57, magnitude: 1.06, magnitudeBand: 'V', spectralType: 'M1Ib + B2.5V', colorIndex: 1.865, colorSystem: 'B-V' };
|
||||||
const PROCYON_B: StarRecord = { id: 37279, name: 'Gl 280B', x: -1.08, y: 3.19, z: 0.34, magnitude: 10.7, magnitudeBand: 'V', spectralType: 'DA', colorIndex: null };
|
const PROCYON_B: StarRecord = { id: 37279, name: 'Gl 280B', x: -1.08, y: 3.19, z: 0.34, magnitude: 10.7, magnitudeBand: 'V', spectralType: 'DA', colorIndex: null };
|
||||||
|
|
||||||
const STARS: StarRecord[] = [SUN, ALPHA_CENTAURI, PROXIMA, ANTARES, PROCYON_B];
|
// A host only the archive places, at the stand-in magnitude, with no type and no colour.
|
||||||
|
const LENS: StarRecord = { id: 1070000536, name: 'KMT-2016-BLG-1107L', x: 6651, y: 0, z: 0, magnitude: 15, spectralType: 'Unknown', colorIndex: null, source: 'exoplanet-archive' };
|
||||||
|
|
||||||
|
const STARS: StarRecord[] = [SUN, ALPHA_CENTAURI, PROXIMA, ANTARES, PROCYON_B, LENS];
|
||||||
const STAR_POSITIONS = new Float32Array(STARS.flatMap((star) => [star.x, star.y, star.z]));
|
const STAR_POSITIONS = new Float32Array(STARS.flatMap((star) => [star.x, star.y, star.z]));
|
||||||
|
|
||||||
// Proxima's planet as the archive gives it, with its host's radius, temperature and luminosity.
|
// Proxima's planet as the archive gives it, with its host's radius, temperature and luminosity.
|
||||||
@@ -290,7 +294,7 @@ describe('GalaxySystemSceneComponent camera-flight transitions', () => {
|
|||||||
const [focus] = refocus.mock.calls[0];
|
const [focus] = refocus.mock.calls[0];
|
||||||
expect(focus.view).toBeDefined();
|
expect(focus.view).toBeDefined();
|
||||||
// The Sun has Earth and Proxima its b, so both are hosts; the others have nothing catalogued.
|
// The Sun has Earth and Proxima its b, so both are hosts; the others have nothing catalogued.
|
||||||
expect(Array.from(focus.hosts ?? [])).toEqual([1, 0, 1, 0, 0]);
|
expect(Array.from(focus.hosts ?? [])).toEqual([1, 0, 1, 0, 0, 0]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('chooses again once the camera has turned half the margin, and not for less', async () => {
|
it('chooses again once the camera has turned half the margin, and not for less', async () => {
|
||||||
@@ -951,6 +955,24 @@ describe('GalaxySystemSceneComponent camera-flight transitions', () => {
|
|||||||
expect(navigationStore.viewLevel()).toBe('galactic');
|
expect(navigationStore.viewLevel()).toBe('galactic');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('names what the neighbourhood holds, and where the positions of the stars in it come from', async () => {
|
||||||
|
await advanceFrames(engine, 0.3);
|
||||||
|
const component = fixture.componentInstance as unknown as { hudSubtitle(): string; hudNote(): string };
|
||||||
|
expect(component.hudSubtitle()).toBe(catalogueCensus(STARS));
|
||||||
|
expect(component.hudNote()).toBe(positionsNote(STARS));
|
||||||
|
expect(component.hudNote()).toContain('1 planet hosts only the NASA Exoplanet Archive places, from its distances');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('offers a star for a route by its classification, and a star with none by its name alone', () => {
|
||||||
|
const component = fixture.componentInstance as unknown as { starSearchIndex(): { entry: { starId?: number; subtitle: string } }[]; currentStarOption(): { subtitle: string } | null };
|
||||||
|
expect(component.starSearchIndex().find(({ entry }) => entry.starId === LENS.id)?.entry.subtitle).toBe('');
|
||||||
|
expect(component.starSearchIndex().find(({ entry }) => entry.starId === PROXIMA.id)?.entry.subtitle).toBe('M5V');
|
||||||
|
navigationStore.selectStar(LENS.id);
|
||||||
|
expect(component.currentStarOption()?.subtitle).toBe('');
|
||||||
|
navigationStore.selectStar(PROXIMA.id);
|
||||||
|
expect(component.currentStarOption()?.subtitle).toBe('M5V');
|
||||||
|
});
|
||||||
|
|
||||||
describe('each star at its own size and in its own colour', () => {
|
describe('each star at its own size and in its own colour', () => {
|
||||||
type DrawnStar = {
|
type DrawnStar = {
|
||||||
starMarkerGeometry: THREE.SphereGeometry;
|
starMarkerGeometry: THREE.SphereGeometry;
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import {
|
|||||||
galacticCentrePositionPc,
|
galacticCentrePositionPc,
|
||||||
galacticToEquatorial,
|
galacticToEquatorial,
|
||||||
} from '../../shared/astro/galaxy';
|
} from '../../shared/astro/galaxy';
|
||||||
|
import { spectralClassification } from '../../shared/astro/spectral';
|
||||||
import { blackbodyColor } from '../../shared/astro/stellar';
|
import { blackbodyColor } from '../../shared/astro/stellar';
|
||||||
import { DataLoaderService } from '../../core/data/data-loader.service';
|
import { DataLoaderService } from '../../core/data/data-loader.service';
|
||||||
import { EngineService, SceneCamera } from '../../core/engine/engine.service';
|
import { EngineService, SceneCamera } from '../../core/engine/engine.service';
|
||||||
@@ -84,7 +85,7 @@ import { JumpLinkRenderer } from './jump-link-renderer';
|
|||||||
import { ReservedBox, ringPlacement } from './label-ring';
|
import { ReservedBox, ringPlacement } from './label-ring';
|
||||||
import { LabeledPoint, LabelSide, StarLabelOverlay } from './star-label-overlay';
|
import { LabeledPoint, LabelSide, StarLabelOverlay } from './star-label-overlay';
|
||||||
import { SystemOrbitsRenderer } from './system-orbits-renderer';
|
import { SystemOrbitsRenderer } from './system-orbits-renderer';
|
||||||
import { catalogueCensus, starReadouts, starSubtitle } from './star-readouts';
|
import { catalogueCensus, positionsNote, starReadouts, starSubtitle } from './star-readouts';
|
||||||
|
|
||||||
/** Radius, in CSS pixels, below which a body in the system view is scaled up to be seen at all. */
|
/** Radius, in CSS pixels, below which a body in the system view is scaled up to be seen at all. */
|
||||||
const MIN_MARKER_PIXELS = 3;
|
const MIN_MARKER_PIXELS = 3;
|
||||||
@@ -572,6 +573,7 @@ export class GalaxySystemSceneComponent implements AfterViewInit, OnDestroy {
|
|||||||
private stars: readonly StarRecord[] = [];
|
private stars: readonly StarRecord[] = [];
|
||||||
/** The neighbourhood's subtitle: what the catalogue holds, by the catalogue describing it. */
|
/** The neighbourhood's subtitle: what the catalogue holds, by the catalogue describing it. */
|
||||||
private catalogueCensus = '';
|
private catalogueCensus = '';
|
||||||
|
private positionsNote = '';
|
||||||
private starsById = new Map<number, StarRecord>();
|
private starsById = new Map<number, StarRecord>();
|
||||||
private bodies: readonly BodyRecord[] = [];
|
private bodies: readonly BodyRecord[] = [];
|
||||||
private exoplanets: readonly ExoplanetRecord[] = [];
|
private exoplanets: readonly ExoplanetRecord[] = [];
|
||||||
@@ -721,6 +723,7 @@ export class GalaxySystemSceneComponent implements AfterViewInit, OnDestroy {
|
|||||||
]);
|
]);
|
||||||
this.stars = stars;
|
this.stars = stars;
|
||||||
this.catalogueCensus = catalogueCensus(stars);
|
this.catalogueCensus = catalogueCensus(stars);
|
||||||
|
this.positionsNote = positionsNote(stars);
|
||||||
this.starsById = new Map(stars.map((star) => [star.id, star]));
|
this.starsById = new Map(stars.map((star) => [star.id, star]));
|
||||||
this.neighbourhood = new StarNeighbourhood(stars);
|
this.neighbourhood = new StarNeighbourhood(stars);
|
||||||
this.routing = new RoutingClient(stars, positions, this.neighbourhood);
|
this.routing = new RoutingClient(stars, positions, this.neighbourhood);
|
||||||
@@ -730,7 +733,7 @@ export class GalaxySystemSceneComponent implements AfterViewInit, OnDestroy {
|
|||||||
stars.map((star) => ({
|
stars.map((star) => ({
|
||||||
kind: 'star' as const,
|
kind: 'star' as const,
|
||||||
name: star.name,
|
name: star.name,
|
||||||
subtitle: star.spectralType,
|
subtitle: spectralClassification(star),
|
||||||
starId: star.id,
|
starId: star.id,
|
||||||
})),
|
})),
|
||||||
),
|
),
|
||||||
@@ -1848,9 +1851,7 @@ export class GalaxySystemSceneComponent implements AfterViewInit, OnDestroy {
|
|||||||
// The one thing the field itself cannot show: which of those points can be flown into.
|
// The one thing the field itself cannot show: which of those points can be flown into.
|
||||||
{ label: 'Systems', value: `${this.enterableSystems}` },
|
{ label: 'Systems', value: `${this.enterableSystems}` },
|
||||||
]);
|
]);
|
||||||
this.hudNote.set(
|
this.hudNote.set(this.positionsNote);
|
||||||
'Positions from measured parallaxes. Grid marks the galactic plane through the Sun.',
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Where the current press started, so a drag can be told apart from a click. */
|
/** Where the current press started, so a drag can be told apart from a click. */
|
||||||
@@ -1959,7 +1960,7 @@ export class GalaxySystemSceneComponent implements AfterViewInit, OnDestroy {
|
|||||||
readonly currentStarOption = computed<RouteStarOption | null>(() => {
|
readonly currentStarOption = computed<RouteStarOption | null>(() => {
|
||||||
const starId = this.navigationStore.selectedStarId();
|
const starId = this.navigationStore.selectedStarId();
|
||||||
const star = starId === null ? undefined : this.starsById.get(starId);
|
const star = starId === null ? undefined : this.starsById.get(starId);
|
||||||
return star ? { id: star.id, name: star.name, subtitle: star.spectralType } : null;
|
return star ? { id: star.id, name: star.name, subtitle: spectralClassification(star) } : null;
|
||||||
});
|
});
|
||||||
|
|
||||||
onRouteQuery(query: string): void {
|
onRouteQuery(query: string): void {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { describe, expect, it } from 'vitest';
|
import { describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
import { StarRecord } from '../../shared/models/star.model';
|
import { StarRecord } from '../../shared/models/star.model';
|
||||||
import { catalogueCensus, describingCatalogue, starReadouts, starSubtitle } from './star-readouts';
|
import { catalogueCensus, describingCatalogue, positionsNote, starReadouts, starSubtitle } from './star-readouts';
|
||||||
|
|
||||||
/** Three kinds of star the catalogue holds, each as the decoder gives it back. */
|
/** Three kinds of star the catalogue holds, each as the decoder gives it back. */
|
||||||
const HYG_STAR: StarRecord = {
|
const HYG_STAR: StarRecord = {
|
||||||
@@ -94,6 +94,15 @@ describe('starReadouts', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('positionsNote', () => {
|
||||||
|
it("says which positions are the archive's distances rather than parallaxes, and how many", () => {
|
||||||
|
expect(positionsNote([HYG_STAR, GAIA_STAR])).toBe('Positions from measured parallaxes. Grid marks the galactic plane through the Sun.');
|
||||||
|
expect(positionsNote([HYG_STAR, { ...GAIA_STAR, source: 'exoplanet-archive' }])).toBe(
|
||||||
|
'Positions from measured parallaxes, and for the 1 planet hosts only the NASA Exoplanet Archive places, from its distances. Grid marks the galactic plane through the Sun.'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('starSubtitle', () => {
|
describe('starSubtitle', () => {
|
||||||
it("prints the catalogue's classification where it has one", () => {
|
it("prints the catalogue's classification where it has one", () => {
|
||||||
expect(starSubtitle(HYG_STAR)).toBe('Spectral type A0m');
|
expect(starSubtitle(HYG_STAR)).toBe('Spectral type A0m');
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { spectralTypeFromColor } from '../../shared/astro/spectral';
|
import { spectralClassification } from '../../shared/astro/spectral';
|
||||||
import { formatDistance, formatLuminosity } from '../../shared/format/quantity';
|
import { formatDistance, formatLuminosity } from '../../shared/format/quantity';
|
||||||
import { StarRecord } from '../../shared/models/star.model';
|
import { StarRecord } from '../../shared/models/star.model';
|
||||||
import { StarSurface } from '../body-detail/body-view-model';
|
import { StarSurface } from '../body-detail/body-view-model';
|
||||||
@@ -24,11 +24,8 @@ export function describingCatalogue(star: StarRecord): string {
|
|||||||
* estimate. Empty with neither, rather than the ETL's literal "Unknown".
|
* estimate. Empty with neither, rather than the ETL's literal "Unknown".
|
||||||
*/
|
*/
|
||||||
export function starSubtitle(star: StarRecord): string {
|
export function starSubtitle(star: StarRecord): string {
|
||||||
if (star.spectralType && star.spectralType !== 'Unknown') {
|
const classification = spectralClassification(star);
|
||||||
return `Spectral type ${star.spectralType}`;
|
return !classification ? '' : `Spectral type ${classification}${classification.startsWith('~') ? ', from colour' : ''}`;
|
||||||
}
|
|
||||||
const estimate = spectralTypeFromColor(star.colorIndex, star.colorSystem);
|
|
||||||
return estimate ? `Spectral type ~${estimate}, from colour` : '';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -66,6 +63,20 @@ function radiusReadout(radiusSolar: number, derived: boolean): HudReadout {
|
|||||||
: { label: 'Radius', value: `${figure} solar radii` };
|
: { label: 'Radius', value: `${figure} solar radii` };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Where the neighbourhood's positions come from: parallaxes, except for the stars only the
|
||||||
|
* Exoplanet Archive places, which sit at its own distances — a lensing model's for the
|
||||||
|
* microlensing hosts among them, OGLE-2005-BLG-390L's 6.6 kpc for one, with no parallax behind it.
|
||||||
|
*/
|
||||||
|
export function positionsNote(stars: readonly StarRecord[]): string {
|
||||||
|
const archive = stars.filter((star) => star.source === 'exoplanet-archive').length;
|
||||||
|
const where =
|
||||||
|
archive === 0
|
||||||
|
? 'Positions from measured parallaxes.'
|
||||||
|
: `Positions from measured parallaxes, and for the ${archive.toLocaleString('en-GB')} planet hosts only the NASA Exoplanet Archive places, from its distances.`;
|
||||||
|
return `${where} Grid marks the galactic plane through the Sun.`;
|
||||||
|
}
|
||||||
|
|
||||||
/** What the catalogue holds, counted by the catalogue describing each star, largest first. */
|
/** What the catalogue holds, counted by the catalogue describing each star, largest first. */
|
||||||
export function catalogueCensus(stars: readonly StarRecord[]): string {
|
export function catalogueCensus(stars: readonly StarRecord[]): string {
|
||||||
const counts = new Map<string, number>();
|
const counts = new Map<string, number>();
|
||||||
|
|||||||
@@ -16,7 +16,10 @@ function starRecord(id: number, name: string): StarRecord {
|
|||||||
/** Enough "Iot ..." stars to fill the result list ahead of the moon Io, as the real index does. */
|
/** Enough "Iot ..." stars to fill the result list ahead of the moon Io, as the real index does. */
|
||||||
const STARS: StarRecord[] = [
|
const STARS: StarRecord[] = [
|
||||||
...Array.from({ length: 12 }, (_, i) => starRecord(100 + i, `Iot Star ${i}`)),
|
...Array.from({ length: 12 }, (_, i) => starRecord(100 + i, `Iot Star ${i}`)),
|
||||||
starRecord(1, 'Proxima Centauri')
|
starRecord(1, 'Proxima Centauri'),
|
||||||
|
// As the ETL files a Gaia star: no type, a BP−RP colour; and a star with neither.
|
||||||
|
{ ...starRecord(2, 'TRAPPIST-1'), spectralType: 'Unknown', colorIndex: 4.902, colorSystem: 'BP-RP' },
|
||||||
|
{ ...starRecord(3, 'KMT-2016-BLG-1107L'), spectralType: 'Unknown', colorIndex: null }
|
||||||
];
|
];
|
||||||
|
|
||||||
const IO: BodyRecord = {
|
const IO: BodyRecord = {
|
||||||
@@ -109,6 +112,14 @@ describe('SearchComponent', () => {
|
|||||||
expect(resultNames()).toContain('Proxima Cen b');
|
expect(resultNames()).toContain('Proxima Cen b');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("lists a star by the type its colour gives it where it has none, and never as \"Unknown\"", async () => {
|
||||||
|
const kindLine = (): string => (element.querySelector('[data-testid="search-results"] button span:last-child')?.textContent ?? '').trim();
|
||||||
|
await type('TRAPPIST-1');
|
||||||
|
expect(kindLine()).toBe('Star · ~M8');
|
||||||
|
await type('KMT-2016-BLG-1107L');
|
||||||
|
expect(kindLine()).toBe('Star');
|
||||||
|
});
|
||||||
|
|
||||||
it('shows nothing for a query that matches nothing', async () => {
|
it('shows nothing for a query that matches nothing', async () => {
|
||||||
await type('zzzzz');
|
await type('zzzzz');
|
||||||
expect(element.querySelector('[data-testid="search-results"]')).toBeNull();
|
expect(element.querySelector('[data-testid="search-results"]')).toBeNull();
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { Component, computed, ElementRef, output, signal, viewChild } from '@ang
|
|||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
import { DataLoaderService } from '../../core/data/data-loader.service';
|
import { DataLoaderService } from '../../core/data/data-loader.service';
|
||||||
|
import { spectralClassification } from '../../shared/astro/spectral';
|
||||||
import { NavigationStore } from '../../shared/state/navigation.store';
|
import { NavigationStore } from '../../shared/state/navigation.store';
|
||||||
import { ReticleIconComponent } from '../../shared/ui/reticle-icon.component';
|
import { ReticleIconComponent } from '../../shared/ui/reticle-icon.component';
|
||||||
import { buildSearchIndex, IndexedSearchEntry, rankSearchResults, SearchEntry, SearchResultKind } from './search-ranking';
|
import { buildSearchIndex, IndexedSearchEntry, rankSearchResults, SearchEntry, SearchResultKind } from './search-ranking';
|
||||||
@@ -66,7 +67,7 @@ const KIND_LABELS: Record<SearchResultKind, string> = {
|
|||||||
class="flex w-full items-baseline gap-3 border-l border-transparent px-3 py-2 text-left transition-colors hover:border-l-accent hover:bg-accent/8 focus-visible:border-l-accent focus-visible:bg-accent/12 focus-visible:outline-1 focus-visible:-outline-offset-1 focus-visible:outline-accent"
|
class="flex w-full items-baseline gap-3 border-l border-transparent px-3 py-2 text-left transition-colors hover:border-l-accent hover:bg-accent/8 focus-visible:border-l-accent focus-visible:bg-accent/12 focus-visible:outline-1 focus-visible:-outline-offset-1 focus-visible:outline-accent"
|
||||||
>
|
>
|
||||||
<span class="min-w-0 flex-1 truncate text-sm text-text">{{ result.name }}</span>
|
<span class="min-w-0 flex-1 truncate text-sm text-text">{{ result.name }}</span>
|
||||||
<span class="type-label max-w-[45%] shrink-0 truncate text-muted">{{ kindLabel(result.kind) }} · {{ result.subtitle }}</span>
|
<span class="type-label max-w-[45%] shrink-0 truncate text-muted">{{ kindLabel(result.kind) }}@if (result.subtitle) { · {{ result.subtitle }}}</span>
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
@@ -153,7 +154,7 @@ export class SearchComponent {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
const entries: SearchEntry[] = [
|
const entries: SearchEntry[] = [
|
||||||
...stars.map((star): SearchEntry => ({ kind: 'star', name: star.name, subtitle: star.spectralType, starId: star.id })),
|
...stars.map((star): SearchEntry => ({ kind: 'star', name: star.name, subtitle: spectralClassification(star), starId: star.id })),
|
||||||
...bodies.map((body): SearchEntry => ({ kind: 'body', name: body.name, subtitle: body.kind, bodyId: body.id })),
|
...bodies.map((body): SearchEntry => ({ kind: 'body', name: body.name, subtitle: body.kind, bodyId: body.id })),
|
||||||
...exoplanets.map((exoplanet): SearchEntry => ({ kind: 'exoplanet', name: exoplanet.name, subtitle: exoplanet.hostStarName, bodyId: exoplanet.id }))
|
...exoplanets.map((exoplanet): SearchEntry => ({ kind: 'exoplanet', name: exoplanet.name, subtitle: exoplanet.hostStarName, bodyId: exoplanet.id }))
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { describe, expect, it } from 'vitest';
|
import { describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
import { dwarfSequenceAtColor, isGiant, parseSpectralClass, SPECTRAL_CLASSES, spectralTypeFromColor, spectralTypeToColorIndex, temperatureToColorIndex } from './spectral';
|
import { dwarfSequenceAtColor, isGiant, parseSpectralClass, spectralClassification, SPECTRAL_CLASSES, spectralTypeFromColor, spectralTypeToColorIndex, temperatureToColorIndex } from './spectral';
|
||||||
|
|
||||||
describe('parseSpectralClass', () => {
|
describe('parseSpectralClass', () => {
|
||||||
it('reads a clean class and subclass', () => {
|
it('reads a clean class and subclass', () => {
|
||||||
@@ -143,6 +143,14 @@ describe('spectralTypeFromColor', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('spectralClassification', () => {
|
||||||
|
it("gives the catalogue's type, else the colour's marked as an estimate, else nothing", () => {
|
||||||
|
expect(spectralClassification({ spectralType: 'M5Ve', colorIndex: 1.807, colorSystem: 'B-V' })).toBe('M5Ve');
|
||||||
|
expect(spectralClassification({ spectralType: 'Unknown', colorIndex: 4.902, colorSystem: 'BP-RP' })).toBe('~M8');
|
||||||
|
expect(spectralClassification({ spectralType: 'Unknown', colorIndex: null })).toBe('');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('dwarfSequenceAtColor', () => {
|
describe('dwarfSequenceAtColor', () => {
|
||||||
it("puts the Sun's colour in either system at the Sun's temperature and correction", () => {
|
it("puts the Sun's colour in either system at the Sun's temperature and correction", () => {
|
||||||
for (const [colour, system] of [[0.65, 'B-V'], [0.823, 'BP-RP']] as const) {
|
for (const [colour, system] of [[0.65, 'B-V'], [0.823, 'BP-RP']] as const) {
|
||||||
|
|||||||
@@ -179,6 +179,19 @@ export function spectralTypeFromColor(colorIndex: number | null, system: 'B-V' |
|
|||||||
return nearest[0];
|
return nearest[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A star's classification as a row or an option lists it: the catalogue's type, or the dwarf type
|
||||||
|
* its colour matches, marked `~` as an estimate; empty with neither, rather than the ETL's literal
|
||||||
|
* "Unknown", which 383 695 stars carry and search rows and route options used to print.
|
||||||
|
*/
|
||||||
|
export function spectralClassification(star: { spectralType: string; colorIndex: number | null; colorSystem?: 'B-V' | 'BP-RP' }): string {
|
||||||
|
if (star.spectralType && star.spectralType !== 'Unknown') {
|
||||||
|
return star.spectralType;
|
||||||
|
}
|
||||||
|
const estimate = spectralTypeFromColor(star.colorIndex, star.colorSystem);
|
||||||
|
return estimate ? `~${estimate}` : '';
|
||||||
|
}
|
||||||
|
|
||||||
/** What the dwarf sequence says of a star of a given colour. */
|
/** What the dwarf sequence says of a star of a given colour. */
|
||||||
export interface DwarfSequencePoint {
|
export interface DwarfSequencePoint {
|
||||||
/** B−V, the colour in the other system's terms where it was read off BP−RP. */
|
/** B−V, the colour in the other system's terms where it was read off BP−RP. */
|
||||||
|
|||||||
Reference in New Issue
Block a user