Etape 3 – Attributs

1 Introduction

Cette troisième étape a pour but de définir des classes permettant de représenter et d'attacher aux entités géométriques des attributs et leur valeur.

1.1 Attributs

Les entités géométriques définies lors de l'étape prédécente (polylignes et polygones à trous) sont insuffisantes, à elles seules, pour dessiner une carte. En effet, même s'il est p.ex. possible de représenter la géométrie d'une forêt et celle d'un lac au moyen d'un polygone à trous, il est clair que ces deux types d'éléments doivent être représentés différemment sur une carte, par exemple au moyen de couleurs distinctes.

Les entités géométriques composant les cartes sont donc combinées avec ce que nous nommerons des attributs, décrivant les caractéristiques non géométriques de ces entités. Chaque attribut a un nom et une valeur associée. Les attributs utilisés dans ce projet sont simplement ceux définis par OpenStreetMap, qui possède également cette notion.

Pour reprendre l'exemple ci-dessus, une forêt pourrait être représentée par un polygone auquel est associé l'attribut natural avec la valeur wood. Si cette forêt a un nom, comme p.ex. le Bois de Sauvabelin, ce nom peut également être attaché au polygone, au moyen de l'attribut name dont la valeur serait Bois de Sauvabelin. Un lac quant à lui, p.ex. le Léman, possèderait aussi l'attribut natural, mais sa valeur serait water pour indiquer qu'il s'agit d'une étendue d'eau. Son nom, Lac Léman, serait là encore attaché à l'attribut name. Et ainsi de suite.

Un ensemble d'attributs n'est donc rien d'autre qu'un ensemble de paires (clef, valeur) où la clef est le nom de l'attribut et sa valeur est la valeur de l'attribut.

1.2 Attributs OpenStreetMap

Comme cela a été expliqué ci-dessus, nous utilisons dans ce projet les noms et valeurs des attributs définis par OpenStreetMap, afin de simplifier les choses. Même s'il n'est pas strictement nécessaire de connaître ou de comprendre la signification de ces attributs pour réaliser cette étape, il peut être intéressant d'avoir quelques connaissances de base à leur sujet.

Une liste des principaux attributs et de leurs valeurs possibles est donnée sur la page Map Features du Wiki OpenStreetMap, disponible en français et en anglais. Il est conseillé de passer quelques minutes à la parcourir rapidement, en particulier les parties concernant les routes, les types de terrain, la nature, les voies ferrées et les cours d'eau.

2 Mise en œuvre Java

2.1 Attributes

La classe Attributes du paquetage ch.epfl.imhof, publique, finale et immuable, représente un ensemble d'attributs et la valeur qui leur est associée. Les attributs et leur valeur sont des chaînes de caractères, et la classe Attributes n'est donc rien d'autre qu'une table associative immuable dont les clefs et les valeurs sont des chaînes de caractères.

La classe Attributes est dotée d'un unique constructeur public :

  • Attributes(Map<String, String> attributes), qui construit un ensemble d'attributs avec les paires clef/valeur présentes dans la table associative donnée.

En plus de ce constructeur, la classe Attributes offre les méthodes publiques suivantes :

  • boolean isEmpty(), qui retourne vrai si et seulement si l'ensemble d'attributs est vide.
  • boolean contains(String key), qui retourne vrai si l'ensemble d'attributs contient la clef donnée.
  • String get(String key), qui retourne la valeur associée à la clef donnée, ou null si la clef n'existe pas.
  • String get(String key, String defaultValue), qui retourne la valeur associée à la clef donnée, ou la valeur par défaut donnée si aucune valeur ne lui est associée.
  • int get(String key, int defaultValue), qui retourne l'entier associé à la clef donnée, ou la valeur par défaut donnée si aucune valeur ne lui est associée, ou si cette valeur n'est pas un entier valide.
  • Attributes keepOnlyKeys(Set<String> keysToKeep), qui retourne une version filtrée des attributs ne contenant que ceux dont le nom figure dans l'ensemble passé.

La plupart de ces méthodes correspondent assez directement à des méthodes de la table associative privée qui contient les attributs et leurs valeurs, et leur mise en œuvre est donc triviale. Par exemple, la méthode isEmpty ne fait rien d'autre qu'appeler la méthode isEmpty sur la table associative privée. Les seules exceptions à cette règle sont les deux dernières méthodes mentionnées ci-dessus.

Pour la première d'entre-elles, à savoir la variante de get retournant un entier, il est utile de connaître l'existence de la méthode parseInt de la classe java.lang.Integer, qui transforme une chaîne de caractères en l'entier correspondant. Par exemple, si on l'appelle ainsi :

Integer.parseInt("123");

elle retourne l'entier 123. Si la chaîne passée n'est pas un entier valide, elle lève l'exception NumberFormatException.

2.2 Attributes.Builder

