Ocelet

Relation

Une relation porte la description de ce qui se passe lorsque des entités interagissent les unes avec les autres.

Ocelet fait un usage important du concept de graphe d'interaction. Un graphe d'interaction est d'abord un graphe (au sens mathématique du terme) c'est à dire un ensemble d'entités (les sommets du graphe) et un ensemble de liens entre des entités (les arètes du graphe).

C'est à travers la définition d'une relation qu'il est possible de construire un exemplaire (ou plusieurs) de graphe d'interction. Cette définition permet d'abord d'indiquer la nature du graphe : on indique les catégories d'entités qui sont susceptibles d'interagir; elle permet ensuite d'indiquer ce qui se passe lorsque ces entités interagissent les unes avec les autres : on décrit des fonctions d'interaction.

L'utilisation dans un scénario se fait principalement à travers l'appel de fonctions prédéfinies (construction du graphe), puis de fonctions de filtrage et d'interactions que l'on a défini.

Contenu

Définir une relation

Syntaxe

Définition d'une relation

relation Nom de la relation < Entité1 role1 , Entité2 role2 > {
définitions de fonctions d'interactions, de filtrage, et de propriétés d'arètes
}

Le Nom de la relation doit impérativement commencer par une lettre (majuscule de préférence) mais peut éventuellement être suivi par des chiffres, et ne pas contenir d'espace ni de caractères spéciaux.

On peut placer dans une définition de relation autant de propriétés et de fonctions de filtrage et d'interaction que l'on veut.

