Makina Blog
Enrichissement de données ouvertes, croisement spatial et web sémantique
Un cas pratique basé sur le besoin d’avoir des informations sur des monuments classés à l’UNESCO, l’utilisation du web sémantique est ici explorée avec Wikidata et DATAtourisme ainsi que le croisement de données ouvertes (OpenData) dans des formats classiques comme le CSV ou encore avec OpenStreetMap.
Plusieurs approches ou outils pour la récupération et le croisement de données sont possibles. Les traitements ont été faits ici avec des outils en ligne de commande tels que « curl » pour le téléchargement, « csvkit » ou les manipulations de CSV et « ogr2ogr » ou les données géographiques.
Parmi les sites classés à l’UNESCO, il y en a un d’un peu particulier nommé « Chemins de Compostelle en France ». Celui-ci est composé de 71 constructions (cathédrales, églises, ponts…) et 7 tronçons de chemin répartis en France.
Ici, nous recherchons à obtenir des informations sur les différentes « composantes » de ce « site » et leurs environnements immédiats en utilisant et croisant des sources de données libres et ouvertes.
Web sémantique et WikidataIls nous l’ont longtemps promis, mais aujourd’hui le web sémantique est disponible et utilisable, notamment via Wikidata. Ce dernier est un projet de la Fondation Wikimédia, dérivé de Wikipédia. Tous ceux qui ont déjà essayé de retraiter de façon automatique le contenu de Wikipédia savent combien c'est compliqué, en particulier pour en tirer des informations structurées. C’est aujourd’hui possible grâce à Wikidata.
Wikidata est une base de données mais pas au sens le plus classique du terme. Il s'agit d'un graphe d’information structurée. Il est composé de « triplets » : sujet, prédicat, objet. Tout y est décrit suivant ce formalisme, par exemple :
- Le sujet « Linux » (Q14579) a pour prédicat « nommé depuis » (P138) l’objet « Linus Torvalds » (Q34253),
- Le sujet « Linus Torvalds » (Q34253) a pour prédicat « date de naissance » (P569) la valeur « 28 décembre 1969 ».
Ces informations servent par exemple à créer les boîtes d’information sur les pages de Wikipédia.
Des recherches sur ce graphe pour répondre à des questions peuvent également être faites. Le langage pour faire de telles requêtes est le « SPARQL ». Wikidata dispose d’une API et d’une interface web pour l’interroger : Wikidata Query Service. L’interface permet d’écrire, d’exécuter et de visualiser les résultats de requêtes, mais aussi de charger des exemples de requêtes toutes faites.
Nous pouvons nous poser la question suivante : quelle est la date de naissance de « Linus Torvalds » ?
SELECT ?date WHERE { # Sujet « Linus Torvalds » (Q34253) # Prédicat « date de naissance » (P569) # Variable de l’objet recherché : ?date wd:Q34253 wdt:P569 ?date. }Obtenir les composantes classées et leurs détails depuis Wikidata
Notre point de départ est l’entité Wikidata du « site » classé à l’UNESCO. Nous pouvons par exemple le retrouver sur la page Wikipédia : Q2962473, c’est un identifiant unique et stable, indépendant de la langue.
Les premières propriétés de la fiche Wikidata Q2962473.
En premier lieu, la propriété qui va nous intéresser est celle du prédicat « à pour partie » (P527) qui a autant d’instances que de composantes. La requête suivante va donc nous les renvoyer toutes.
SELECT ?compo ?compoLabel WHERE { wd:Q2962473 wdt:P527 ?compo. }
wd:Q20883 |
wd:Q106934 |
wd:Q207985 |
wd:Q217452 |
wd:Q222501 |
wd:Q333850 |
[…] |
Certes le résultat est là, mais peu lisible. Nous allons utiliser en complément un service de Wikidata qui permet de retourner le nom textuel en français correspondant. Ce service recherche un intitulé pour les variables suffixées par « Label ».
SELECT ?compo ?compoLabel WHERE { wd:Q2962473 wdt:P527 ?compo. SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE],fr". } }
wd:Q20883 | mont Saint-Michel |
wd:Q106934 | cathédrale Notre-Dame d'Amiens |
wd:Q207985 | cathédrale Saint-Étienne de Bourges |
wd:Q217452 | basilique Sainte-Marie-Madeleine |
wd:Q222501 | église Saint-Pierre d'Aulnay |
[…] |
Le résultat se lit donc : Le sujet « Chemins de Compostelle en France » (Q2962473) a comme parties le « mont Saint-Michel » (Q20883), la « cathédrale Notre-Dame d'Amiens » (Q106934)…
Chaque composante est elle-même un objet qui possède des propriétés. Celles-ci sont variables suivant les types d’objets et les objets eux-mêmes.
Pour chaque composante, nous souhaitons ensuite récupérer la valeur de certaines propriétés comme les coordonnées géographiques, la commune de localisation, le site web, le type d’architecture, etc. Nous allons continuer en prenant pour exemple les plans des composantes. Ce sont en fait les URL des images de ces plans vers Wikimedia Commons (projet frère de Wikipédia pour stocker les médias).
Nous repartons donc des composantes pour obtenir la valeur de la propriété « image du plan » (P3311).
SELECT ?compo ?plan_image WHERE { wd:Q2962473 wdt:P527 ?compo. ?compo wdt:P3311 ?plan_image. }
wd:Q207985 | commons:Plan.cathedrale.Bourges.png |
wd:Q333850 | commons:F07 Cadouin Grundriss.jpg |
wd:Q333984 | commons:Plan de l'abbaye (Drouyn 1851).png |
wd:Q770459 | commons:Plan.eglise.ND.du.port.Clermont.Ferrand.png |
wd:Q847628 | commons:Plan.cathedrale.Cahors.png |
[…] | […] |
Toutefois cette requête ne permet d’obtenir que les composantes qui ont un plan, ce qui correspond à un « INNER JOIN » en SQL. Or, nous voulons conserver toutes les composantes, même celles sans plan (« LEFT JOIN » en SQL). On va donc rendre le plan optionnel.
SELECT ?compo ?plan_image WHERE { wd:Q2962473 wdt:P527 ?compo. OPTIONAL { ?compo wdt:P3311 ?plan_image. } }
wd:Q222501 | |
wd:Q333850 | commons:F07 Cadouin Grundriss.jpg |
wd:Q333984 | commons:Plan de l'abbaye (Drouyn 1851).png |
wd:Q334150 | |
wd:Q334217 | |
[…] | […] |
Nous pouvons également avoir des composantes avec plusieurs instances de plan. Nous allons donc agréger les plans pour avoir une seule ligne par composante (équivalent du « GROUP BY » en SQL).
SELECT ?compo (GROUP_CONCAT(DISTINCT ?plan_image; SEPARATOR=", ") AS ?plan_images) WHERE { wd:Q2962473 wdt:P527 ?compo. OPTIONAL { ?compo wdt:P3311 ?plan_image. } } GROUP BY ?compo
Parmi les propriétés des composantes, nous trouvons également une série de références vers d’autres bases de données, dont OpenStreetMap et les monuments historiques (base « Mérimée »). Nos allons donc utiliser ces références pour faire une jointure avec ces autres bases pour collecter plus d’informations.
Identifiants externes vers d’autres sources.
Les résultats de ces requêtes peuvent être incorrects ou incomplets, mais comme pour Wikipédia ou OpenStreetMap, c’est une œuvre collaborative libre et ouverte. Il est donc possible de corriger et d’ajouter des propriétés aux objets de Wikidata.
En pratique pour exécuter et récupérer les résultats de requêtes SPARQL, nous écrivons la requête dans un fichier texte. Nous l’envoyons ensuite à l’API de Wikidata, puis récupérons le résultat en CSV simplement avec une commande « curl ».
curl \ --data-urlencode "query@wikidata.sparql" \ --header "Accept: text/csv" \ https://query.wikidata.org/sparql > wikidata.csv
Les données de Wikidata sont sous licence CC0.
Enrichir les données avec la base Mérimée des monuments historiquesLa base « Mérimée » est le recensement et la description des monuments historiques classés en France. Nous pouvons la consulter en ligne. Cette base de données est librement accessible en Opendata. IL est possible de la télécharger au format CSV. Chaque monument possède un identifiant, le même que celui que nous avons pu obtenir depuis Wikidata. Le besoin se résume donc à faire une jointure sur un identifiant entre deux CSV. On va alors simplement utiliser les outils en ligne de commande de la suite csvkit. « csvcut » pour ne garder que les colonnes qui nous intéressent, puis « csvjoin » pour faire la jointure avec les données déjà extraites de Wikidata.
On télécharge la base Mérimée (36 Mo).
wget "https://data.culture.gouv.fr/explore/dataset/liste-des-immeubles-proteges-au-titre-des-monuments-historiques/download/" -O 20_merinee.csv
Nous filtrons pour ne conserver que certaines colonnes.
csvcut -d ';' \ -c Référence,Statut,Historique \ 20_merinee.csv > 20_merinee-columns.csv
Nous réalisons la jointure de la colonne refs_merimee
de Wikidata avec la colonne Référence
de Mérimée. --left
est pour « left join », on garde la ligne issue de Wikidata même s’il n’y a pas de correspondance dans Mérimée.
csvjoin -d ',' \ --left --columns refs_merimee,Référence \ 15_wikidata.csv 20_merinee-columns.csv > 20_wikidata+merimee.csv
La base de données Mérimée est sous licence Ouverte (LO).
Enrichir les données avec OpenStreetMapDepuis Wikidata nous avons pu également extraire des coordonnées géographiques. Or, pour les composantes de type chemin nous avons également un identifiant OpenStreetMap. Les coordonnées Wikidata représentent un point. Cela est acceptable pour un bâtiment, mais ne l'est pas pour un tronçon de chemin. Nous pouvons récupérer la géométrie du linéaire des chemins sur OpenStreetMap.
Nous ne l’utilisons pas ici, mais il est à noter que l’approche inverse aurait également été possible. OpenStreetMap contient des références vers les objets Wikidata.
L’API d’OpenStreetMap permet de récupérer des objets « composites » nommés relations depuis leur identifiant. Cependant, les résultats sont dans le format XML propre à OpenStreetMap. Il faut les convertir dans un format SIG plus classique. Nous avons choisi le WKT pour pouvoir l’embarquer dans un CSV. Nous utilisons pour cela l’utilitaire ogr2ogr dédié à la conversion de formats de données géographiques.
Exemple du tronçon 10266352 « Via Podiensis (12b) : Lectoure > Condom ».
Téléchargement d’un chemin depuis OpenStreetMap au format XML.
curl https://osm.org/api/0.6/relation/10266352/full > /tmp/path.osm
Fusion des segments en seul linéaire et conversion en WTK à embarquer dans le CSV.
ogr2ogr \ -lco GEOMETRY=AS_WKT \ -dialect sqlite -sql " \ SELECT LineMerge(Collect(geometry)) AS wkt, 10266352 AS osm_rel_id \ FROM lines" \ /tmp/path.csv /tmp/path.osm
Les données d’OpenStreetMap et donc les géométries collectées sont sous licence ODbL.
Données de contexteEn plus des composantes elles-mêmes, nous souhaitons avoir des informations de contexte : c’est-à-dire des points d’intérêt (POI) à proximité pour les visiteurs et randonneurs du chemin de Compostelle. Ces derniers peuvent être des commerces, comme des restaurants, des supermarchés, des pharmacies mais aussi d’autres points d’intérêt touristiques.
Nous allons pouvoir trouver les établissements commerciaux comme les supermarchés ou les pharmacies dans la base SIREN. Les autres POI vont pouvoir être extraits de la base DATAtourisme.
POI de SIRENLa base SIREN du répertoire des entreprises produite par l’INSEE est disponible en OpenData sous Licence Ouverte (LO). Nous allons utiliser la version géocodée « geosiren » où chaque établissement commercial est en plus associé à une localisation géographique. Le téléchargement de la base de données de la France entière (1.3 Go compressé) est fait par un simple wget.
wget http://data.cquest.org/geo_sirene/last/etablissements_actifs.csv.gz
À chaque établissement sont associés des attributs comme un nom, un nombre de salariés, un statut… ainsi qu’un type d’activité principale. C’est le code APE. Par exemple, le code « 4773Z » correspond à « Commerce de détail de produits pharmaceutiques en magasin spécialisé », c’est-à-dire aux pharmacies. Nous faisons donc la liste des codes APE des activités qui nous intéressent pour filtrer le CSV. On pourrait utiliser « csvgrep », mais vu la taille du fichier un premier filtre avec « grep » fera aussi bien l’affaire.
zgrep -E "4711|4773Z" etablissements_actifs.csv.gz > 40_etablissements_filtre.csv
Contrairement aux jointures précédentes, une simple jointure par identifiant ne suffit pas. Nous devons donc retrouver les établissements à proximité.
Nous pouvons faire cette jointure spatiale en utilisant « ogr2ogr » qui permet d’écrire une requête de type SQL. Ici, les sources de données sont des CSV que nous assemblons dans un unique fichier SQLite. Au passage, nous changeons les coordonnées de latitude/longitude (exprimées en degrés) pour les passer en projection de référence en France X/Y dont l’unité est le mètre, permettant ainsi de faire un calcul de distance cohérent en mètres.
ogr2ogr \ -s_srs epsg:4326 \ -oo X_POSSIBLE_NAMES=longitude -oo Y_POSSIBLE_NAMES=latitude \ -t_srs epsg:2154 -f sqlite -dsco SPATIALITE=YES -nln siren \ 40_etablissements_filtre.sqlite 40_etablissements_filtre.csv ogr2ogr -append \ -s_srs epsg:4326 -oo GEOM_POSSIBLE_NAMES=coor_ \ -t_srs epsg:2154 -nln data \ 40_etablissements_filtre.sqlite 20_wikidata+merimee.csv
Il ne reste plus qu’à extraire les établissements à moins de 500m des composantes classées (commande simplifié).
ogr2ogr \ -sql " \ SELECT * \ FROM data \ JOIN siren ON \ PtDistWithin(data.geometry, siren.geometry, 500)" \ 40_data.csv 40_etablissements_filtre.sqlitePOI de DATAtourisme
DATAtourisme est l’unification des données touristiques provenant des agences régionales et départementales du tourisme. Ces données hétérogènes sont rassemblées dans une base de données sémantique au même formalisme que Wikidata. Nous allons donc à nouveau pouvoir l’interroger en SPARSQL. Ces données sont également sous Licence Ouverte (LO).
Le contenu de cette base est consultable et interrogeable via une interface web. L’exécution des requêtes doit être planifiée et le résultat est obtenu plus tard par API (processus qui a été fait pour un cas d’utilisation qui correspond mal au nôtre).
Chaque propriété est affichée ou fait référence à un autre objet vers lequel nous pouvons naviguer.
Dans notre cas, nous ne voulons garder que certains types d’offres touristiques. De plus, nous voudrions qu’elles soient obligatoirement localisées par des coordonnées géographiques.
Prefix datatourisme: Prefix rdf: Prefix rdfs: Prefix schema: SELECT ?res ?type ?label ?latitude ?longitude ?hasCarPark ?reducedMobilityAccess WHERE { ?res rdf:type ?type; rdfs:label ?label; datatourisme:isLocatedAt ?location. FILTER (?type IN ( datatourisme:ArcheologicalSite, datatourisme:BoutiqueOrLocalShop, datatourisme:FastFoodRestaurant, datatourisme:HotelTrade, datatourisme:LocalTouristOffice )). ?location schema:geo ?geo. ?geo schema:latitude ?latitude; schema:longitude ?longitude. OPTIONAL { ?res datatourisme:reducedMobilityAccess ?reducedMobilityAccess. } BIND (exists{ ?res datatourisme:isEquippedWith datatourisme:CarPark } AS ?hasCarPark). }
res | type | label | latitude | longitude | has Car Park | reduced Mobility Access |
70574f27-5213-30f6- a616-08103615ba70 | FastFoodRestaurant | Beloute Café | 42.89402 | -0.556057 | false | false |
9aaca0e2-f81a-3b67- a766-f2acab10c037 | HotelTrade | Le Chateau d'Arance | 42.974641 | -0.600645 | false | true |
a1795102-c94b-3dd9- a83f-ac4e4d1920a4 | FastFoodRestaurant | Snack Bar Le Fario | 42.8795069 | -0.3962667 | false | true |
d70eecd7-4d40-3f83- 8372-30c638e9d03d | HotelTrade | Hôtel des Voyageurs | 42.8726932 | -0.554552 | false | true |
efb75ac7-6102-3fcd- 8237-347e190f51d6 | FastFoodRestaurant | Restaurant Le Panoramic | 42.8975857 | -0.3960066 | false | false |
95d05007-9c9a-31d8- b240-9f1bc016a68a | HotelTrade | Hôtel du Pourtalet | 42.80684 | -0.418914 | false | true |
Nous appliquons ensuite la même stratégie de conversion en SQLite pour faire une jointure spatiale reposant sur la distance avec les différentes composantes, comme nous l’avons déjà fait pour la base SIREN.
ConclusionNous montrons ici comment récupérer et croiser des données issues de formats divers. Nous faisons à la fois des requêtes SPARQL qui extraient et filtrent des données sur des serveurs distants, mais qui permettent également des croisements de données en local. L’usage de SPARQL permet de ne récupérer que les donnés souhaitées et évite l’utilisation de traitement et logiciel complexes en local. En conséquence les outils en ligne de commande utilisés ici restent accessibles.
Une fois les données obtenues, il ne reste plus qu’à les afficher.
Projets clientsFormations associées
Formations SIG / Cartographie
Formation Python pour l'analyse géospatiale
À distance (FOAD) Du 9 au 13 décembre
Voir la formationFormations SIG / Cartographie
Formation QGIS
À distance (FOAD) Du 2 au 6 décembre 2024
Voir la formationFormations SIG / Cartographie
Formation Développer avec l'écosystème d'OpenStreetMap
Aucune session de formation n'est prévue pour le moment.
Pour plus d'informations, n'hésitez pas à nous contacter.
Voir la formationActualités en lien
Mini-guide à l’usage des collectivités : l’Open Data, entre nécessité et opportunité
Tout ce que vous avez toujours voulu savoir sur l’Open Data. Petit guide à destination des collectivités pour l’appréhender et se l’approprier.
Rando Écrins : l'application mobile au coeur du parc
Découvrez l'application mobile du parc national des Écrins, pour parcourir leurs nombreux sentiers de randonnées en toute simplicité.
Découvrez les sentiers de randonnées du Parc national des Écrins
Faites de la randonnée dans le Parc national des Écrins, avec le nouveau portail en ligne, Geotrek, développé par Makina Corpus.