Simuler un ciel étoilé avec Python et Skyfield !
Skyfield est une librairie Python pour tous les passionnés d’astronomie. Elle permet de positionner n’importe quel objet céleste dans l’espace, de suivre les comètes et les planètes, de calculer les phases de la Lune et même de tenir compte de la déformation du ciel en fonction de la température. C’est dingue non ?
Je me suis alors mis en tête de reproduire la cartographie 3D d’un ciel étoilé, à la façon de l’excellente application Stellarium. Voici le résultat :

Les sources de données
Première étape : avoir de bonnes données. Voici les trois sources que j’ai utilisées :
- Pour les étoiles, le catalogue Hipparcos terminé en 1996 fournit la position de 120 000 étoiles et leurs mouvements. Oui, 30 ans de données — mais elles sont encore valides aujourd’hui et restent la référence pour l’observation depuis la Terre. Des catalogues plus complets existent (Tycho-1), mais on n’en a pas besoin ici.
- Pour les planètes, on parle d’éphémérides. Un fichier
de421.bspsuffit pour consulter leur position dans le système solaire. - Pour les constellations, c’est un tableau de relations entre étoiles. Je me suis basé directement sur le fichier fourni par Stellarium.
Le code pour récupérer toutes ces données est au final très simple :
from skyfield.data import hipparcos, stellarium
CONSTELLATION_URL = (
"https://raw.githubusercontent.com/Stellarium/stellarium/"
"eb47095a9282cf6b981f6e37fe1ea3a3ae0fd167/"
"skycultures/modern_st/constellationship.fab"
)
def load_data():
# load celestial data
# de421 shows position of earth and sun in space, this is a file in local
eph = load("de421.bsp")
# hipparcos dataset contains star location data
with load.open(hipparcos.URL) as f:
stars = hipparcos.load_dataframe(f)
with load.open(CONSTELLATION_URL) as f:
constellations = stellarium.parse_constellations(f)
return eph, stars, constellations
Ces fichiers contiennent beaucoup de données. Voici un aperçu de ce que donne le catalogue d’étoiles :
| hip | magnitude | ra_degrees | dec_degrees | parallax_mas | ra_mas_per_year | dec_mas_per_year | ra_hours | epoch_year |
|---:|---:|---:|---:|---:|---:|---:|---:|---:|
| 1 | 9.10 | 0.000912 | 1.089013 | 3.54 | -5.20 | -1.88 | 0.000061 | 1991.25 |
| 2 | 9.27 | 0.003797 | -19.498837 | 21.90 | 181.21 | -0.93 | 0.000253 | 1991.25 |
| 3 | 6.61 | 0.005008 | 38.859286 | 2.81 | 5.24 | -2.91 | 0.000334 | 1991.25 |
| 4 | 8.06 | 0.008382 | -51.893546 | 7.75 | 62.85 | 0.16 | 0.000559 | 1991.25 |
| 5 | 8.55 | 0.009965 | -40.591224 | 2.87 | 2.53 | 9.07 | 0.000664 | 1991.25 |
Pas d’inquiétude si c’est du charabia, retenez juste l’essentiel :
- magnitude : plus la valeur est élevée, moins l’étoile brille (9 = presque invisible, 3 = bien visible !)
- ra_degrees (Right Ascension) : position horizontale de l’étoile par rapport au centre de la Terre, en partant de la droite — négatif = vers la gauche.
- dec_degrees (Déclinaison) : position verticale par rapport au centre de la Terre — négatif = vers le bas.
Le calcul des positions
Alors là, on rentre dans les maths. À nous les cosinus, les calculs de vecteurs… sauf qu’en pratique, Skyfield fait pratiquement tout le travail à notre place.
L’objectif est de convertir la position des étoiles en fonction d’une date et d’un point précis sur le globe — c’est ce qu’on appelle un Observateur Topocentrique :
En résumé :
- Right Ascension & Déclinaison : coordonnées par rapport au centre de la Terre.
- Altitude & Azimuth : coordonnées par rapport à un point précis sur le globe (le vôtre !).
# Getted from Ephemeride
earth = eph["earth"]
# define observation time from our UTC datetime
ts = load.timescale()
t = ts.from_datetime(utc_dt)
# Topocentric observator (Earth ground)
observer = earth + wgs84.latlon(latitude_degrees=lat, longitude_degrees=long)
# Observe stars table from our Observer object
star_vectors = observer.at(t).observe(Star.from_dataframe(stars)).apparent()
# Convert Right Ascension & Declinaison into Altitude and Azimuth
alt, az, _ = star_vectors.altaz()
data = stars.copy()
data["alt"] = alt.degrees
data["az"] = az.degrees
Petit avertissement pour les curieux : Skyfield ne supporte que la Terre comme point d’observation, pas question de simuler le ciel depuis Mars :D !
Convertir nos étoiles en coordonnées cartésiennes
On commence par filtrer les étoiles selon la magnitude souhaitée. L’œil humain perçoit jusqu’à une magnitude d’environ 5, selon la pollution lumineuse de l’endroit.
Ensuite, on convertit l’Altitude et l’Azimuth en coordonnées cartésiennes pour placer chaque étoile sur un dôme virtuel.
# Filtre: étoiles visibles à l'œil nu et au-dessus de l'horizon
data = data[
(data["alt"] > 0) & data["magnitude"].notna() & (data["magnitude"] <= MAGNITUDE)
].copy()
# Conversion sphérique -> cartésien (dôme)
alt_r = np.radians(data["alt"].to_numpy())
az_r = np.radians(data["az"].to_numpy())
data["x"] = np.cos(alt_r) * np.sin(az_r) # Est +
data["y"] = np.sin(alt_r) # Zénith +
data["z"] = np.cos(alt_r) * np.cos(az_r) # Nord +
Une fois ces coordonnées prêtes, il suffit de les passer à ThreeJS — je te laisse fouiller le GitHub pour cette partie ! :)
Attention cependant : le repère cartésien calculé ici est celui d’un observateur extérieur au dôme. Comme on veut le voir de l’intérieur, il faut appliquer une symétrie sur la coordonnée
x.
Vers l’infini, et au-delà !
Il y a encore plein de choses à explorer :
- Afficher le nom des principales étoiles et des constellations
- Ajouter les planètes du système solaire (de minuscules points, à peine visibles à l’œil nu)
- Intégrer les Deep Sky Objects — galaxies et nébuleuses observables aux jumelles ou au télescope
- Placer la Voie lactée et la Lune pour une immersion totale
Le code source est disponible sur GitHub : Skyfield-Observatory