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.

Sorry, your browser does not support SVG.
Figure 1 : Couleur d'un corps noir en fonction de sa température (de Wikimedia Commons)

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 de double 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 de Sun,
  • sunPosition, qui retourne la position du Soleil dans le plan, sous la forme d'une instance de CartesianCoordinates,

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 de double.

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 et BlackBodyColor 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.