Dans cette définition, Entité1 et Entité2 sont des catégories d'entités qui doivent être définies par ailleurs. Il ne s'agit pas d'instances mais bien du nom générique de catégories d'entités, (c'est la raison pour laquelles nous avons mis la première lettre en majuscule). Les mots role1 et role2 sont des identifiants que vous pouvez choisir, ils vont être utilisés pour représenter une instance d'entité (un sommet du graphe) dans les fonctions d'interaction. Autrement dit, quand on va définir une fonction d'interaction, on pourra utiliser l'identifiant role1 comme si c'était un variable de type Entité1 et on fera de même avec role2 en tant qu'instance d'Entité2. Chaque arète du graphe d'interaction aura dans ce cas deux extrémités : role1 et role2, et la définition de fonctions d'interactions va permettre de décrire ce qui se passe entre ces deux extrémités au moment d'une interaction entre elles.


Définition d'une fonction d'interaction

interaction nom de la fonction ( (Type argument (,Type argument)* )? ) {
code du corps de la fonction
(agg { opérations d'aggrégation })?
}

Le nom de la fonction doit impérativement commencer par une lettre (minuscule de préférence) mais peut éventuellement être suivi par des chiffres, et ne pas contenir d'espace ni de caractères spéciaux.

On peut optionnellement déclarer un ou plusieurs arguments qu'il est possible de passer à la fonction (la notation (...)? utilisée ici signifie 0 ou 1 occurence). Dans ce cas chaque argument sera déclaré par : Type argument. Le Type est soit un type simple d'Ocelet (Integer, Double, Point, etc.), soit un type composite, soit une catégorie d'entité, soit un datafacer, soit même une autre relation. L'argument est un nom de variable que vous choisissez, il doit commencer par une lettre en minuscule.

Si on déclare plusieurs arguments, ils doivent être séparés par une virgule : Type1 arg1, Type2 arg2, Type3 arg3 etc. (La notation (...)* utilisée dans la définition de syntaxe signifie 0 occurence ou plus).

Le code du corps de la fonction contient une série d'instructions du langage, d'appels à des fonctions usuelles, ou à des fonctions d'aggrégation. Ce code correspond à ce qui se passe sur chaque arète du graphe lorsqu'une entité role1 interagit avec une entité role2.

Il est important de noter que dans le code d'une fonction d'interaction, les propriétés des entités role1 et role2 ne sont pas modifiées directement si on leur affecte une nouvelle valeur. Dans le principe on lit l'état n et on écrit l'état n+1 des propriétés. La modification effective n'intervient qu'après application de la fonction d'interaction à toutes les arètes du graphe.

Dans certains cas, c'est à travers un opérateur d'aggrégation que cette affectation a lieu. Une syntaxe particulière doit être utilisée à la fin de la fonction pour indiquer sur quelles propriétés on fera appel à des opérateurs d'aggrégation :

agg {
( role . propriété << | +<< opérateur d'aggrégation)*
}

On peut déclarer l'usage d'opérateurs d'aggrégation sur autant de propriétés que nécessaire. C'est ce qu'indique la notation (...)*. Les opérateurs d'aggrégation peuvent être soit un de ceux qui sont prédéfinis dans Ocelet (Random, Max, Min, Mean, etc.) soit un opérateur que vous aurez défini vous même.

La notation << | +<< signifie que l'on peut utiliser soit le sigle << soit le sigle +<<. Dans le premier cas seules les nouvelles valeurs affectées à la propriété (les valeurs de l'état n+1) sont passées dans la liste des valeurs candidates à l'opérateur d'affectation. Dans le second cas, la valeur précédente (l'état n) de la propriété est elle aussi présente dans la liste des valeurs candidates. Voir la section consacrée à l'usage des opérateurs d'aggrégation pour davantage de détails.


Définition d'une propriété d'arète

On peut si nécessaire attacher des propriétés aux arètes d'un graphe d'interaction. Il suffit de déclarer ces propriété dans la définition de la relation. On pourra ensuite mettre à jour ou lire la valeur de ces propriétés d'arc dans le code des fonctions d'interaction comme s'il s'agissait de variables. On déclarera chaque propriété de la façon suivante :

property Type nom

Le Type d'une propriété est soit un type simple d'Ocelet ( Integer, Double, Point, etc.), soit un type composite défini par vous même sous la forme d'une structure.

Le nom d'une propriété doit impérativement commencer par une lettre (minuscule de préférence) mais peut éventuellement être suivi par des chiffres, et ne pas contenir d'espace ni de caractère spéciaux.


Définition d'une fonction de filtrage

Les filtres sont des fonctions qui permettent de conserver certaines arètes d'un graphe d'interaction. Si par exemple on souhaite exécuter une fonction d'interaction sur une partie seulement d'un graphe, il est pratique de faire appel à un filtre pour n'appliquer cette fonction qu'aux arètes qui répondent à certains critères.

Un filtre est donc une fonction qui doit déterminer si chacune des arètes visitées doit être conservée ou non et renvoyer une valeur de type Boolean : soit true (si on conserve l'arète) soit false (si on ne la conserve pas). La syntaxe de définition d'un filtre sur les arètes est la suivante :

filter nom du filtre { code du filtre }

Le nom du filtre doit impérativement commencer par une lettre (minuscule de préférence) mais peut éventuellement être suivi par des chiffres, et ne pas contenir d'espace ni de caractère spéciaux.

Le code du filtre est le corps de la fonction, il doit renvoyer une valeur soit true (si on conserve l'arète) soit false (si on ne la conserve pas).


Exemples

On fait référence à un ou plusieurs types d'entité dans la définition d'une Relation. C'est la raison pour laquelle nous avons aussi des définitions d'entités dans les exemples présentés ci-dessous.

entity Parcelle {
  Integer id
  Integer ocsol
  MultiPolygon geom
  String culture
}

entity MntCellule{
  property Cell cell
  property Double altitude
}

entity Hexagone{
  property Cell cell
  property Double valeur
  property Integer os
}
relation Voisin<Parcelle p1, Parcelle p2> {

   // Vrai si p1 et p2 ont la même occupation du sol que l'argument os fourni
   filter memeOcSol(Integer os) {
     return ((p1.ocsol == os) && (p2.ocsol == os))
   }

   // Vrai si la distance entre p1 et p2 est inférieure à dmax
   filter distance(Double dmax) {
     return (p1.geom.distance(p2.geom) <= dmax)
   }

   // Dessine une arète de ce graphe dans un fichier Kml
   // KmlOut est ici un Datafacer de type KmlExport
   interaction drawEdge(KmlOut ko, Date beg, Date end){
     let pc1 = Point|xy(p1.geom.centroid.x,p1.geom.centroid.y)
     let pc2 = Point|xy(p2.geom.centroid.x,p2.geom.centroid.y)
     let arc = Line|points(pc1,pc2)
     ko.addGeometry("Voisins",p1.id+" -- "+p2.id,beg,end,arc,"vois",0)
  }
}

entity Exploitant {

// L'exploitant choisit quelle culture mettre sur la parcelle
  // connaissant l'indentifiant de la parcelle et la date.
  service String getSemis(Integer idParc, Date datesemis) {
    ...
  }
}

relation Explotation<Exploitant ex, Parcelle p> {

  // La culture utilisée sur la parcelle est mise à jour
   interaction Seme(Date dateSemis) {
     p.culture = ex.getSemis(p.id, dateSemis)
   }
}

relation MntToHexagon<MntCellule mnt, Hexagone hexagone>{

  // La valeur en altitude est attribuée à la propriété valeur des entités Hexagones (moyenne par    aggregation)
   interaction setValeurMnt(){
     hexagone.valeur = mnt.altitude
   }agg{
     hexagone.valeur << Mean
   }
 }

 relation HexagoneEtParcelle<Hexagone hexagone, Parcelle parcelle>{

   // Attribution de l'occupation du sol aux Hexagones venant du parcellaires
   interaction setOcs(){
     hexagone.os = parcelle.ocsol
   }
 }

 relation HexagonVoisins<Hexagone hexagone1, Hexagone hexagone2>{

 // Moyenne de la valeur des hexagones voisins sur l'hexagone central
   interaction moyenneDesVoisins(){
     hexagone1.valeur = hexagone2.valeur
     hexagone2.valeur = hexagone1.valeur
   }agg{
     hexagone1.valeur << Mean
     hexagone2.valeur << Mean
   }
 }
Retour en début de page  

Utilisation dans un scenario

Syntaxe

La définition d'une Relation permet de décrire un type de graphe d'interaction. Cela permet de construire une ou plusieurs variables contenant un exemplaire de graphe de ce type là (un graphe d'interaction est une instance de relation).

scenario MonModele {

// On obtient une liste de parcelles à partir d'un datafacer Shapefile
let shpData = new ShpDatafacer
let lparc  = shpData.readAll()

// Creation d'un graphe de type Voisin
let graphVoisinage = new Voisin

// Ajoute une liste de Parcelles au graphe
graphVoisinage.addAllParcelle(lparc)

...
}


Fonctions disponibles sur les graphes d'interaction

Lorsque l'on a initialisé une variable avec un graphe d'interaction, on dispose de deux sortes de fonctions qu'il est possible d'appliquer à ce graphe :

Fonctions prédéfinies

  • connect() : ajoute une arète retenue par un filtre au graphe
  • disconnect() : retire une arète parcourue du graphe
  • getComplete() : parcours les arètes (virtuelles) du graphe complet. Les arètes du graphe complet ne sont pas réellement ajoutées au graphe.
  • Integer size() : renvoie le nombre d'arètes du graphe

Les fonctions générées à partir de la définition de la relation

// Construit des arètes entre les parcelles voisines
//    (moins de 30m de distance entre elles)
graphVoisinage.complete.distance(30).connect()

Graphes d'interaction dont une entité contient un type Cell

Lors de l'utilisation d'une relation avec des entités non définies par une propriété de type Cell il faut explicitement, pour chaque entité utiliser la fonction connect(). Dans le cas d'une relation avec au moins une entité ayant une propriété Cell si les autres entités sont définies avec une Geometry ou un type Cell la connection est implicite par correspondance spatiale ou par voisinage. Il existe trois types de graphes implicites selon les formes de représentations utilisées :

  • Un graphe de voisinage sur les entités cellulaires d'un même type,
  • un graphe par correspondance spatiale sur des entités cellulaires dont le type diffère, et
  • un graphe entre des entités cellulaires et des entités définies par une géométrie.

Fonctions prédéfinies pour une relation avec le même type d'entités cellulaires

  • connect(List<Entité> entités) : Connecte automatiquement les entités cellulaires avec leurs voisines

  • Integer size() : renvoie le nombre d'arètes du graphe

Les fonctions générées à partir de la définition de la relation

  • createSquares(Geometry geom, Double resolutionX, Double resolutionY) : Création d'entités cellulaires de formes carrée ou rectangulaire. La résolution correspond au côté du carré. L'ensemble des entités est généré sur la zone correpondant a l'enveloppe de la géométrie passée en paramètre.
  • createHexagons(Geometry geom, Double resolution) : Création d'entités cellulaires de formes hexagonales régulière. La résolution correspond au côté d'un hexagone. L'ensemble des entités est généré sur la zone correpondant a l'enveloppe de la géométrie passée en paramètre.
  • createTriangles(Geometry geom, Double resolution) : Création d'entités cellulaires de forme triangulaire. La résolution correspond au côté du triangle. L'ensemble des entités est généré sur la zone correpondant a l'enveloppe de la géométrie passée en paramètre.
  • getAllNom_Entité() : renvoie la liste d'entités cellulaires créées avec une méthode createSquares(...), createHexagons(...) ou createTriangles(...), où Nom_Entité est le nom utilisé dans la définition du type d'entité.
  • extendedMoore(Integer distance) : Créer une distance de voisinage étendue en nombre de pixel sous la forme d'un voisinage de Moore.
  • extendedCircularMoore(Integer distance) : Créer une distance circulaire de voisinage étendue en nombre de pixel sous la forme d'un voisinage de Moore.

Fonctions prédéfinies pour une relation avec des types différents d'entités cellulaires

  • connect(List<Entité1> entités1, List<Entités2> entités2) : Connecte automatiquement les entités cellulaires de la première liste avec les entités cellulaires de la deuxième par correspondance spatiale.

  • Integer size() : renvoie le nombre d'arètes du graphe

Fonctions prédéfinies pour une relation avec entités cellulaires et des entités ayant une propriété de type Géométrique

  • connect(List<Entité1> entités1, List<Entités2> entités2) : Connecte automatiquement les entités cellulaires avec les entités géométriques par correspondance spatiale.
  • Integer size() : renvoie le nombre d'arètes du graphe

Exemple d'utilisation dans un scénario

 scenario MonModele {

 // On obtient une liste de parcelles à partir d'un datafacer Shapefile
 let shpData = new ShpDatafacer
 let lparc  = shpData.readAll()

 // Creation d'un graphe de type Voisin
 let graphVoisinage = new Voisin

 // Ajoute une liste de Parcelles au graphe
 graphVoisinage.addAllParcelle(lparc)

 //On obtient une liste de MntCellule à partir d'un datafacer RasterFile dont l'emprise est calculée sur celle du Shapefile
 let mnt = new Mnt
 let mntList = mnt.readAllMntCellule(shpData.boundaries)

 //On créait une relation de voisinnages entre les entités Hexagones
 let hexaVoisins = new HexagonVoisins
 //On créait les entités à partir de la relation précédente sur l'emprise du shapefile (hexagones avec des cotés de 100 mètres)
 hexaVoisins.createHexagons(shpData.boundaries, 100.0)
 //On obtient une liste d'Hexagones à partir de la relation
 let hexaList = hexaVoisins.getAllHexagone

 // Création de la relation entre Hexagones et Parcelles 
 let hexaParcelle = new HexagoneEtParcelle
 hexaParcelle.connect(hexaList, lparc)
 //On lance l'interaction pour attributer l'occupation du sol des parcelles aux hexagones
 hexaParcelle.setOcs

 // Création de la relation entre Hexagones et MntCellule 
 let mntHexa = new MntToHexagon
 mntHexa.connect(mntList, hexaList)
 //On lance l'interaction pour attributer l'altitude des MntCellules aux hexagones
 mntHexa.setValeurMnt 

 //On lance l'interaction sur les hexagones voisins pour calculer la moyenne de leurs valeurs sur l'hexagone central
 hexaVoisins.moyenneDesVoisins
 ...
 }

Retour en début de page