La classe Attributes.Builder, imbriquée statiquement dans la classe Attributes, publique et finale, sert de bâtisseur à la classe Attributes. Elle ne possède pas de constructeur autre que celui par défaut, mais offre les méthodes publiques suivantes :

  • void put(String key, String value), qui ajoute l'association (clef, valeur) donnée à l'ensemble d'attributs en cours de construction. Si un attribut de même nom avait déjà été ajouté précédemment à l'ensemble, sa valeur est remplacée par celle donnée.
  • Attributes build(), qui construit un ensemble d'attributs contenant les associations clef/valeur ajoutées jusqu'à présent.

2.3 Attributed

La classe générique Attributed<T> du paquetage ch.epfl.imhof, publique, finale et immuable, représente une entité de type T dotée d'attributs. Elle ne possède qu'un seul constructeur public :

  • Attributed(T value, Attributes attributes), qui construit une valeur attribuée dont la valeur et les attributs sont ceux donnés (voir l'exemple d'utilisation plus bas).

En plus de ce constructeur, la classe Attributed possède les méthodes d'accès suivantes, qui permettent d'obtenir les valeur passées au constructeur :

  • T value(), qui retourne la valeur à laquelle les attributs sont attachés.
  • Attributes attributes(), qui retourne les attributs attachés à la valeur.

La classe Attributed offre également les méthodes publiques ci-dessous, chacune d'entre-elles correspondant à une méthode de la classe Attributes. Leur seul but est de faciliter l'accès aux attributs et à leur valeur :

  • boolean hasAttribute(String attributeName), qui retourne vrai si et seulement si les attributs incluent celui dont le nom est passé en argument. Correspond à la méthode contains de Attributes.
  • String attributeValue(String attributeName), qui retourne la valeur associée à l'attribut donné, ou null si celui-ci n'existe pas. Correspond à l'une des méthode get de la classe Attributes.
  • String attributeValue(String attributeName, String defaultValue), qui retourne la valeur associée à l'attribut donné, ou la valeur par défaut donnée si celui-ci n'existe pas. Correspond à l'une des méthodes get de la classe Attributes.
  • int attributeValue(String attributeName, int defaultValue), qui retourne la valeur entière associée à l'attribut donné, ou la valeur par défaut si celui-ci n'existe pas ou si la valeur qui lui est associée n'est pas un entier valide. Correspond à l'une des méthodes get de la classe Attributes.

2.4 Exemple

L'utilisation des différentes classes décrites plus haut n'étant pas forcément évidente à comprendre, l'exemple ci-dessous montre comment associer les attributs natural (avec la valeur water), name (avec la valeur Lac Léman) et ele (avec la valeur 372) à un polygone p pour obtenir ap, un polygone attribué :

Attributes.Builder b = new Attributes.Builder();
b.put("natural", "water");
b.put("name", "Lac Léman");
b.put("ele", "372");
Attributes a = b.build();

Polygon p = ...;
Attributed<Polygon> ap = new Attributed<>(p, a);

// Affiche 372
System.out.println(ap.attributeValue("ele", 0));

2.5 Tests

A partir de cette étape, nous ne vous fournissons plus de tests unitaires. Il vous faut donc les écrire vous-même si vous désirez en avoir, ce qui est fortement conseillé.

Nous vous fournissons néanmoins une archive Zip à importer dans votre projet. Elle contient un seul fichier, NameCheck03.java, qui ressemble à un test unitaire mais qui n'en est pas un. En effet, même si l'unique méthode de la classe qu'il définit fait référence à la totalité des noms de classes et de méthodes de cette étape, elle ne vérifie aucunement qu'elles se comportent correctement.

Le but de ce fichier est de vous permettre de vérifier que vous avez correctement nommé les classes et méthodes qu'il vous est demandé de réaliser. Cela est très important, car la moindre faute dans un nom nous empêcherait d'exécuter nos tests unitaires lors du rendu testé de la mi-semestre.

Notez que le système de rendu compile votre code avec ce fichier de vérification de noms et refuse votre rendu en cas de problème. Assurez-vous donc qu'Eclipse ne signale aucune erreur dans ce fichier avant d'effectuer un rendu.

3 Résumé

Pour cette étape, vous devez :

  • écrire les classes Attributes, Attributes.Builder et Attributed en fonction des spécifications données plus haut,
  • vous assurer que le fichier de vérification de noms fourni, une fois importé dans votre projet, ne contient aucune erreur,
  • tester votre code et corriger les éventuels problèmes rencontrés,
  • documenter la totalité des entités publiques que vous avez définies,
  • rendre votre code au plus tard le 6 mars 2015 à 16h00, via le système de rendu (ce rendu est un rendu mineur et compte donc pour la note finale).

Notez que si vous n'avez pas totalement terminé cette étape à temps, vous pouvez en rendre une version incomplète. Pour que votre rendu soit accepté, il faut bien entendu que la totalité des classes et méthodes demandées soient définies, mais leur mise en œuvre peut être incomplète : les méthodes peuvent retourner une valeur par défaut comme null ou 0, suivant les cas.