---
title: Axiomes et règles pour compléter le graphe des adresses
tags: charly-phd
---
# Axiomes et règles pour compléter le graphe des adresses
## Règles sur l'ordre des changements et des événements
Les implémentations se feront via SWRL ou SPARQL ou GraphDB ruleset...
| Enoncé | Exemple | Formalisation |
| -------- | -------- | -------- |
| Si un changement d'attribut conduit à la création d'une nouvelle version de cet attribut et que un autre changement d'attribut conduit à la disparition de cette version alors le premier changement précède le second dans le temps. Ceci est valable pour tous les types d'attributs: nom, géométrie, longueur, largeur, etc. | Les libellés "place du Trône", "place du Trône Renversé", "place du Trône" et "place de la Nation" sont les 4 versions succesives du nom de l'actuelle place de la Nation: chacun a disparu pour laisser la place à la version suivante. | addr:after(c1, x) ∧ addr:before(c2, x) ∧ addr:AttributeChange(c1) ∧ addr:AttributeChange(c2) -> time:after(c2,c1)
| Si un changement lié aux repères conduit à une création d'une ressource et qu'un autre changement de même nature conduit à la disparition de cette ressource, alors le premier changement précède le second dans le temps. | La rue Basse du Rempart est créée vers 1670 et supprimée en 1858: le changement conduisant à sa création précède celui conduisant à sa disparition (aujourd'hui, elle est remplacée par le boulevard des Capucines). | addr:isChangeType(c1, addr:LandmarkCreation) ∧ addr:isChangeType(c2, addr:LandmarkDissolution) ∧ addr:LandmarkChange(c1) ∧ addr:LandmarkChange(c2) ∧ addr:appliedTo(c1, lm) ∧ addr:appliedTo(c2, lm) -> time:after(c2,c1)
| Si un changement lié aux relations entre repères conduit à une création d'une ressource et qu'un autre changement de même nature conduit à la disparition de cette ressource, alors le premier changement précède le second dans le temps. | | addr:isChangeType(c1, addr:LandmarkRelationCreation) ∧ addr:isChangeType(c2, addr:LandmarkRelationDissolution) ∧ addr:LandmarkRelationChange(c1) ∧ addr:LandmarkRelationChange(c2) ∧ addr:appliedTo(c1, lmr) ∧ addr:appliedTo(c2, lmr) -> time:after(c2,c1)
| Si on a un premier changement qui conduit à la création d'une ressource (ex. un repère), suivi d'un deuxième changement qui conduit à autre chose que la création ou la dissolution de cette ressource, et qu'un troisième changement de même nature conduit à la disparition de cette ressource, alors le deuxième changement suit le premier et precéde le troisième. | La rue de Rivoli est percée en 1800 et prolongée en 1840. Le premier événement conduit à la création de la voie et de son attribut géométrie, le second conduit au prolongement de sa géométrie et un troisième événement, à ce jour fictif, à sa dissolution. Le second changement a lieu après le premier et avant le troisième.| addr:isChangeType(c1, addr:LandmarkCreation) ∧ addr:isChangeType(c2, ct) ∧ addr:isChangeType(c3, addr:LandmarkDissolution) ∧ ct != addr:LandmarkCreation ∧ ct != addr:LandmarkDissolution ∧ addr:LandmarkChange(c1) ∧ addr:LandmarkChange(c2) ∧ addr:LandmarkChange(c3) ∧ addr:appliedTo(c1, lm) ∧ addr:appliedTo(c2, lm) ∧ addr:appliedTo(c3, lm) -> time:after(c2,c1) ∧ time:after(c3,c2)
| Si un changement qui s'applique à un repère dépend d'un événement associé à un instant qui précède un autre instant lié à un événément lui même associé à un autre changement concernant le même repère, alors le premier changement précède le second dans le temps. | La rue de Rivoli est percée en 1800 et prolongée en 1840. Le premier événement conduit à de nombreux changements datés également en 1800: création de la voie, création d'une géométrie pour représenter la voie, création des numéros d'adresses dans la voie, etc. Le second conduit à d'autres changements, datés de 1840: création de nouveaux numéros d'adresses à la suite de ceux exsitants, création d'une nouvelle géométrie pour représenter la rue, etc. La création de la première géométrie à l'occasion du percement de la rue précède celle de la seconde, associée à l'extension de la rue.| addr:Change(c1) ∧ addr:dependsOn(c1, e1) ∧ addr:hasTime(e1, t1) ∧ addr:appliedTo(c1, lm) ∧ addr:Change(c2) ∧ addr:dependsOn(c2, e2) ∧ addr:hasTime(e2, t2) ∧ addr:appliedTo(c2, lm) ∧ time:after(t2, t1) -> time:after(c2, c1)
:warning: Changer les valeurs addr:after et addr:before
Remarques :
* garder [time:after](https://www.w3.org/TR/owl-time/#time:after) ? domain et range sont des `time:TemporalEntity`, ce que ne sont pas nos changements ?
* pour les changements qui sont contemporains, quelle propriété utiliser ?
* regrouper l'ensemble des changements liés à un repère (de manière directe et indirecte) : création / dissolution du repère, changement d'une valeur d'un de ses attributs, relation avec un autre repère (en tant que locatum)
* comment déterminer time:after(t1, t2) ? Requête SPARQL qui crée des liens `time:after` entre des changements liés à un repère via une valeur temporelle :
```
PREFIX addr: <http://rdf.geohistoricaldata.org/address#>
PREFIX time: <http://www.w3.org/2006/time#>
INSERT {
?c1 ?pred ?c2
} WHERE {
?c1 a addr:Change; addr:dependsOn [a addr:Event; addr:hasTime [addr:timePrecision ?tp; addr:timeCalendar ?tc; addr:timeStamp ?ts1]].
?c2 a addr:Change; addr:dependsOn [a addr:Event; addr:hasTime [addr:timePrecision ?tp; addr:timeCalendar ?tc; addr:timeStamp ?ts2]].
FILTER (?c1 != ?c2)
FILTER (?ts1 >= ?ts2)
BIND (IF(?ts1 = ?ts2, time:equals, time:after) AS ?pred)
?lm a addr:Landmark.
{?c1 addr:appliedTo ?lm.}
UNION
{?c1 addr:appliedTo ?attr1. ?lm addr:hasAttribute ?attr1.}
UNION
{?c1 addr:appliedTo ?lmr1. ?lm addr:locatum ?lmr1.}
{?c2 addr:appliedTo ?lm.}
UNION
{?c2 addr:appliedTo ?attr2. ?lm addr:hasAttribute ?attr2.}
UNION
{?c2 addr:appliedTo ?lmr2. ?lm addr:locatum ?lmr2.}
}
```
## SKOS concepts des changements
* `AttributeChange` :
* apparition (`addr:attrAppearance`) : pour un attribut, apparition d'une ou plusieurs versions (changement est relié à des versions uniquement via la propriété `addr:makesEffective`)
* disparition (`addr:attrDisappearance`) : pour un attribut, disparition d'une ou plusieurs versions (changement est relié à des versions uniquement via la propriété `addr:outdates`), le changement rend les versions caduques
* transition (`addr:attrTransition`) : pour un attribut, apparition et disparition d'une ou plusieurs versions (changement est relié à des versions via les propriétés `addr:makesEffective` ET `addr:outdates`)

L'image ci-dessus illustre l'évolution d'un attribut avec différents types de changements. La figure du bas illustre l'évolution des versions de l'attribut au fil du temps selon la modélisation de la figure du haut. Il est possible qu'un attribut possède plusieurs versions sur une période donnée et aucune sur une autre.
* `LandmarkRelationChange` :
* apparition : ce changement indique l'apparition (le début de validité) de la relation entre les repères
* disparition : ce changement indique la disparition (la fin de validité) de la relation entre les repères
* `LandmarkChange` :
* ***création** : apparition d'une identité pour le repère*
* **planification / conceptualisation** :
* décrit le moment où l'entité commence à être définie
* arrêté de création d'une voie
* déclaration d'utilité publique
* apparition de son emprise et son nom sur un plan de planification urbaine
* **apparition** :
* apparition physique de la voie, début d'existence de la voie, selon Hornsby et Egenhofer 2001, existence est liée à la présence physique de l'entité ou de sa perception pour les entités conceptuelles (que sont les communes par exemple). Ils citent l'exemple des USA qui est un concept et où son existence débute par un décret.
* pour les voies, apparition = processus durant lequel il y a une officialisation de son existence (décret de dénomination...). Pour un arrêté de création d'une entité, il peut y avoir deux dates : la date de l'arrêté / décision et sa date d'effet. La première est liée à la planification et la seconde à son apparition.
* **"mise à mort" / décision de disparition** :
* similaire à planification / conceptualisation mais permet d'annoncer que l'entité va disparaître.
* **disparition** :
* Hallot décrit l'absence d'un objet comme étant sa non-appartenance à l'espace de travail défini (qui est ici la Terre)
* suppression physique du repère ou perte de son identité. Si une rue n'existe plus physiquement, elle disparait. Si une rue est absorbée par une autre, elle disparait. Si deux (ou plus) rues fusionnent pour en former une nouvelle, elles disparaissent
* :warning: Pas de **réincarnation** possible !
Événements cités dans la [nomenclature des voies de Paris](https://opendata.paris.fr/explore/dataset/denominations-emprises-voies-actuelles/information/?disjunctive.siecle&disjunctive.statut&disjunctive.typvoie&disjunctive.arrdt&disjunctive.quartier&disjunctive.feuille&sort=nivellement) :
* ouverture : création de la voie
* dénomination : création ou mise à jour du nom
* classement
* nivellement
* assainissement
* numérotation
* alignement
* déclassement
Exemple avec la [rue de Rivoli](https://fr.wikipedia.org/wiki/Rue_de_Rivoli) :
* planification : 9 octobre 1801 (17 vendémiaire an X), un arrêté prévoit que « sera percée une rue dans toute la longueur du passage du Manège jusqu'à celle de Saint-Florentin ».
* apparition :
Définition de l'identité d'une voie :
* changement de nom + emprise géographique reste la même => même identité
* nom reste le même + emprise géographique change mais possède une partie identitique => même identité
## Règles sur les entités temporelles
Les implémentations se feront via SWRL ou SPARQL ou GraphDB ruleset...
### Comparaison d'instants
| Enoncé | Exemple | Formalisation |
| -------- | -------- | -------- |
| | | En cours de rédaction : t1 = addr:CrispTimeInstant ^ t2 = addr:CrispTimeInstant ^ addr:timeCalendar(t1, cal) ^ addr:timeCalendar(t2, cal) ^ addr:timePrecision(t1, prec) ^ addr:timePrecision(t2, prec)
### Déduction d'intervalles temporels de validité en fonction des changements
| Enoncé | Exemple | Formalisation |
| -------- | -------- | -------- |
| Si un changement décrit le fait qu'une version d'attribut est effective et que l'événement auquel est associé ce changement possède un instant temporel (flou ou non), alors cet instant décrit le début d'intervalle de validité de la version. | Un changement affirme que "place du Trône" est remplacé par "place de la Nation" pour l'attribut de type nom de la place de la Nation actuelle. Ce changement est associé à un événément datant de 1880 donc l'intervalle de validité de la version "place de la Nation" a pour début 1880. | vers = addr:AttributeVersion ^ cg = addr:AttributeChange ^ ev = addr:Event ^ tins = addr:TimeInstant ^ tint = addr:TimeInterval ^ addr:makesEffective(cg, vers) ^ addr:dependsOn(cg, ev) ^ addr:hasTime(ev, tins) ^ addr:hasTime(vers, tint) -> addr:hasBeginning(tint, tins)
| Si un changement décrit le fait qu'une version d'attribut est périmée et que l'événement auquel est associé ce changement possède un instant temporel (flou ou non), alors cet instant décrit la fin d'intervalle de validité de la version.|En prenant le même exemple que le précédent, on peut en déduire l'intervalle de validité de la version "place du Trône" a pour fin 1880.| vers = addr:AttributeVersion ^ cg = addr:AttributeChange ^ ev = addr:Event ^ tins = addr:TimeInstant ^ tint = addr:TimeInterval ^ addr:outdates(cg, vers) ^ addr:dependsOn(cg, ev) ^ addr:hasTime(ev, tins) ^ addr:hasTime(vers, tint) -> addr:hasEnd(tint, tins)
| Si un changement décrit le fait qu'une relation entre repères est effective et que l'événement auquel est associé ce changement possède un instant temporel (flou ou non), alors cet instant décrit le début d'intervalle de validité de la relation.|Un changement décrit que la route de Fontainebleau (actuelle avenue d'Italie) commence à être localisée dans Paris en 1860| lr = addr:LandmarkRelation ^ cg = addr:LandmarkRelationChange ^ ev = addr:Event ^ tins = addr:TimeInstant ^ tint = addr:TimeInterval ^ addr:isChangeType(cg, LandmarkRelationCreation) ^ addr:dependsOn(cg, ev) ^ addr:hasTime(ev, tins) ^ addr:hasTime(vers, tint) -> addr:hasBeginning(tint, tins)
| Si un changement décrit le fait qu'une relation entre repères est périmée et que l'événement auquel est associé ce changement possède un instant temporel (flou ou non), alors cet instant décrit le fin d'intervalle de validité de la relation.|Un changement décrit que la route de Fontainebleau (actuelle avenue d'Italie) n'est plus localisée à Gentilly en 1860| lr = addr:LandmarkRelation ^ cg = addr:LandmarkRelationChange ^ ev = addr:Event ^ tins = addr:TimeInstant ^ tint = addr:TimeInterval ^ addr:isChangeType(cg, LandmarkRelationDissolution) ^ addr:dependsOn(cg, ev) ^ addr:hasTime(ev, tins) ^ addr:hasTime(vers, tint) -> addr:hasEnd(tint, tins)
## Règles sur des raccourcis de propriétés
Certaines ressources sont liées indirectement à d'autres ressources. Il est des fois nécessaire de créer des raccourcis. Par exemple, la manière de représenter des adresses est comme ceci :
```
:Address :firstStep :Step1 .
:Step1 :nextStep :Step2 .
...
:Step2 :nextStep :StepN .
```
On voudrait donc des raccourcis pour avoir `:Address :hasStep :Step1`, `:Address :hasStep :Step2` et `:Address :hasStep :StepN`
Ces règles sont implémentées dans un fichier de règles de GraphDB ([`rules.pie`](https://github.com/charlybernard/phd-peuplement/blob/main/sources/rules.pie))
| Nom |Description | Clause HORN |
| -------- | -------- | -------- |
| `perso_rule_address_1` | La première étape d'une adresse est une étape | address = addr:Address ^ seg = addr:AddressSegment ^ addr:firstStep(address, seg) -> addr:firstStep(seg, address) |
| `perso_rule_address_2` | Une étape qui suit une autre étape d'une adresse et une étape de l'adresse | address = addr:Address ^ seg1 = addr:AddressSegment ^ seg2 = addr:AddressSegment ^ addr:isStepOf(seg1, address) ^ addr:nextStep(seg1, seg2) -> addr:isStepOf(seg2, address) |
## Règles des égalités de ressources
Certaines ressources sont équivalentes selon des règles définies, ce qui implique qui faut les lier par un lien `owl:sameAs`.
Ces règles sont implémentées dans un fichier de règles de GraphDB ([`rules.pie`](https://github.com/charlybernard/phd-peuplement/blob/main/sources/rules.pie))
| Nom |Description | Clause HORN |
| -------- | -------- | -------- |
| `Attribute` | Deux attributs d'un même repère qui sont de même type sont la même attribut | attr1 = addr:Attribute ^ attr2 = addr:Attribute ^ addr:hasAttribute(lm, attr1) ^ addr:hasAttribute(lm, attr2) ^ addr:isAttributeType(attr1, attrType) ^ addr:isAttributeType(attr2, attrType) -> owl:sameAs(attr1, attr2) |
| `Change` | Deux changements de même type qui dépendent d'un même événement et qui sont appliqués au même éléments sont le même changement | cg1 = addr:Change ^ cg2 = addr:Change ^ addr:dependsOn(cg1, ev) ^ addr:dependsOn(cg2, ev) ^ addr:appliedTo(cg1, elem) ^ addr:appliedTo(cg2, elem) ^ addr:isChangeType(cg1, cgType) ^ addr:isChangeType(cg1, cgType) -> owl:sameAs(cg1, cg2) |
| `LandmarkChange` | Deux changements appliqués à un repère de même type sont le même changement (ie un type de changement n'est appliqué qu'une seule fois à un repère) | cg1 = addr:LandmarkChange ^ cg2 = addr:LandmarkChange ^ addr:appliedTo(cg1, lm) ^ addr:appliedTo(cg2, lm) ^ addr:isChangeType(cg1, cgType) ^ addr:isChangeType(cg1, cgType) -> owl:sameAs(cg1, cg2) |
| `LRChange` (landmark relation change) | Deux changements appliqués à une relation entre repères de même type sont le même changement (ie un type de changement n'est appliqué qu'une seule fois à une relation entre repères) | cg1 = addr:LandmarkRelationChange ^ cg2 = addr:LandmarkRelationChange ^ addr:appliedTo(cg1, lr) ^ addr:appliedTo(cg2, lr) ^ addr:isChangeType(cg1, cgType) ^ addr:isChangeType(cg1, cgType) -> owl:sameAs(cg1, cg2) |
| `AttributeChange` (1) | Deux changements appliqués à un attribut qui indique la mise en effet d'une même version d'attribut sont le même changement | cg1 = addr:AttributeChange ^ cg2 = addr:AttributeChange ^ addr:makesEffective(cg1, attrVers) ^ addr:makesEffective(cg2, attrVers) -> owl:sameAs(cg1, cg2) |
| `AttributeChange` (2) | Deux changements appliqués à un attribut qui indique la péremption d'une même version d'attribut sont le même changement | cg1 = addr:AttributeChange ^ cg2 = addr:AttributeChange ^ addr:appliedTo(cg1, attr) ^ addr:appliedTo(cg2, attr) ^ addr:outdates(cg1, attrVers) ^ addr:outdates(cg2, attrVers) -> owl:sameAs(cg1, cg2) |
| `Event` | Deux événéments dont dépendent un même changement sont le même événement (ie un changement dépend d'un seul événément) | cg = addr:Change ^ ev1 = addr:Event ^ ev2 = addr:Event ^ addr:dependsOn(cg, ev1) ^ addr:dependsOn(cg, ev2) -> owl:sameAs(ev1, ev2) |
| `CrispTimeInstant` | Deux instants précis sont le même instant s'ils décrivent la valeur temporelle d'un même événement et s'ils ont les mêmes timestamps, précisions et calendriers. | ev = addr:Event ^ t1 = addr:CrispTimeInstant ^ t2 = addr:CrispTimeInstant ^ addr:hasTime(t1, ev) ^ addr:hasTime(t2, ev) ^ addr:timeCalendar(t1, cal) ^ addr:timeCalendar(t2, cal) ^ addr:timePrecision(t1, prec) ^ addr:timePrecision(t2, prec) ^ addr:timeStamp(t1, stamp) ^ addr:timeStamp(t2, stamp) -> owl:sameAs(t1, t2) |
Conversion des règles en requêtes SPARQL :
* `Attribute` :
```
PREFIX addr: <http://rdf.geohistoricaldata.org/def/address#>
PREFIX facts: <http://rdf.geohistoricaldata.org/id/address/facts/>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX owl: <http://www.w3.org/2002/07/owl#>
INSERT {
?attr1 owl:sameAs ?attr2.
}
WHERE {
?attr1 a addr:Attribute; addr:isAttributeType ?attrType.
?attr2 a addr:Attribute; addr:isAttributeType ?attrType.
?lm addr:hasAttribute ?attr1, ?attr2.
FILTER (?attr1 != ?attr2)
}
```
* `Change` :
```
PREFIX addr: <http://rdf.geohistoricaldata.org/def/address#>
PREFIX facts: <http://rdf.geohistoricaldata.org/id/address/facts/>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX owl: <http://www.w3.org/2002/07/owl#>
INSERT {
?cg1 owl:sameAs ?cg2.
}
WHERE {
?cg1 a addr:Change; addr:isChangeType ?cgType; addr:dependsOn ?ev; addr:appliedTo ?elem.
?cg2 a addr:Change; addr:isChangeType ?cgType; addr:dependsOn ?ev; addr:appliedTo ?elem.
FILTER (?cg1 != ?cg2)
}
```
* `LandmarkChange` :
```
PREFIX addr: <http://rdf.geohistoricaldata.org/def/address#>
PREFIX facts: <http://rdf.geohistoricaldata.org/id/address/facts/>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX owl: <http://www.w3.org/2002/07/owl#>
INSERT {
?cg1 owl:sameAs ?cg2.
}
WHERE {
?cg1 a addr:LandmarkChange; addr:isChangeType ?cgType; addr:appliedTo ?elem.
?cg2 a addr:LandmarkChange; addr:isChangeType ?cgType; addr:appliedTo ?elem.
FILTER (?cg1 != ?cg2)
}
```
* `LandmarkRelationChange` :
```
PREFIX addr: <http://rdf.geohistoricaldata.org/def/address#>
PREFIX facts: <http://rdf.geohistoricaldata.org/id/address/facts/>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX owl: <http://www.w3.org/2002/07/owl#>
INSERT {
?cg1 owl:sameAs ?cg2.
}
WHERE {
?cg1 a addr:LandmarkRelationChange; addr:isChangeType ?cgType; addr:appliedTo ?elem.
?cg2 a addr:LandmarkRelationChange; addr:isChangeType ?cgType; addr:appliedTo ?elem.
FILTER (?cg1 != ?cg2)
}
```
* `AttributeChange` (1) :
```
PREFIX addr: <http://rdf.geohistoricaldata.org/def/address#>
PREFIX facts: <http://rdf.geohistoricaldata.org/id/address/facts/>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX owl: <http://www.w3.org/2002/07/owl#>
INSERT {
?cg1 owl:sameAs ?cg2.
}
WHERE {
?cg1 a addr:AttributeChange; addr:appliedTo ?elem; addr:makesEffective ?attrVersion.
?cg2 a addr:AttributeChange; addr:appliedTo ?elem; addr:makesEffective ?attrVersion.
FILTER (?cg1 != ?cg2)
}
```
* `AttributeChange` (2) :
```
PREFIX addr: <http://rdf.geohistoricaldata.org/def/address#>
PREFIX facts: <http://rdf.geohistoricaldata.org/id/address/facts/>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX owl: <http://www.w3.org/2002/07/owl#>
INSERT {
?cg1 owl:sameAs ?cg2.
}
WHERE {
?cg1 a addr:AttributeChange; addr:appliedTo ?elem; addr:outdates ?attrVersion.
?cg2 a addr:AttributeChange; addr:appliedTo ?elem; addr:outdates ?attrVersion.
FILTER (?cg1 != ?cg2)
}
```
* `Event` :
```
PREFIX addr: <http://rdf.geohistoricaldata.org/def/address#>
PREFIX facts: <http://rdf.geohistoricaldata.org/id/address/facts/>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX owl: <http://www.w3.org/2002/07/owl#>
INSERT {
?ev1 owl:sameAs ?ev2.
}
WHERE {
?ev1 a addr:Event.
?ev2 a addr:Event.
?cg addr:dependsOn ?ev1, ?ev2.
FILTER (?ev1 != ?ev2)
}
```
* `CrispTimeInstant` :
```
PREFIX addr: <http://rdf.geohistoricaldata.org/def/address#>
PREFIX facts: <http://rdf.geohistoricaldata.org/id/address/facts/>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX owl: <http://www.w3.org/2002/07/owl#>
INSERT {
?ti1 owl:sameAs ?ti2.
}
WHERE {
?ti1 a addr:CrispTimeInstant; addr:timeStamp ?timeStamp; addr:timePrecision ?timePrec; addr:timeCalendar ?timeCal.
?ti2 a addr:CrispTimeInstant; addr:timeStamp ?timeStamp; addr:timePrecision ?timePrec; addr:timeCalendar ?timeCal.
FILTER (?ti1 != ?ti2)
}
```
Pour être plus propre, on peut utiliser cette requête :
```
PREFIX : <http://rdf.geohistoricaldata.org/def/address#>
PREFIX time: <http://www.w3.org/2006/time#>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
INSERT {
?t1 owl:sameAs ?t2.
}
WHERE {
?ev :hasTime ?t1, ?t2.
?t1 a :CrispTimeInstant; :timeStamp ?ts1; :timeCalendar ?tc; :timePrecision ?tp.
?t2 a :CrispTimeInstant; :timeStamp ?ts2; :timeCalendar ?tc; :timePrecision ?tp.
FILTER(?t1 != ?t2)
BIND(YEAR(?ts1) = YEAR(?ts2) AS ?sameYear)
BIND(MONTH(?ts1) = MONTH(?ts2) AS ?sameMonth)
BIND(DAY(?ts1) = DAY(?ts2) AS ?sameDay)
BIND(IF(?tp = time:unitMillenium, FLOOR(YEAR(?ts1)/1000) = FLOOR(YEAR(?ts2)/1000),
IF (?tp = time:unitCentury, FLOOR(YEAR(?ts1)/100) = FLOOR(YEAR(?ts2)/100),
IF (?tp = time:unitDecade, FLOOR(YEAR(?ts1)/10) = FLOOR(YEAR(?ts2)/10),
IF (?tp = time:unitYear, ?sameYear,
IF (?tp = time:unitMonth, ?sameMonth && ?sameYear,
IF (?tp = time:unitDay, ?sameDay && ?sameMonth && ?sameYear, "false"^^xsd:boolean)))))) AS ?sameTime)
FILTER (?sameTime)
}
```
Contrairement à la requête précédente, la contrainte du timeStamp est plus souple, si on a les timestamps "2020-01-12" et "2020-01-04" et que la précision temporelle porte sur le mois, alors on va considérer que les valeurs temporelles sont les mêmes. `?ev :hasTime ?t1, ?t2.` est inséré dans la requête car deux valeurs sont temporelles sont les mêmes si elles appartiennent au même événément.
* Tri de valeurs temporelles (instants) :
```
PREFIX time: <http://www.w3.org/2006/time#>
PREFIX : <http://rdf.geohistoricaldata.org/def/address#>
INSERT {
?time1 time:before ?time2.
} WHERE {
?event ?timePredicate ?time1, ?time2.
FILTER(?timePredicate IN (:hasLatestTimeInstant, :hasEarliestTimeInstant))
?time1 :timeStamp ?ts1; :timeCalendar ?tc; :timePrecision ?tp.
?time2 :timeStamp ?ts2; :timeCalendar ?tc; :timePrecision ?tp.
FILTER(?ts1 < ?ts2)
}
```
## Tri sur des changements
| Enoncé | Formalisation | Exemple |
| -------- | -------- | -------- |
| Le changement lié à l'apparition d'un repère précède celui lié à sa disparition | `lm = addr:Landmark ^ cg1 = addr:Change ^ cg2 = addr:Change ^ addr:isChangeType(cg1, ctype:LandmarkAppearance) ^ addr:isChangeType(cg1, ctype:LandmarkDisappearance) ^ addr:appliedTo(cg1, lm) ^ addr:appliedTo(cg2, lm) => addr:before(cg1, cg2)` | La disparition de la place du Trône arrive après l'apparition de cette place.
| Le changement lié à l'apparition d'une relation entre repères précède celui lié à sa disparition | `lr = addr:LandmarkRelation ^ cg1 = addr:Change ^ cg2 = addr:Change ^ addr:isChangeType(cg1, ctype:LandmarkRelationAppearance) ^ addr:isChangeType(cg2, ctype:LandmarkRelationDisappearance) ^ addr:appliedTo(cg1, lr) ^ addr:appliedTo(cg2, lr) => addr:before(cg1, cg2)` | L'apparition de la relation "rue Gérard est dans Gentilly" arrive avant sa disparition
| Le changement lié à l'apparition d'une relation entre repères succède (ou est contemporain) aux changements liés à l'apparition des repères en jeu | `lm = addr:LandmarkRelation ^ lr = addr:Landmark ^ (addr:locatum(?lm, ?lr) ∨ addr:relatum(?lm, ?lr)) ^ cg1 = addr:Change ^ cg2 = addr:Change ^ addr:isChangeType(cg1, ctype:LandmarkRelationAppearance) ^ addr:isChangeType(cg2, ctype:LandmarkAppearance) ^ addr:appliedTo(cg1, lr) ^ addr:appliedTo(cg2, lm) => addr:after(cg1, cg2)` | L'apparition de la relation "rue Gérard est dans Gentilly" arrive après (ou au même moment) que l'apparition de la rue Gérard et de Gentilly.
| Le changement lié à la disparition d'une relation entre repères précède (ou est contemporain) aux changements liés à la disparition des repères en jeu | `lm = addr:LandmarkRelation ^ lr = addr:Landmark ^ (addr:locatum(?lm, ?lr) ∨ addr:relatum(?lm, ?lr)) ^ cg1 = addr:Change ^ cg2 = addr:Change ^ addr:isChangeType(cg1, ctype:LandmarkRelationDisappearance) ^ addr:isChangeType(cg2, ctype:LandmarkDisappearance) ^ addr:appliedTo(cg1, lr) ^ addr:appliedTo(cg2, lm) => addr:before(cg1, cg2)` | La disparition de la relation "rue Gérard est dans Gentilly" arrive avant (ou au même moment) que la disparition de la rue Gérard et de Gentilly.
| Un changement annonçant l'expiration d'une version d'attribut succède à un changement annonçant l'effectivité de cette version |`av = AttributeVersion ^ cg1 = addr:Change ^ cg2 = addr:Change ^ addr:makesEffective(cg1, av) ^ addr:outdates(cg2, av) => addr:before(cg1, cg2)` | Le changement qui indique que le code INSEE 75037 associé à Gentilly n'est plus valable est précédé par le changement qui indique que ce même code INSEE est valable |
Le tri sur les changements est propagé aux événenements dont ils dépendent : si deux changements cgA et cgB dépendent respectivement d'événements evA et evB et que *cgA before cgB* alors *evA before evB*. L'inverse est valable aussi.
## Algèbre des intervalles d'Allen
Dans notre ontologie, un intervalle temporel est composé deux instants (de début et de fin) liés par `:hasBeginning` et `:hasEnd`.
```facts:TimeIntervalExample a :CrispTimeInterval ;
:hasBeginning facts:InstantBeg ;
:hasEnd facts:InstantEnd ;
```
| Lien | Règle SWRL | SPARQL |
| -------- | -------- | -------- |
| before | `CrispTimeInterval(i1) ^ CrispTimeInterval(i2) ^ hasEnd(i1, i1beg) ^ hasBeginning(i2, i2beg) ^ time:before(i1end, i2beg) -> time:intervalBefore(i1, i2)` | Voir `query_before` |
| meets | `CrispTimeInterval(i1) ^ CrispTimeInterval(i2) ^ hasEnd(i1, i1beg) ^ hasBeginning(i2, i2beg) ^ time:same(i1end, i2beg) -> time:intervalMeets(i1, i2)` | Voir `query_meets` |
| overlaps | `CrispTimeInterval(i1) ^ CrispTimeInterval(i2) ^ hasBeginning(i1, i1beg) ^ hasEnd(i1, i1beg) ^ hasBeginning(i2, i2beg) ^ hasEnd(i2, i2beg) ^ time:before(i1beg, i2beg) ^ time:after(i1end, i2beg) ^ time:before(i1end, i2end) -> time:intervalOverlaps(i1, i2)` | Voir `query_overlaps` |
| starts | `CrispTimeInterval(i1) ^ CrispTimeInterval(i2) ^ hasBeginning(i1, i1beg) ^ hasEnd(i1, i1beg) ^ hasBeginning(i2, i2beg) ^ hasEnd(i2, i2beg) ^ time:same(i1beg, i2beg) ^ time:before(i1end, i2end) -> time:intervalStarts(i1, i2)` | Voir `query_starts` |
| during | `CrispTimeInterval(i1) ^ CrispTimeInterval(i2) ^ hasBeginning(i1, i1beg) ^ hasEnd(i1, i1beg) ^ hasBeginning(i2, i2beg) ^ hasEnd(i2, i2beg) ^ time:after(i1beg, i2beg) ^ time:before(i1end, i2end) -> time:intervalDuring(i1, i2)` | Voir `query_during` |
| finishes | `CrispTimeInterval(i1) ^ CrispTimeInterval(i2) ^ hasBeginning(i1, i1beg) ^ hasEnd(i1, i1beg) ^ hasBeginning(i2, i2beg) ^ hasEnd(i2, i2beg) ^ time:after(i1beg, i2beg) ^ time:same(i1end, i2end) -> time:intervalFinishes(i1, i2)` | Voir `query_finishes` |
| equals | `CrispTimeInterval(i1) ^ CrispTimeInterval(i2) ^ hasBeginning(i1, i1beg) ^ hasEnd(i1, i1beg) ^ hasBeginning(i2, i2beg) ^ hasEnd(i2, i2beg) ^ time:same(i1beg, i2beg) ^ time:same(i1end, i2end) -> time:intervalEquals(i1, i2)` | Voir `query_equals` |
### `query_before`
```
INSERT {
?i1 time:intervalsBefore ?i2
}
WHERE {
?i1 a addr:CrispTimeInterval ; addr:hasEnd ?i1end .
?i2 a addr:CrispTimeInterval ; addr:hasBeginning ?i2beg .
?i1end time:before ?i2beg .
}
```
### `query_meets`
```
INSERT {
?i1 time:intervalsMeets ?i2
}
WHERE {
?i1 a addr:CrispTimeInterval ; addr:hasEnd ?i1end .
?i2 a addr:CrispTimeInterval ; addr:hasBeginning ?i2beg .
?i1end time:same ?i2beg .
}
```
### `query_overlaps`
```
INSERT {
?i1 time:intervalOverlaps ?i2
}
WHERE {
?i1 a addr:CrispTimeInterval ; addr:hasBeginning ?i1beg ; addr:hasEnd ?i1end .
?i2 a addr:CrispTimeInterval ; addr:hasBeginning ?i2beg ; addr:hasEnd ?i2end .
?i1beg time:before ?i2beg .
?i1end time:after ?i2beg .
?i1end time:before ?i2end .
}
```
### `query_starts`
```
INSERT {
?i1 time:intervalStarts ?i2
}
WHERE {
?i1 a addr:CrispTimeInterval ; addr:hasBeginning ?i1beg ; addr:hasEnd ?i1end .
?i2 a addr:CrispTimeInterval ; addr:hasBeginning ?i2beg ; addr:hasEnd ?i2end .
?i1beg time:same ?i2beg .
?i1end time:before ?i2end .
}
```
### `query_during`
```
INSERT {
?i1 time:intervalDuring ?i2
}
WHERE {
?i1 a addr:CrispTimeInterval ; addr:hasBeginning ?i1beg ; addr:hasEnd ?i1end .
?i2 a addr:CrispTimeInterval ; addr:hasBeginning ?i2beg ; addr:hasEnd ?i2end .
?i1beg time:after ?i2beg .
?i1end time:before ?i2end .
}
```
### `query_finishes`
```
INSERT {
?i1 time:intervalFinishes ?i2
}
WHERE {
?i1 a addr:CrispTimeInterval ; addr:hasBeginning ?i1beg ; addr:hasEnd ?i1end .
?i2 a addr:CrispTimeInterval ; addr:hasBeginning ?i2beg ; addr:hasEnd ?i2end .
?i1beg time:after ?i2beg .
?i1end time:same ?i2end .
}
```
### `query_equals`
```
INSERT {
?i1 time:intervalEquals ?i2
}
WHERE {
?i1 a addr:CrispTimeInterval ; addr:hasBeginning ?i1beg ; addr:hasEnd ?i1end .
?i2 a addr:CrispTimeInterval ; addr:hasBeginning ?i2beg ; addr:hasEnd ?i2end .
?i1beg time:same ?i2beg .
?i1end time:same ?i2end .
}
```
## Trier les versions
### Mettre en relation temporelle les versions
Pour trier les versions, on met en relation chaque version d'attribut selon l'[algèbre des intervalles d'Allen](https://fr.wikipedia.org/wiki/Alg%C3%A8bre_des_intervalles_d%27Allen). Ces requêtes permettent de créer ces relations :
* trier les instants compris dans intervalles (hasBeginning et hasEnd)
* trier les intervalles à partir des instants
Détection des (in)égalités d'instants de changements sur les attributs, la requête tri les instants liés à un attribut (instants des événéments liés à des changements appliqués à un attribut) :
```
PREFIX facts: <http://rdf.geohistoricaldata.org/id/address/facts/>
PREFIX : <http://rdf.geohistoricaldata.org/def/address#>
PREFIX time: <http://www.w3.org/2006/time#>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
INSERT {
GRAPH ?g {
?ti1 ?timeProp ?ti2
}
}
WHERE {
BIND(<http://localhost:7200/repositories/addresses_from_factoids/rdf-graphs/time> AS ?g)
{
?attr a :Attribute ; :changedBy ?cg1, ?cg2.
?cg1 a :AttributeChange ; :dependsOn [?tpred1 ?ti1] .
?cg2 a :AttributeChange ; :dependsOn [?tpred2 ?ti2] .
} UNION {
?ev a :Event ; ?tpred1 ?ti1 ; ?tpred2 ?ti2 .
}
?ti1 a :CrispTimeInstant; :timeStamp ?ts1; :timeCalendar ?tc; :timePrecision ?tp1.
?ti2 a :CrispTimeInstant; :timeStamp ?ts2; :timeCalendar ?tc; :timePrecision ?tp2.
BIND(?tp1 = ?tp2 AS ?samePrecision)
BIND(YEAR(?ts1) = YEAR(?ts2) AS ?sameYear)
BIND(MONTH(?ts1) = MONTH(?ts2) AS ?sameMonth)
BIND(DAY(?ts1) = DAY(?ts2) AS ?sameDay)
FILTER(?ts1 <= ?ts2)
BIND(IF(time:unitMillenium in (?tp1, ?tp2), FLOOR(YEAR(?ts1)/1000) = FLOOR(YEAR(?ts2)/1000),
IF(time:unitCentury in (?tp1, ?tp2), FLOOR(YEAR(?ts1)/100) = FLOOR(YEAR(?ts2)/100),
IF(time:unitDecade in (?tp1, ?tp2), FLOOR(YEAR(?ts1)/10) = FLOOR(YEAR(?ts2)/10),
IF(time:unitYear in (?tp1, ?tp2), ?sameYear,
IF(time:unitMonth in (?tp1, ?tp2), ?sameYear && ?sameMonth,
IF(time:unitDay in (?tp1, ?tp2), ?sameYear && ?sameMonth && ?sameDay,
"false"^^xsd:boolean)
))))) AS ?sameTime)
BIND(IF(?sameTime, time:same, time:before) AS ?timeProp)
}
```
Déduction des instants au plus tôt et au plus tard pour des événements (pour la construction d'intervalles) :
```
PREFIX facts: <http://rdf.geohistoricaldata.org/id/address/facts/>
PREFIX : <http://rdf.geohistoricaldata.org/def/address#>
PREFIX time: <http://www.w3.org/2006/time#>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
INSERT {
GRAPH ?g {
?ev ?estPred ?t
}
}
WHERE {
BIND(<http://localhost:7200/repositories/addresses_from_factoids/rdf-graphs/time> AS ?g)
{
BIND(:hasEarlierTimeInstant AS ?erPred)
BIND(:hasEarliestTimeInstant AS ?estPred)
BIND(time:after AS ?compPred)
} UNION {
BIND(:hasLaterTimeInstant AS ?erPred)
BIND(:hasLatestTimeInstant AS ?estPred)
BIND(time:before AS ?compPred)
}
?ev a :Event ; ?erPred ?t
MINUS {
?ev ?erPred ?tbis .
?tbis ?compPred ?t .
}
}
```
### Obtenir des intervalles de validité
Pour chaque version d'attribut, obtenir des intervalles de validité et les insérer dans un graphe nommé lié au temps.
```
PREFIX facts: <http://rdf.geohistoricaldata.org/id/address/facts/>
PREFIX : <http://rdf.geohistoricaldata.org/def/address#>
PREFIX time: <http://www.w3.org/2006/time#>
INSERT {
GRAPH ?g {
?av :hasTime ?timeInterval .
?timeInterval a :CrispTimeInterval ; :hasBeginning ?ti1 ; :hasEnd ?ti2 .
}
}
WHERE {
BIND(<http://localhost:7200/repositories/addresses_from_factoids/rdf-graphs/time> AS ?g)
?av a :AttributeVersion; :isMadeEffectiveBy ?cg1; :isOutdatedBy ?cg2.
?cg1 a :AttributeChange; :dependsOn ?ev1.
?cg2 a :AttributeChange; :dependsOn ?ev2.
OPTIONAL {?ev1 :hasTime ?tip1}
OPTIONAL {?ev2 :hasTime ?tip2}
OPTIONAL {?ev1 :hasLatestTimeInstant ?til1 .}
OPTIONAL {?ev2 :hasLatestTimeInstant ?til2 .}
OPTIONAL {?ev1 :hasEarliestTimeInstant ?tie1 .}
OPTIONAL {?ev2 :hasEarliestTimeInstant ?tie2 .}
BIND(IF(BOUND(?tip1), ?tip1, IF(BOUND(?til1), ?til1, IF(BOUND(?tie1), ?tie1, facts:NULL_1))) AS ?ti1)
BIND(IF(BOUND(?tip2), ?tip2, IF(BOUND(?til2), ?til2, IF(BOUND(?tie2), ?tie2, facts:NULL_2))) AS ?ti2)
?ti1 a :TimeInstant .
?ti2 a :TimeInstant .
BIND(URI(CONCAT(STR(URI(facts:)), "TI_", STRUUID())) AS ?timeInterval)
}
```
### Sélection des versions successives
```
SELECT * WHERE {
?attr a :Attribute ; :hasAttributeVersion ?v1, ?v2 .
FILTER(?v1 != ?v2)
?v1 :hasTime ?i1 .
?v2 :hasTime ?i2 .
?i1 time:intervalBefore ?i2
MINUS {
?attr :hasAttributeVersion ?v3 .
?v3 :hasTime ?i3 .
FILTER(?v1 != ?v3)
FILTER(?v2 != ?v3)
?i1 time:intervalBefore ?i3 .
?i2 time:intervalAfter ?i3 .
}
}
```
Pour une version vj, on note son ij intervalle de validité, cbeg_j et cend_j les changements qui indiquent respectivement le début et la fin de validité de vj.
Soit deux versions v1 et v2 d'intervalles respectifs i1 et i2 :
* si les deux versions sont égales (`<v1 :sameVersionValue v2>`) :
* si `<i1 prop i2>` où `prop in (time:intervalMeets, time:intervalOverlaps, time:intervalEquals, time:intervalDuring, time:intervalStarts, time:intervalFinishes)` alors `<v1 owl:sameAs v2>`, ie deux versions de même valeur dont les intervalles ont au moins un instant communs sont les mêmes versions ;
* si `<i1 time:intervalBefore i2>` et s'il existe une version v3 d'intervalle i3 tel que `<v1 :differentVersionValue v3>`, `<v2 :differentVersionValue v3>`, alors `<v1 owl:differentFrom v2>`