feat: catálogo de estrelas embutido (Yale BSC, V<=5.5, 2887 estrelas)
tools/build_catalog.py baixa o bsc5-short.json, converte RA/Dec/V/K e emite starcatalog.js compacto (65KB) com paleta de cores por temperatura (corpo negro dessaturado). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015HtPHsTnDcLVSCiF5FGHZj
This commit is contained in:
parent
0caf1fbbf8
commit
998de4a945
2 changed files with 3010 additions and 0 deletions
2893
plugin/contents/ui/starcatalog.js
Normal file
2893
plugin/contents/ui/starcatalog.js
Normal file
File diff suppressed because it is too large
Load diff
117
tools/build_catalog.py
Normal file
117
tools/build_catalog.py
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Gera plugin/contents/ui/starcatalog.js a partir do Yale Bright Star Catalog.
|
||||
|
||||
Baixa bsc5-short.json (RA/Dec/V/K), filtra V <= MAG_LIMIT, converte a
|
||||
temperatura de cor em RGB (aprox. corpo negro, quantizada numa paleta pequena)
|
||||
e emite um JS compacto:
|
||||
|
||||
const STAR_PALETTE = [[r,g,b], ...];
|
||||
const STAR_DATA = [[raDeg, decDeg, vmag, colorIdx], ...]; // ordenado por mag
|
||||
|
||||
Uso: python3 tools/build_catalog.py
|
||||
"""
|
||||
|
||||
import json
|
||||
import math
|
||||
import re
|
||||
import urllib.request
|
||||
from pathlib import Path
|
||||
|
||||
URL = "https://raw.githubusercontent.com/brettonw/YaleBrightStarCatalog/master/bsc5-short.json"
|
||||
OUT = Path(__file__).resolve().parent.parent / "plugin/contents/ui/starcatalog.js"
|
||||
MAG_LIMIT = 5.5
|
||||
|
||||
# Limites (K) e centro representativo de cada faixa de temperatura
|
||||
TEMP_BINS = [
|
||||
(0, 3500, 3000), # M — alaranjado-avermelhado
|
||||
(3500, 4500, 4000), # K — laranja
|
||||
(4500, 5500, 5000), # G — amarelo-branco
|
||||
(5500, 6500, 6000), # F — branco-amarelado
|
||||
(6500, 8000, 7200), # A — branco
|
||||
(8000, 11000, 9500), # B tardio — branco-azulado
|
||||
(11000, 16000, 13000), # B — azul-branco
|
||||
(16000, 10**9, 20000), # O/B quente — azul
|
||||
]
|
||||
SATURATION = 0.6 # estrelas reais parecem quase brancas; 1.0 = cor plena
|
||||
|
||||
|
||||
def blackbody_rgb(kelvin: float) -> tuple[int, int, int]:
|
||||
"""Aproximação de Tanner Helland (válida ~1000K-40000K)."""
|
||||
t = kelvin / 100.0
|
||||
if t <= 66:
|
||||
r = 255.0
|
||||
g = 99.4708025861 * math.log(t) - 161.1195681661
|
||||
b = 0.0 if t <= 19 else 138.5177312231 * math.log(t - 10) - 305.0447927307
|
||||
else:
|
||||
r = 329.698727446 * (t - 60) ** -0.1332047592
|
||||
g = 288.1221695283 * (t - 60) ** -0.0755148492
|
||||
b = 255.0
|
||||
def clamp(x):
|
||||
return max(0, min(255, round(x)))
|
||||
return clamp(r), clamp(g), clamp(b)
|
||||
|
||||
|
||||
def desaturate(rgb, amount):
|
||||
r, g, b = rgb
|
||||
lum = 0.2126 * r + 0.7152 * g + 0.0722 * b
|
||||
mix = lambda c: round(lum + (c - lum) * amount)
|
||||
return mix(r), mix(g), mix(b)
|
||||
|
||||
|
||||
def parse_ra(s: str) -> float:
|
||||
m = re.match(r"(\d+)h\s*(\d+)m\s*([\d.]+)s", s)
|
||||
h, mi, se = float(m.group(1)), float(m.group(2)), float(m.group(3))
|
||||
return (h + mi / 60 + se / 3600) * 15.0
|
||||
|
||||
|
||||
def parse_dec(s: str) -> float:
|
||||
m = re.match(r"([+-])(\d+)°\s*(\d+)′\s*([\d.]+)″", s)
|
||||
sign = -1.0 if m.group(1) == "-" else 1.0
|
||||
d, mi, se = float(m.group(2)), float(m.group(3)), float(m.group(4))
|
||||
return sign * (d + mi / 60 + se / 3600)
|
||||
|
||||
|
||||
def temp_index(kelvin: float) -> int:
|
||||
for i, (lo, hi, _) in enumerate(TEMP_BINS):
|
||||
if lo <= kelvin < hi:
|
||||
return i
|
||||
return len(TEMP_BINS) - 1
|
||||
|
||||
|
||||
def main():
|
||||
raw = json.loads(urllib.request.urlopen(URL, timeout=30).read())
|
||||
stars = []
|
||||
for entry in raw:
|
||||
if "V" not in entry or "RA" not in entry or "Dec" not in entry:
|
||||
continue
|
||||
vmag = float(entry["V"])
|
||||
if vmag > MAG_LIMIT:
|
||||
continue
|
||||
kelvin = float(entry.get("K", 6000))
|
||||
stars.append((
|
||||
round(parse_ra(entry["RA"]), 2),
|
||||
round(parse_dec(entry["Dec"]), 2),
|
||||
round(vmag, 2),
|
||||
temp_index(kelvin),
|
||||
))
|
||||
|
||||
stars.sort(key=lambda s: s[2]) # mais brilhantes primeiro
|
||||
|
||||
palette = [desaturate(blackbody_rgb(center), SATURATION)
|
||||
for _, _, center in TEMP_BINS]
|
||||
|
||||
rows = ",\n".join(
|
||||
f"[{ra},{dec},{mag},{ci}]" for ra, dec, mag, ci in stars
|
||||
)
|
||||
OUT.write_text(
|
||||
"/* Gerado por tools/build_catalog.py — NÃO editar à mão.\n"
|
||||
f" Yale Bright Star Catalog, V <= {MAG_LIMIT}, {len(stars)} estrelas.\n"
|
||||
" Formato: [raGraus, decGraus, magV, idxPaleta] ordenado por magnitude. */\n"
|
||||
f"const STAR_PALETTE = {json.dumps([list(p) for p in palette])};\n"
|
||||
f"const STAR_DATA = [\n{rows}\n];\n"
|
||||
)
|
||||
print(f"{len(stars)} estrelas -> {OUT} ({OUT.stat().st_size / 1024:.0f} KB)")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Add table
Add a link
Reference in a new issue