Etape 4 – Entités OpenStreetMap
1 Introduction
Le but de cette quatrième étape est de définir les classes permettant de représenter les entités des cartes OpenStreetMap, à savoir les nœuds, les chemins et les relations.
1.1 Entités OpenStreetMap
On pourrait imaginer — et souhaiter — que le projet OpenStreetMap (abrégé OSM ci-dessous) utilise des entités géométriques simples du type de celles que nous avons définies à l'étape 2 (polylignes, polygones) pour décrire les éléments composant sa carte.
Malheureusement, pour des raisons principalement historiques, il n'en est rien. Au lieu de cela, une carte OpenStreetMap est composée de trois types d'entités :
- les nœuds, qui représentent un point à la surface de la Terre,
- les chemins, qui représentent une séquence de nœuds,
- les relations, qui représentent des ensembles d'autres entités — nœuds, chemins ou même autres relations.
Chacun de ces trois types d'entités possède des attributs semblables à ceux que nous avons définis à l'étape précédente. De plus, chaque entité est identifiée par un identifiant numérique globalement unique, c'est-à-dire que deux entités différentes ne peuvent pas avoir le même identifiant.
La correspondance entre les différents types d'entités OSM et les entités géométriques que nous avons définies n'est pas triviale. En effet, si un nœud OSM correspond assez bien à un point en coordonnées sphériques, les chemins OSM sont utilisés aussi bien pour représenter des polylignes attribuées que des polygones attribués. Quant aux relations OSM, elles sont utilisées dans de nombreux buts, mais nous ne traiterons ici que celles utilisées pour représenter les polygones à trous.
Le dessin des cartes du projet sera fait à partir des entités géométriques attribuées définies lors des étapes précédentes. Toutefois, étant donné que les données brutes obtenues du projet OSM sont exprimées en termes des entités décrites ci-dessus, il est nécessaire de pouvoir représenter ces dernières. Le but de cette étape est donc simplement de définir des classes permettant de le faire.
Nœuds
Dans le modèle OpenStreetMap, un nœud (node) est un point à la surface de la Terre dont la position est donnée par sa longitude et sa latitude, exprimées en degrés dans le système WGS 84. Un nœuds OSM est donc très similaire à ce que nous avons appelé un point en coordonnées sphériques (classe PointGeo
), la différence principale étant que les nœuds OSM peuvent avoir des attributs attachés.
Comme cela a été dit plus haut, toute entité OSM est identifié par un entier unique. Pour la suite du projet, il peut être utile de savoir que n'importe quelle entité de la base de données OSM peut être examinée via le site Web du projet. Ainsi, on peut obtenir des informations concernant le nœud dont l'identifiant unique est 35295150, qui représente le centre de Lausanne, à l'adresse :
http://www.openstreetmap.org/node/35295150
En suivant ce lien, on constate p.ex. que de nombreux attributs sont attachés à ce nœud, parmi lesquels l'attribut place
, dont la valeur city
le désigne comme étant une ville, ou encore l'attribut population
, dont la valeur 132626 donne le nombre approximatif d'habitants de la ville.
Chemins
Dans le modèle OpenStreetMap, un chemin (way) est une séquence ordonnée de nœuds. Un chemin OSM est donc très similaire à une polyligne, si ce n'est que les points qui composent un chemin sont en coordonnées sphériques et pas en coordonnées planaires, et qu'un chemin peut avoir des attributs attachés. De plus, un chemin fermé se reconnaît au fait que son dernier nœud est identique au premier. Ainsi, un chemin OSM fermé représentant un triangle comporte quatre nœuds — le dernier égal au premier — alors que la polyligne fermée représentant ce même triangle, dans notre modèle, ne comporte que trois sommets.
OSM utilise les chemins dans deux buts très différents : d'une part pour représenter des entités qui sont effectivement linéaires, comme des routes ou des voies de chemin de fer, et d'autre part pour représenter des polygones sans trous, comme des bâtiments simples.
Tout comme pour les nœuds, il est possible d'obtenir les informations liées à un chemin sur le site Web du projet OSM. Par exemple, on saura tout sur le chemin 51740696, qui représente l'Allée de Savoie à l'EPFL, à l'adresse suivante :
http://www.openstreetmap.org/way/51740696
En suivant ce lien, on constate que de nombreux attributs sont attachés à ce chemin, parmi lesquels l'attribut highway
, dont la valeur unclassified
le désigne comme étant une route d'importance mineure, ou encore l'attribut name
, dont la valeur Allée de Savoie
donne le nom de la route.
Pour illustrer un cas où un chemin est utilisé pour représenter une surface, on peut par exemple examiner celui portant l'identifiant 46828008, qui modélise la Tour de Sauvabelin.
Relations et multipolygones
Dans le modèle OpenStreetMap, une relation (relation) est un ensemble d'autres entités OSM (nœuds, chemins ou relations), qu'on appelle ses membres (members). Chaque membre d'une relation doit avoir un rôle (role), qui décrit sa fonction dans la relation.
Les relations étant très générales, elles rencontrent de nombreuses utilisations dans le projet OpenStreetMap. Toutefois, dans le cadre du projet Imhof, un seul type de relation nous intéresse : celui permettant de représenter ce qu'OSM nomme un multipolygone. Il s'agit donc du seul type de relation décrit dans ce qui suit.
Un multipolygone est une collection de polygones à trous. Comme expliqué ci-dessus, un chemin OSM ne permet de représenter qu'un seul polygone sans trous. Pour cette raison, un chemin ne peut être utilisé pour représenter un polygone qui a effectivement un ou plusieurs trous, comme un bâtiment avec une cour intérieure, un lacs avec une île, ou encore une forêt avec une ou plusieurs clairières. De même, un chemin ne peut représenter une entité logiquement unique mais composée de plusieurs polygones disjoints, comme une forêt coupée en deux par une route. Ces entités sont donc représentées par des relations de type multipolygone.
Tous les membres d'une relation multipolygone doivent être des chemins et doivent avoir l'un des deux rôles suivants :
outer
si le chemin fait partie de l'enveloppe d'un polygone composant le multipolygone,inner
si le chemin fait partie d'un trou d'un polygone composant le multipolygone.
Le site Web OSM permet d'obtenir des informations sur une relation. Ainsi, on peut examiner celle portant l'identifiant 331569, qui représente le Learning Center de l'EPFL, à l'adresse :
http://www.openstreetmap.org/relation/331569
Le Learning Center étant un bâtiment à trous, il n'est pas possible de le représenter au moyen d'un simple chemin fermé, et une relation est donc nécessaire. Un certain nombre d'attributs sont attachés à cette relation, parmi lesquels l'attribut type
, dont la valeur multipolygon
désigne la relation comme étant un multipolygone, ou encore l'attribut building
dont la valeur yes
désigne le multipolygone comme représentant un bâtiment.
En plus de ses attributs, cette relation possède un grand nombre de membres. Parmi ceux-ci figure par exemple le chemin 44675207 qui représente le trou nord-est du bâtiment.
2 Mise en œuvre Java
La mise en œuvre Java des trois types d'entités OpenStreetMap décrits ci-dessus se fait naturellement dans trois classes distinctes. Etant donné que ces trois types d'entités possèdent des caractéristiques communes, p.ex. les attributs, ces trois classes héritent toutes d'une même classe mère.
Toutes ces classes font partie du paquetage ch.epfl.imhof.osm
, réservé aux classes modélisant les concepts propres au projet OpenStreetMap.
2.1 OSMEntity
La classe OSMEntity
, publique, abstraite et immuable, sert de classe mère à toutes les classes représentant les entités OSM. Elle est dotée d'un unique constructeur public, dont le seul but est d'être appelé par les constructeurs des sous-classes :
OSMEntity(long id, Attributes attributes)
, qui construit une entité OSM dotée de l'identifiant unique et des attributs donnés.
Notez qu'il est nécessaire d'utiliser le type long
pour représenter l'identifiant des entités, le type int
ne suffisant pas.
En plus de ce constructeur, la classe OSMEntity
possède les méthodes publiques suivantes :
long id()
, qui retourne l'identifiant unique de l'entité.Attributes attributes()
, qui retourne les attributs de l'entité.boolean hasAttribute(String key)
, qui retourne vrai si et seulement si l'entité possède l'attribut passé en argument.String attributeValue(String key)
, qui retourne la valeur de l'attribut donné, ounull
si celui-ci n'existe pas.
2.2 OSMEntity.Builder
La classe OSMEntity.Builder
, publique, abstraite et imbriquée statiquement dans la classe OSMEntity
, sert de classe mère à toutes les classes de bâtisseurs d'entités OSM. En plus de l'identifiant de l'entité en construction et de ses attributs, cette classe possède un champ permettant de savoir si l'entité est incomplète ou non, dont l'utilité deviendra claire lors d'une étape ultérieure. Pour l'instant, la seule chose qu'il est important de savoir à son sujet est qu'initialement une entité en construction n'est pas incomplète, mais le devient si la méthode setIncomplete
décrite plus bas est appelée.
La classe OSMEntity.Builder
possède un unique constructeur public, dont le seul but est d'être appelé par les constructeurs des sous-classes :
Builder(long id)
, qui construit un bâtisseur pour une entité OSM identifiée par l'entier donné.
La classe OSMEntity.Builder
possède de plus les méthodes publiques suivantes :
void setAttribute(String key, String value)
, qui ajoute l'association (clef, valeur) donnée à l'ensemble d'attributs de l'entité en cours de construction. Si un attribut de même nom avait déjà été ajouté précédemment, sa valeur est remplacée par celle donnée.void setIncomplete()
, qui déclare que l'entité en cours de construction est incomplète.boolean isIncomplete()
, qui retourne vrai si et seulement si l'entité en cours de construction est incomplète, c-à-d si la méthodesetIncomplete
a été appelée au moins une fois sur ce bâtisseur depuis sa création.
2.3 OSMNode
La classe OSMNode
, publique, finale et immuable, représente un nœud OSM. Elle hérite bien entendu de la classe OSMEntity
et possède un unique constructeur public :
OSMNode(long id, PointGeo position, Attributes attributes)
, qui construit un nœud OSM avec l'identifiant, la position et les attributs donnés.
En plus de ce constructeur et des méthodes héritées de sa superclasse, la classe OSMNode
possède la méthode publique suivante :
PointGeo position()
, qui retourne la position du nœud.
2.4 OSMNode.Builder
La classe OSMNode.Builder
, imbriquée statiquement dans la classe OSMNode
, publique et finale, hérite de la classe OSMEntity.Builder
. Elle sert de bâtisseur à la classe OSMNode
et permet de construire un nœud en plusieurs étapes. Elle possède l'unique constructeur public suivant :
Builder(long id, PointGeo position)
, qui construit un bâtisseur pour un nœud ayant l'identifiant et la position donnés.
Cette classe offre de plus la méthode de construction suivante :
OSMNode build()
, qui construit un nœud OSM avec l'identifiant et la position passés au constructeur, et les éventuels attributs ajoutés jusqu'ici au bâtisseur. Lève l'exceptionIllegalStateException
si le nœud en cours de construction est incomplet, c-à-d si la méthodesetIncomplete
a été appelée sur ce bâtisseur depuis sa création.
2.5 OSMWay
La classe OSMWay
, publique, finale et immuable, représente un chemin OSM. Elle hérite bien entendu de la classe OSMEntity
et possède un unique constructeur public :
OSMWay(long id, List<OSMNode> nodes, Attributes attributes)
, qui construit un chemin étant donnés son identifiant unique, ses nœuds et ses attributs. Lève l'exceptionIllegalArgumentException
si la liste de nœuds possède moins de deux éléments.
En plus de ce constructeur et des méthodes héritées de sa superclasse, la classe OSMWay
possède les méthodes publiques suivantes :
int nodesCount()
, qui retourne le nombre de nœuds du chemin.List<OSMNode> nodes()
, qui retourne la liste des nœuds du chemin.List<OSMNode> nonRepeatingNodes
, qui retourne la liste des nœuds du chemin sans le dernier si celui-ci est identique au premier,OSMNode firstNode()
, qui retourne le premier nœud du chemin.OSMNode lastNode()
, qui retourne le dernier nœud du chemin.boolean isClosed()
, qui retourne vrai si et seulement si le chemin est fermé, c-à-d que la méthodeequals
du premier nœud considère que le dernier nœud lui est égal.
2.6 OSMWay.Builder
La classe OSMWay.Builder
, imbriquée statiquement dans la classe OSMWay
, publique et finale, hérite de la classe OSMEntity.Builder
. Elle sert de bâtisseur à la classe OSMWay
et permet de construire un chemin en plusieurs étapes. Elle possède l'unique constructeur public suivant :
Builder(long id)
, qui construit un bâtisseur pour un chemin ayant l'identifiant donné.
Cette classe offre de plus les méthodes publiques suivantes :
void addNode(OSMNode newNode)
, qui ajoute un nœud à (la fin) des nœuds du chemin en cours de construction,OSMWay build()
, qui construit et retourne le chemin ayant les nœuds et les attributs ajoutés jusqu'à présent. Lève l'exceptionIllegalStateException
si le chemin en cours de construction est incomplet.
De plus, la classe OSMWay.Builder
redéfinit la méthode isIncomplete
héritée de sa super-classe afin qu'un chemin en cours de construction mais possèdant moins de deux nœuds soit également considéré comme incomplet, même si la méthode setIncomplete
n'a pas été appelée.
2.7 OSMRelation
La classe OSMRelation
, publique, finale et immuable, représente une relation OSM. Elle hérite bien entendu de la classe OSMEntity
et possède un unique constructeur public :
OSMRelation(long id, List<Member> members, Attributes attributes)
, qui construit une relation étant donnés son identifiant unique, ses membres et ses attributs. (La classeMember
est décrite ci-dessous.)
En plus de ce constructeur et des méthodes héritées de sa superclasse, la classe OSMRelation
possède la méthode publique suivante :
List<Member> members()
, qui retourne la liste des membres de la relation.
2.8 OSMRelation.Member
La classe Member
, imbriquée statiquement dans la classe OSMRelation
, publique, finale et immuable, représente un membre d'une relation OSM. Elle est équipée d'un unique constructeur public :
Member(Type type, String role, OSMEntity member)
, qui construit un membre ayant le type, le rôle et la valeur donnés. (L'énumérationType
est décrite ci-dessous.)
En plus de ce constructeur, cette classe offre les trois méthodes d'accès suivantes :
Type type()
, qui retourne le type du membre.String role()
, qui retourne le rôle du membre.OSMEntity member()
, qui retourne le membre lui-même.
2.9 OSMRelation.Member.Type
L'énumération OSMRelation.Member.Type
, imbriquée dans la classe OSMRelation.Member
, énumère les trois types de membres qu'une relation peut comporter, à savoir NODE
pour les nœuds, WAY
pour les chemins, et RELATION
pour les relations.
2.10 OSMRelation.Builder
La classe OSMRelation.Builder
, imbriquée statiquement dans la classe OSMRelation
, publique et finale, hérite de la classe OSMEntity.Builder
. Elle sert de bâtisseur à la classe OSMRelation
et permet de construire une relation en plusieurs étapes. Elle possède l'unique constructeur public suivant :
Builder(long id)
, qui construit un bâtisseur pour une relation ayant l'identifiant donné.
Cette classe offre de plus les méthodes publiques suivantes :
void addMember(Member.Type type, String role, OSMEntity newMember)
, qui ajoute un nouveau membre de type et de rôle donnés à la relation.OSMRelation build()
, qui construit et retourne la relation ayant l'identifiant passé au constructeur ainsi que les membres et les attributs ajoutés jusqu'à présent au bâtisseur. Lève l'exceptionIllegalStateException
si la relation en cours de construction est incomplète.
2.11 Tests
Comme précédemment, nous ne vous fournissons pas de tests unitaires pour cette étape mais uniquement un fichier de vérification de noms, contenu dans une archive Zip à importer dans votre projet.
3 Résumé
Pour cette étape, vous devez :
- écrire les classes
OSMEntity
,OSMNode
,OSMWay
,OSMRelation
,OSMRelation.Member
et leurs éventuels bâtisseurs ainsi que l'énumérationOSMRelation.Member.Type
en fonction des spécifications données plus haut, - vérifier 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 13 mars 2015 à 16h00, via le système de rendu (ce rendu est un rendu mineur et compte donc pour la note finale).