Ciel observé
Rigel – étape 7
1 Introduction
Le but principal de cette étape est d'écrire une classe représentant le contenu du ciel à un instant donné, projeté dans le plan par une projection stéréographique. Son but secondaire est d'écrire une méthode permettant de faire correspondre la température d'un corps noir à sa couleur, sujet qui mérite une rapide introduction.
2 Concepts
2.1 Température de couleur
La notion de température de couleur a déjà été introduite à la §2.8 de l'étape 5, dans laquelle figurait déjà l'image ci-dessous montrant la correspondance entre la température d'un corps noir (en degrés Kelvin) et sa couleur.
Afin de pouvoir dessiner les étoiles avec une couleur correspondant à leur température, il est nécessaire de pouvoir effectuer la conversion représentée par cette image. Pour ce faire, nous utiliserons les données provenant d'un fichier disponible sur la page What color is a blackbody? de Mitchell Charity.
Ce fichier est un fichier textuel constitué d'une séquence de lignes, chacune d'entre elles pouvant être soit :
- une ligne de commentaire, qui commence par le caractère dièze (
#
) et qui doit être simplement ignorée, - une ligne de données, qui commence par une espace et qui donne la couleur correspondant à une température.
Il existe deux types de lignes de données, qui se distinguent par le fait qu'elles contiennent soit le texte 2deg
, soit le texte 10deg
à une position donnée. La différence entre les deux importe peu, mais nous utiliserons ici uniquement celles contenant le texte 10deg
, en ignorant les autres.
Par exemple, la ligne 211 de ce fichier ressemble à ceci — la partie centrale, qui ne nous intéresse pas, a été remplacée par […]
pour des questions de présentation :
10500 K 10deg […] #c8d9ff
Elle signifie que la couleur correspondant à une température de 10500°K est la couleur notée #c8d9ff
. Cette notation des couleurs, que l'on rencontre entre autres dans les feuilles de style sur le Web, donne les trois composantes de la couleur (rouge, verte puis bleue) sous la forme de trois nombres hexadécimaux à deux chiffres. Chaque composante est donc un nombre entier compris entre 0 et 255, où 0 représente l'absence totale de cette composante, 255 sa présence maximale.
Ainsi, la couleur notée #c8d9ff
est celle dont :
- la composante rouge vaut c816, soit 200,
- la composante verte vaut d916, soit 217,
- la composante bleue vaut ff16, soit 255.
Il s'agit donc d'un bleu clair :
Chaque ligne de données du fichier est organisée en colonnes qui commencent à des positions fixes. La table ci-dessous donne la position de début (inclusive) et de fin (exclusive) des colonnes qui nous intéressent :
De | A | Contenu |
---|---|---|
1 | 6 | Température en degrés Kelvin |
10 | 15 | Soit 2deg (précédé d'une espace), soit 10deg |
80 | 87 | Couleur RGB en notation Web (hexadécimale) |
La plage de températures couverte par le fichier va de 1000°K (première ligne de données) à 40000°K (dernière ligne de données), par pas de 100°K. Il y a donc un total de 391 lignes de données de type 10deg
.
Pour les températures qui ne sont pas des multiples de 100°K, le fichier ne fournit pas de couleur. Dans un tel cas, utiliser la couleur correspondant au plus proche multiple de 100°K de la température suffit largement à nos besoins.
3 Mise en œuvre Java
Cette étape est la première de la seconde partie du projet, durant laquelle vous serez plus libres et moins guidés que durant la première. En particulier, vous avez maintenant le droit de modifier ou augmenter l'interface publique des classes et interfaces proposées, pour peu bien entendu que vous ayez une bonne raison de le faire. De plus, nous nous attendons à ce que vous lisiez et compreniez la documentation des parties de la bibliothèque Java que vous devez utiliser.
Pour faciliter la correction, nous vous demandons néanmoins de respecter les noms de paquetages et de classes, interfaces et types énumérés donnés. En particulier, notez que la classe BlackBodyColor
décrite plus bas appartient à un nouveau paquetage, ch.epfl.rigel.gui
, destiné à contenir tout le code lié à l'interface utilisateur graphique (graphical user interface ou gui en anglais).
3.1 Classe ObservedSky
La classe ObservedSky
du paquetage ….astronomy
, publique et immuable, représente un ensemble d'objets célestes projetés dans le plan par une projection stéréographique. En d'autres termes, elle représente une sorte de photographie du ciel à un instant et un endroit d'observation donnés.
Le constructeur de cette classe prend en arguments tous les paramètres décrivant l'observation, à savoir :
- l'instant d'observation (donné par un couple date/heure « zoné »),
- la position d'observation (donnée par ses coordonnées géographiques),
- la projection stéréographique à utiliser,
- le catalogue contenant les étoiles et les astérismes.
Au moyen de ces informations, et des modèles du Soleil, de la Lune et des planètes du système solaire, ce constructeur calcule la position projetée dans le plan de tous les objets célestes, à savoir : le Soleil, la Lune, les planètes du système solaire — à l'exception de la Terre — et les étoiles du catalogue.
Pour chacun de ces types d'objets célestes, la classe ObservedSky
offre de plus :
- une méthode d'accès permettant d'obtenir le (ou les) objet(s) en question, sous la forme d'instance(s) de la sous-classe de
CelestialObject
correspondant au type d'objet, - une méthode donnant la position de l'objet dans le plan, sous la forme d'une instance de
CartesianCoordinates
pour les objets individuels (Soleil et Lune), ou sous la forme d'un tableau dedouble
pour les objets multiples (planètes et étoiles), comme décrit plus bas.
Par exemple, pour le Soleil, ces méthodes pourraient être :
sun
, qui retourne le Soleil sous la forme d'une instance deSun
,sunPosition
, qui retourne la position du Soleil dans le plan, sous la forme d'une instance deCartesianCoordinates
,
tandis que pour les planètes, ces méthodes pourraient être :
planets
, qui retourne la liste des sept planètes extraterrestres du système solaire,planetPositions
, qui retourne les coordonnées cartésiennes de ces mêmes planètes dans un tableau dedouble
.
Le tableau retourné par planetPositions
contient à la position 0 la coordonnée x
de la première planète retournée par planets
, à la position 1 la coordonnée y
de cette même planète, et ainsi de suite. Il contient donc en tout 14 valeurs.
La raison pour laquelle la position des planètes (et des étoiles) est retournée sous cette forme plutôt que sous la forme d'une liste de CartesianCoordinate
est que cela permettra ultérieurement de transformer ces coordonnées de manière très efficace.
En effet, avant de pouvoir afficher les objets à l'écran, il faudra encore appliquer une dernière transformation à leurs coordonnées, afin de les exprimer dans le repère de l'écran de l'ordinateur. Cette transformation sera représentée par la classe Transform
de JavaFX — ou une de ses sous-classes. Ces classes possèdent une méthode nommée transform2DPoints
qui peut justement transformer en une seule fois un grand nombre de coordonnées stockées dans un tableau de double
, ce qui explique notre choix.
La classe ObservedSky
offre également des méthodes donnant accès aux astérismes du catalogue utilisé, ainsi qu'à la liste des index des étoiles d'un astérisme donné. Ces méthodes ne font rien d'autre qu'appeler les méthodes correspondantes du catalogue d'étoiles utilisé.
Finalement, la classe ObservedSky
offre une méthode, qui pourrait être nommée objectClosestTo
et qui, étant donné les coordonnées d'un point du plan et une distance maximale, retourne l'objet céleste le plus proche de ce point, pour peu qu'il se trouve à une distance inférieure à la distance maximale. Cette méthode doit être efficace, car elle est destinée à être appelée pour trouver l'objet le plus proche du pointeur de la souris lorsque celle-ci se déplace à l'écran.
3.1.1 Conseils de programmation
Pour signaler le fait qu'aucun objet céleste ne se trouve plus proche que la distance maximale du point donné, la méthode objectClosestTo
pourrait retourner null
. Une autre solution, plus propre, consiste à utiliser le type Optional
de la bibliothèque Java, qui représente une cellule immuable potentiellement vide.
Avec cette classe, objectClosestTo
peut retourner une cellule vide — obtenue grâce à Optional.empty
— lorsqu'elle ne trouve aucun objet, et une cellule pleine — obtenue grâce à Optional.of
— lorsqu'elle en trouve un.
3.2 Installation de JavaFX
Avant d'aller plus loin, il est nécessaire d'installer JavaFX sur votre ordinateur, et de l'ajouter à votre projet. Sans cela, vous ne pourrez pas écrire la classe BlackBodyColor
décrite à la section suivante, car elle utilise une classe de JavaFX.
Pour installer JavaFX et l'ajouter à votre projet, suivez les instructions données dans notre guide à ce sujet.
3.3 Classe BlackBodyColor
La classe BlackBodyColor
du paquetage ….gui
, publique et non instanciable, offre une méthode permettant d'obtenir la couleur d'un corps noir étant donnée sa température.
Pour ce faire, elle utilise le contenu du fichier décrit à la §2.1, que nous vous fournissons dans une archive Zip. Ce fichier doit être placé dans le répertoire resources
de votre projet, qui contient déjà les catalogues d'étoiles et d'astérismes. Encore une fois, il est capital que vous mettiez effectivement ce fichier à cet endroit et que, pour lire son contenu, vous utilisiez la méthode getResourceAsStream
en lui passant le bon nom, qui est ici /bbr_color.txt
(attention à la barre verticale en tête !).
L'unique méthode publique de la classe BlackBodyColor
, nommée par exemple colorForTemperature
, prend en argument une température exprimée en degrés Kelvin et retourne la couleur correspondante, sous la forme d'une instance de la classe Color
de JavaFX. Elle lève bien entendu une exception si la température n'est pas dans la plage couverte par le fichier de référence.
3.3.1 Conseils de programmation
Un problème récurrent des entrées/sorties est que la quasi-totalité des méthodes peuvent lever une exception de type IOException
ou l'un de ses sous-types. Or ces exception sont des exceptions « checked », ce qui est parfois gênant.
Depuis quelques temps, la bibliothèque Java offre un moyen de contourner le problème grâce à la classe UncheckedIOException
qui, comme son nom l'indique, est une exception d'entrée/sortie « unchecked ». Au moyen de cette classe, il est possible de substituer, en quelque sorte, une exception unchecked à une exception checked, de la manière suivante :
try /* ressources éventuelles */ { // … code pouvant lever une IOException } catch (IOException e) { throw new UncheckedIOException(e); }
Ce genre de substitution s'avère très utile lorsque l'on veut, par exemple, faire des entrées/sorties au moment de la construction d'une classe, pour initialiser un de ses attributs statiques.
La classe Color
de JavaFX offre la méthode de construction web
, qui est très utile pour transformer les couleurs données dans le fichier en instances de la classe Color
.
3.4 Tests
À partir de cette étape, aucun fichier de vérification de signatures ne vous est fourni, étant donné que la signature des différentes méthodes n'est plus spécifiée en détail. Pour la même raison, aucun test unitaire ne sera plus fourni à l'avenir, à vous d'écrire les vôtres. Notez que cela est fortement recommandé en général.
4 Résumé
Pour cette étape, vous devez :
- écrire les classes
ObservedSky
etBlackBodyColor
selon les instructions données plus haut, - tester votre code,
- documenter la totalité des entités publiques que vous avez définies.
Aucun rendu n'est à faire pour cette étape avant le rendu final. N'oubliez pas de faire régulièrement des copies de sauvegarde de votre travail en suivant nos indications à ce sujet.