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 :

  1. les nœuds, qui représentent un point à la surface de la Terre,
  2. les chemins, qui représentent une séquence de nœuds,
  3. 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.

Références

Les pages ci-dessous du Wiki OSM pourront être utiles aux personnes désirant en savoir plus sur les entités décrites plus haut :

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é, ou null 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éthode setIncomplete 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'exception IllegalStateException si le nœud en cours de construction est incomplet, c-à-d si la méthode setIncomplete 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'exception IllegalArgumentException 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éthode equals 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'exception IllegalStateException 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 classe Member 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ération Type 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'exception IllegalStateException 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ération OSMRelation.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).