module utils, add article wololo

This commit is contained in:
Tykayn 2025-02-28 16:37:59 +01:00 committed by tykayn
parent 3b9061514f
commit 3bf5856bab
12 changed files with 307 additions and 85 deletions

View file

@ -2,7 +2,7 @@ import os
import json
from datetime import datetime
import argparse
from utils import get_blog_template_conf
from utils.utils import get_blog_template_conf
from website_config import configs_sites
# Configuration des arguments de la ligne de commande

View file

@ -5,7 +5,7 @@ import os
from jinja2 import Environment, FileSystemLoader
import json
from utils import *
from utils.utils import *
from website_config import *
parser = argparse.ArgumentParser(description="Générer un site Web à partir de fichiers HTML.")

View file

@ -1,16 +1,17 @@
#!/bin/python3
# trouver les articles précédents et suivants
from utils import *
from website_config import configs_sites
from utils.utils import *
from website_config import configs_sites, global_config
import datetime as dt
import os
import json
import re
import argparse
import pypandoc
from jinja2 import Environment, FileSystemLoader
import time # Importer le module time
import time
# Démarrer le chronomètre
@ -84,7 +85,7 @@ else:
count_articles = count_files_in_directories(directories_to_scan)
# count_articles = count_files_in_directories(directories_to_scan)
counter=0
rebuild_counter = 0
@ -131,13 +132,13 @@ if generate_linkings_json :
# print(f"Traitement de l'article {counter}/{count_articles} {file_name}")
file_path = os.path.join(directory, subdir, file_name)
if force_html_regen and counter % 10 == 0:
print(f"{time.strftime('%H:%M:%S')} : Articles traités : {counter}/{count_articles}")
print(f"{time.strftime('%H:%M:%S')} : Articles traités : {counter}")
# on ouvre chacun des fichiers interprétables
# pour déterminer les informations qu'il contient
# afin de les stocker dans un json pour la génération des pages html et gemini
with open(file_path, "r", encoding="utf-8") as f:
print(f"----- Traitement de l'article {counter}/{count_articles} {file_name}")
print(f"----- Traitement de l'article {counter}: {file_name}")
content = f.read()
# Convertir le contenu Org en HTML
title = find_first_level1_title(content)
@ -344,7 +345,10 @@ if pandoc_runs_counter > 0 or not os.path.exists(json_file) or rebuild_articles_
#print(f"Nombre d'articles trouvés : {len(sorted_basenames)}")
count_articles_updated = 0
current_year = datetime.now().year
current_year = dt.datetime.now().year
for basename, info in files_dict.items():
date_str = info['date']
@ -398,7 +402,7 @@ def generate_blog_index(json_file, template_file, output_file):
Par {template_content['AUTHOR']}
===============================================
Dernière mise à jour: {datetime.now().strftime('%Y-%m-%d, %H:%M:%S')}
Dernière mise à jour: {dt.datetime.now().strftime('%Y-%m-%d, %H:%M:%S')}
===============================================
{template_content['DESCRIPTION']}

View file

@ -5,6 +5,7 @@
import os
from datetime import datetime
import argparse
import sys
# Configuration des arguments de la ligne de commande
parser = argparse.ArgumentParser(description="Générer un nouvel article en mode orgmode.")
@ -75,13 +76,19 @@ if year_prefix_in_slug:
else:
schema_slug = slug
file_abs_path = os.path.abspath(os.path.dirname(__file__))
# Obtenir le chemin absolu du répertoire du script
script_dir = os.path.dirname(os.path.abspath(__file__))
if args.lang == 'fr' or args.lang == 'en':
filename = f"{file_abs_path}/sources/{args.blog_dir}/lang_{args.lang}/{date_string}__{slug}.org"
else:
print('langue invalide, disponible: fr ou en seulement')
exit(1)
# Ajouter le répertoire du script au path Python
sys.path.append(script_dir)
# Maintenant importer make_article
try:
from utils.maker import make_article
except ImportError:
print("Erreur: Impossible de trouver le module maker.py dans le dossier utils/")
print("Vérifiez que le fichier utils/maker.py existe et est accessible")
sys.exit(1)
import uuid
@ -89,53 +96,17 @@ def create_uuid_property():
uuid_value = uuid.uuid4()
return uuid_value
def make_article(config):
"""
Crée le contenu d'un nouvel article avec les propriétés spécifiées.
# Créer le dossier de langue s'il n'existe pas
lang_dir = os.path.join(base_blog_dir, args.blog_dir, f"lang_{args.lang}")
if not os.path.exists(lang_dir):
os.makedirs(lang_dir)
print(f"Dossier de langue '{lang_dir}' créé.")
Args:
config (dict): Dictionnaire contenant les paramètres de l'article:
- uuid (str): Identifiant unique de l'article
- slug (str): Slug de l'URL de l'article
- title (str): Titre de l'article
- date_string_full (str): Date complète au format YYYY-MM-DD HH:MM:SS
- date_string (str): Date au format YYYYMMDDHHMMSS
- schema_slug (str): Slug avec ou sans préfixe année selon la config
- blog_dir (str): Dossier du blog
Returns:
str: Contenu formaté de l'article avec les propriétés et métadonnées
"""
return f""":PROPERTIES:
:ID: {config.get('uuid')}
:END:
#+title: {config.get('title')}
#+post_ID:
#+post_slug: {config.get('slug')}
#+post_url: https://www.ciperbliss.com/{config.get('schema_slug')}
#+post_title: {config.get('title')}
#+post_tags:
#+post_series:
#+post_type: post
#+post_status: publish
#+post_picture:
#+post_date_published: <{config.get('date_string_full')}>
#+post_date_modified: <{config.get('date_string_full')}>
#+post_index_page_roam_id: {config.get('uuid')}
#+BLOG: {config.get('blog_dir')}
* {config.get('title')}
"""
# Définir le nom du fichier
filename = os.path.join(lang_dir, f"{date_string}__{slug}.org")
# Écriture du fichier org
with open(filename, "w") as f:
with open(filename, "w") as f:
uuid = create_uuid_property()
config={
'uuid': uuid,
@ -145,6 +116,7 @@ with open(filename, "w") as f:
'date_string': date_string,
'schema_slug': schema_slug,
'blog_dir': args.blog_dir,
'lang': args.lang, # Ajout de la langue dans la config
}
f.write(make_article(config))

View file

@ -6,8 +6,6 @@
#+title: Quelques concepts autour d'OpenStreetMap
#+post_ID:
#+post_slug: quelques-concepts-autour-d'openstreetmap
#+post_url: https://www.ciperbliss.com/2025/quelques-concepts-autour-d'openstreetmap
#+post_title: Quelques concepts autour d'OpenStreetMap
#+post_tags: openstreetmap, wiki
@ -26,19 +24,20 @@ La connaissance autour d'OSM se construit de façon collective et est documenté
Tout le monde peut créer de nouvelles façons de qualifier les choses que l'on peut constater dans le monde réel, même si ces choses ne sont pas forcément visibles depuis l'extérieur. Un exemple, les réseaux électriques ou la présence de toilettes, de moyens de paiement, ou d'autres services tel qu'Ask Angela dans un commerce, ou encore ses horaires d'ouverture.
** La grammaire des étiquettes.
Pour rester infiniment --incohérent-- extensible, le système de tags d'OSM ne possède pas de contrainte de validation ou de typage fort. Toutes les étiquettes sont des chaînes de caractères valables. C'est le principe "any tags you like"
https://wiki.openstreetmap.org/wiki/FR:Cr%C3%A9er_un_attribut_qui_manque
Pour rester infiniment incohér... extensible, le système de tags d'OSM ne possède pas de contrainte de validation ou de typage fort. Toutes les étiquettes sont des chaînes de caractères valables. C'est le principe "any tags you like"
[[https://wiki.openstreetmap.org/wiki/FR:Cr%C3%A9er_un_attribut_qui_manque][FR:Créer un attribut qui manque - OpenStreetMap Wiki]]
Si on veut préciser l'unité de valeur d'un nombre on peut la mettre ou pas, seule une documentation dans le wiki, et des outils de contrôle qui suivent ces règles de validation, ou des gens qui suivent les modifications d'un certain type d'objet, pourront comprendre qu'il y a une erreur.
Ainsi, si un jour on décide qu'il vaut mieux mettre l'unité d'une mesure dans un tag séparé, on peut le faire avec un petit script de remplacement.
https://wiki.openstreetmap.org/wiki/Anatomie_des_%C3%A9tiquettes_osm
[[https://wiki.openstreetmap.org/wiki/Anatomie_des_%C3%A9tiquettes_osm][Anatomie des étiquettes osm - OpenStreetMap Wiki]]
** Les bonnes pratiques
Réutilisez les étiquettes existantes, ne mappez pas juste pour faire joli si ça n'a pas le sens qui décrit le monde réel correspondant, mettez le vrai nom des choses et non une description.
[[https://wiki.openstreetmap.org/wiki/FR:Bonnes_pratiques][FR:Bonnes pratiques - OpenStreetMap Wiki]]
@ -55,32 +54,36 @@ Hé bien non, certaines clés sont documentées comme ne pouvant pas être une
Ce que l'on fait souvent alors, c'est faire de la qualification en fonction de la majorité de surface.
[[https://wiki.openstreetmap.org/wiki/FR:Un_item,_un_objet_OSM][FR:Un item, un objet OSM - OpenStreetMap Wiki]]
[[https://wiki.openstreetmap.org/wiki/FR:S%C3%A9parateur_de_valeur_point-virgule][FR:Séparateur de valeur point-virgule - OpenStreetMap Wiki]]
** Certaines descriptions sont contre intuitives.
Les choses contre intuitives le sont pour plusieurs raisons, et elles le restent à cause des procédures de modification des tags pour lesquelles la grande majorité des gens sont frileux.
Il est d'ailleurs assez étonnant que modifier une base de données semble aussi complexe alors que beaucoup de modifications très simples pourraient être faites car on modifie des informations numériques et qu'il est très simple de vérifier leurs effets de bord sans tout casser sur la base partagée.
[[https://wiki.openstreetmap.org/wiki/Counterintuitive_keys_and_values][Counterintuitive keys and values - OpenStreetMap Wiki]]
[[https://wiki.openstreetmap.org/wiki/Counterintuitive_keys_and_values][Counterintuitive keys and values - OpenStreetMap Wiki]]
La plupart du temps, les gens opposent que l'on ne devrait pas changer la façon dont sont tagguées les choses pour la rétrocompatibilité avec les gens qui réutilisent les données, les éditeurs de logiciel de carte, et aussi parce qu'ils n'ont pas envie de modifier les indexes de nom de recherche.
La plupart du temps, les gens opposent que l'on ne devrait pas changer la façon dont sont tagguées les choses pour la rétrocompatibilité avec les gens qui réutilisent les données, les éditeurs de logiciel de carte, et aussi parce qu'ils n'ont pas envie de modifier les indexes de nom de recherche.
"On ne change pas un truc qui fonctionne", cette aversion au changement, alors que des outils et des procédures existent pour faire cela, est très connue dans le monde de la cybersécurité comme "le problème de l'adhérence logicielle". On est scotché à certains logiciels ou certaines façons de faire pas parce qu'elles sont meilleures que le reste, mais juste parce qu'on a une peur bleue qu'il faille ensuite faire des choses qu'on ne fait pas actuellement pour que ça continue à fonctionner.
"On ne change pas un truc qui fonctionne", cette aversion au changement, alors que des outils et des procédures existent pour faire cela, est très connue dans le monde de la cybersécurité comme "le problème de l'adhérence logicielle". On est scotché à certains logiciels ou certaines façons de faire pas parce qu'elles sont meilleures que le reste, mais juste parce qu'on a une peur bleue qu'il faille ensuite faire des choses qu'on ne fait pas actuellement pour que ça continue à fonctionner.
Comment tuer OSM ? Surtout, ne changeons rien [Florian Lainez] - peertube.openstreetmap.fr
https://peertube.openstreetmap.fr/w/kd4FkyQPKvC7b5ACfhWW7X
[[https://peertube.openstreetmap.fr/w/kd4FkyQPKvC7b5ACfhWW7X][Comment tuer OSM ? Surtout, ne changeons rien par Florian Lainez - peertube.openstreetmap.fr]]
[[https://wiki.openstreetmap.org/wiki/FR:Automated_Edits_code_of_conduct][FR:Code de conduite des modifications automatisées - OpenStreetMap Wiki]]
** Comment qualifier correctement un ensemble d'objets et ses précisions possibles?
Il serait probablement bon de clarifier ce que lon attend des modèles de tags pour faciliter les consensus.
Personnellement jattends quelques qualités aux tags, sans ordre de priorité:
- une cohérence dans un ensemble et dans ses sous ensembles
- la réutilisation au mieux de ce qui existe déjà, permettre des combinaisons
- suivre une anatomie qui soit facilement compréhensible et documentée dans le wiki
- des tags au plus possible anglophones et bas de casse pour une utilisation mondiale
- une spécificité suffisante
- une clarté, de la désambiguation
- un certain lien entre monde réel et mots utilisés
- la réutilisation au mieux de ce qui existe déjà, permettre des combinaisons
- suivre une anatomie qui soit facilement compréhensible
- ne pas avoir peur de faire évoluer les tags et déprécié ce qui est mal foutu, même si cest utilisé. On ne devrait pas rester bloqué pour des raisons de « on a toujours fait nimporte quoi alors pourquoi faire autrement? »
Utiliser le principe de moindre surprise en ingénierie informatique.
[[https://fr.wikipedia.org/wiki/Principe_de_moindre_surprise][Principe de moindre surprise — Wikipédia]]
** Les identifiants d'objets ne sont pas pérennes
Zut alors, on ne peut pas simplement faire un lien vers un restaurant et espérer qu'il soit lu pour toujours comme un lien pointant vers ce lieu précis?
En fait, à court terme, si, mais pas sur le long terme. Les commerces changent assez souvent dans le monde réel, mais les identifiants d'OSM peuvent aussi changer si quelqu'un fait une modification sur un chemin en le découpant ou en supprimant un objet pour en créer un autre avec des informations similaires ailleurs, l'identifiant est perdu et l'URL vers un noeud sera morte.
@ -93,26 +96,45 @@ Un des grands intérêts d'OSM est de pouvoir être un pivot entre plusieurs aut
Il y a deux choses à distinguer ici:
- Les objets du monde réel sont construits, naissent, changent, et disparaissent
- Les étiquettes évoluent dans le temps.
- Les objets du monde réel sont envisagés, construits, changent, et disparaissent. On utilise alors des clés et des préfixes de clé pour décrire ces étapes.
[[https://wiki.openstreetmap.org/wiki/FR:Key:proposed][FR:Key:proposed - OpenStreetMap Wiki]]
[[https://wiki.openstreetmap.org/wiki/Category:FR:Cycle_de_vie][Category:FR:Cycle de vie - OpenStreetMap Wiki]]
[[https://wiki.openstreetmap.org/wiki/FR:Key:construction][FR:Key:construction - OpenStreetMap Wiki]]
[[https://wiki.openstreetmap.org/wiki/FR:Key:start_date][FR:Key:start_date - OpenStreetMap Wiki]]
- Les étiquettes évoluent dans le temps, certaines deviennent dépréciées et on met alors en place des contrôles de qualité pour vérifier à ce qu'elles ne réaparaissent pas.
** Participez aux ateliers en visio ou en présence
Le meilleur moyen de vraiment adopter OSM et sa richesse est de rencontrer les gens qui y participent et de voir comment ce que l'on connaît peut s'insérer dans ce grand commun numérique.
- Adopte une commune
[[https://forum.openstreetmap.fr/t/visios-adopteunecommune-les-mardis-a-21h/18754][Visios AdopteUneCommune les mardis à 21h - Forum OSM Fr]]
[[https://osmvideo.cloud68.co/user/nic-qt8-uwp-ul3][AdopteUneCommune salle de visioconférence BBB]]
- Les rencontres mensuelles des groupes locaux
[[https://umap.openstreetmap.fr/fr/map/groupes-locaux-openstreetmap_152488#6/46.392/1.714][Carte des groupes locaux en France OpenStreetMap - uMap]]
[[https://wiki.openstreetmap.org/wiki/France/OSM-FR#Groupes_locaux][France/OSM-FR - OpenStreetMap Wiki]]
- L'évènement annuel State Of The Map France
[[https://wiki.openstreetmap.org/wiki/State_of_the_Map_France][State of the Map France - OpenStreetMap Wiki]]
En attendant des rencontres, vous pouvez échanger sur le forum qui est une mine d'or pour voir le fonctionnement de la gouvernance, les outils, les erreurs courantes, trouver des gens près de chez vous, les thématiques qui pourraient vous intéresser, comment réutiliser les données, comment trouver tous défibrilateurs, ou les panneaux biche.
https://forum.openstreetmap.fr
** Partagez des photos avec Panoramax
Combiner des images de terrain avec des enquêtes au plus près du monde réel pour le décrire au mieux. Contribuer à Panoramax avec son smartphone est une excellente piste pour cela.
https://panoramax.openstreetmap.fr
- [[https://www.cipherbliss.com/2024/contribuer-%C3%A0-panoramax-avec-open-camera][Contribuer à Panoramax avec open camera -]]
- https://panoramax.openstreetmap.fr
- [[https://www.cipherbliss.com/2024/script-d-export-de-mapillary-vers-panoramax-disponible][Script d'export de mapillary vers Panoramax disponible -]]
- [[https://www.cipherbliss.com/2024/conf%C3%A9rence-sotm-2024-de-mapillary-%C3%A0-panoramax-en-transports-d%C3%A9carbonn%C3%A9s][Conférence SOTM 2024 - De mapillary à Panoramax en transports décarbonnés -]]
** Quelques liens:
Pour aller plus loin:
- Conserver l'historique [[https://wiki.openstreetmap.org/wiki/FR:Conserver_l%27historique][FR:Conserver l'historique - OpenStreetMap Wiki]]
- Cartographier des informations privées https://wiki.openstreetmap.org/wiki/FR:Limitation_de_la_cartographie_des_informations_priv%C3%A9es
- Palmarès des arguments anti open data et comment vous y avez répondu https://teamopendata.org/t/palmares-des-arguments-anti-open-data-et-comment-vous-y-avez-repondu/1731?page=2
- [[https://wiki.openstreetmap.org/wiki/FR:Indoor_Mapping][Intérieur des bâtiments - Indoor Mapping - OpenStreetMap Wiki]]
- [[https://wiki.openstreetmap.org/wiki/FR:Import/Guidelines][Conseils pour l'intégration d'open data - Import/Guidelines - OpenStreetMap Wiki]]
- [[https://taginfo.openstreetmap.org/][Utilisations et croisement de tags - OpenStreetMap Taginfo]]
- [[https://hdyc.neis-one.org/][HDYC - Comment avez vous contribué à OpenStreetMap?]]
- [[https://resultmaps.neis-one.org/][Statistiques mondiales - ResultMaps.neis-one.org]]
- [[https://wiki.openstreetmap.org/wiki/FR:Conserver_l%27historique][Conserver l'historique - OpenStreetMap Wiki]]
- [[https://osmose.openstreetmap.fr/fr/map/][Osmose, outil de contrôle qualité]]
- [[https://wiki.openstreetmap.org/wiki/FR:Limitation_de_la_cartographie_des_informations_priv%C3%A9es][Limitation de la cartographie des informations privées - OpenStreetMap Wiki]]
- [[https://teamopendata.org/t/palmares-des-arguments-anti-open-data-et-comment-vous-y-avez-repondu/1731][Palmarès des arguments anti open data et comment vous y avez répondu - TeamOpenData]]

View file

@ -0,0 +1,201 @@
#+TITLE: Wololo! convertir de l'open data en geojson vers des tags openstreetmap
#+DATE: 2025-02-28 15:28:22
#+AUTHOR:
#+EMAIL:
#+LANGUAGE: fr
#+OPTIONS: toc:nil num:nil
#+STARTUP: showall
#+PERMALINK: 2025/wololo!-convertir-de-l'open-data-en-geojson-vers-des-tags-openstreetmap
#+ID: fb14f129-8ef1-4b6f-ac74-66e5328df297
* Article
:PROPERTIES:
:ID: fb14f129-8ef1-4b6f-ac74-66e5328df297
:END:
** Wololo! convertir de l'open data en geojson vers des tags openstreetmap
L'open data concernant les choses situées géographiquement est une mine d'or d'informations mal foutues. Les transformer en vue de les comparer à ce qui existe dans OpenStreetMap est un travail qui a été réalisé de plusieurs façons par bien des personnes différentes.
Aussi je me devais de réaliser une autre façon de faire cela en me concentrant sur des données au format Geojson. Ainsi est né le projet Wololo. L'idée est d'avoir quelques scripts qui vont prendre un certain jeu de données et y appliquer une configuration de conversion en ignorant les données qui ne rentrent dans aucune partie du convertisseur pour en sortir un autre fichier geojson que l'on peut ensuite examiner dans JOSM ou dans d'autres outils d'analyse.
https://forge.chapril.org/tykayn/wololo
Vous pouvez par exemple voir ce que cela donne sur le jeu de données Data Gouv des bornes de recharge pour véhicules électriques qui contient une cinquantaine de propriétés différentes:
#+begin_src bash
git clone https://forge.chapril.org/tykayn/wololo
cd wololo
npm install
make irve
#+end_src
Le makefile permet de lancer des scripts prédéfinis sur le point d'entrée principal du projet, du Nodejs écrit en TypeScript à qui on donne un fichier à convertir et une configuration de conversion.
#+begin_src bash
ts-node convert_to_osm_tags.ts
#+end_src
Vous avez le fichier geojson qui a suivi les conversions selon le Mapper IRVE, et celui ci contient des tags OpenStreetMap. Ça se passe par ici:
https://forge.chapril.org/tykayn/wololo/src/branch/main/mappings/converters/configIRVE.ts
** Un petit exemple de la partie tags
Lorsqu'une feature du Geojson d'origine contient des informations sur plusieurs points de charge, on fait une feature de station de recharge (amenity=charging_station), et si il n'y en a qu'un seul, on un fait une feature de point de charge (amenity=charging_point).
Les autres propriétés de la feature sont ajoutées selon certaines conditions de clés et de valeurs, puis éventuellement formatées selon les standards déterminés par le Wiki d'OpenStreetMap comme par exemple pour les puissance en kW ou les numéros de téléphone.
Les travaux d'intégration des données ouvertes sont documentées dans le wiki OSM, ici par exemple pour les bornes de recharge:
https://wiki.openstreetmap.org/wiki/France/data.gouv.fr/Bornes_de_Recharge_pour_V%C3%A9hicules_%C3%89lectriques
Ce qui permet de savoir comment convertir les propriétés de l'open data.
J'ai aussi ajouté des tags wikidata concernant quelques opérateurs de stations de recharge.
Faire une conversion peut être assez difficile car les producteurs de données sont plus de 3000 et ont chacun les bras cassés d'une façon différente, ce qui est très courant dans l'open data en général. Comme dit l'adage, garbage in, garbage out: on ne peut faire de bonnes conversions que si les données d'entrée sont bonnes.
https://fr.wikipedia.org/wiki/GIGO
Pour limiter cela, des filtres sont possibles si on change le fichier de configuration afin de tester des résultats sur des plus petits ensembles de données, et le convertisseur informe de la présence de filtres activés lors de l'exécution du script principal.
*** Conversion de clé
Dans un Mapper, c'est la partie qui s'occupe de définir comment on convertit les propriétés de chaque Feature du Geojson:
#+begin_src js
const MappingIRVE: MappingConfigType = {
tags: {
nbre_pdc: 'capacity',
siren_amenageur: 'owner:ref:FR:SIREN',
date_mise_en_service: 'start_date',
nom_station: 'description',
// ... etc
}
}
#+end_src
On convertit la clé "siren_amenageur" en "owner:ref:SIREN" et on garde la même valeur.
*** Conversion avec des transformations avancées
La configuration accepte aussi les objets JS pour définir quoi faire selon les valeurs. Ici pour la puissance on devrait avoir des kW, mais comme je disais avant, c'est rempli avec les pieds, on applique donc une fonction de conversion qui va se charger de deviner la puissance. La fonction n'est pas définie dans le Mappeur, mais dans le moteur de conversion qui reconnaîtra la clé socket_output_find_correspondances pour lancer une fonction sur les données de chaque Feature pour tenter de trouver une valeur correcte.
#+begin_src js
const MappingIRVE: MappingConfigType = {
tags: {
puissance_nominale: {
key_converted: 'charging_station:output',
socket_output_find_correspondances: true,
}
// ... etc
}
}
#+end_src
La fonction qui permet de trouver la puissance des sockets est détaillée dans ce fichier:
Dans les exemples suivants je masquerai la partie autour des exemples se trouvant dans la partie tags
*** Valeurs conditionnelles
L'accessibilité a été décrite de plusieurs façons, notamment avec la notion de "réservé aux PMR" que je n'ai pas retrouvé dans le wiki d'OSM. J'ai donc mis une conversion de valeur conditionnelle vers "wheelchair=yes" dans le cas de plusieurs valeurs particulières pour la propriété "accessibilite_pmr".
#+begin_src js
accessibilite_pmr: {
key_converted: "wheelchair",
conditional_values: {
"Accessibilité inconnue": {
ignore_this_data: true, // ne pas ajouter de tag wheelchair si la valeur est égale à Accessibilité inconnue.
},
"Accessible mais non réservé PMR": {
value_converted: "yes"
},
"Réservé PMR": {
value_converted: "yes"
},
"Non accessible": {
value_converted: "no"
},
#+end_src
*** Inversion de valeur booléenne
Quand l'open data demande si une station est gratuite à l'usage et que l'on a une clé openstreetmap qui dit oui quand ce n'est pas gratuit, il faut inverser la valeur du oui de l'open data.
#+begin_src js
/**
,* l'info de gratuité a été mal renseignée par les opérateurs, ils mettent TRÈS souvent que c'est gratuit alors que ce n'est pas vrai.
,*/
gratuit: {
key_converted: 'fee',
convert_to_boolean_value: true,
invert_boolean_value: true,
},
#+end_src
*** Suppression de clé originale
station_deux_roues est une propriété qui n'a pas de tag dans OSM, mais on veut ajouter trois autres tags quand on la croise et qu'elle vaut "vrai".
J'ai donc dû faire en sorte de supprimer une clé et ajouter des tags de façon conditionnelle. En gardant la logique précédente on a donc ceci:
#+begin_src js
station_deux_roues: {
remove_original_key: true,
conditional_values: {
// ajout de trois tags si la valeur est yes
"yes": {
tags_to_add: [
{ bicycle: "yes" },
{ scooter: "yes" },
{ motorcar: "no" },
]
}
}
},
#+end_src
Ces quelques règles simples de transformations à appliquer aux features permettent d'avoir une description lisible et des fonctions réutilisables d'un jeu de données à un autre.
Certaines clés du Mappeur permettent de ne pas avoir à décrire quoi faire pour toutes les clés où les opérateurs ont eu la flemme de remplir les données ou d'apprendre à écrire des accents. Exemple ici, où on ignore les Features si la valeur est "Non renseigne":
https://forge.chapril.org/tykayn/wololo/src/branch/main/mappings/converters/configRouen_PAV.ts
#+begin_src js
tags_to_ignore_if_value_is: ['Non renseigne'],
#+end_src
Nous avons aussi la possibilité de toujours mettre un certain tag et une certaine valeur à toutes les Features que l'on ajoute à notre conversion. Pour les points d'apport volontaire décrivant ds conteneurs, on voudra toujours avoir ces deux tags à minimum: amenity=recycling et recycling_type=container
#+begin_src js
default_properties_of_point: {
'amenity': 'recycling',
'recycling_type': 'container',
},
#+end_src
** Comment ajouter un convertisseur de jeu de données:
Une documentation détaille les étapes pour créer votre Mappeur et l'intégrer au projet.
https://forge.chapril.org/tykayn/wololo/src/branch/main/docs/ajout_jeu_de_donn%C3%A9es.md
Pour d'avantage de flemme, un script python permet de vous proposer des choses à mettre dans la partie tag d'un Mappeur sans avoir à aller chercher toutes les propriétés possibles des features à la mano.
#+begin_src bash
python propose_mapping_from_data.py etalab_data/musées/fr.geojson
#+end_src
Les propositions restent en console et ne sont pas écrites dans un fichier de Mappeur, vous n'avez plus qu'a copier et faire le tri dans un futur Mappeur.
Cela vous permettra aussi de détecter des propriétés similaires si vous avez affaire à des champions des bras cassés de l'open data.
Le projet Wololo permet aussi d'appliquer des conversions sur des geojson issus d'Osmose.
https://osmose.openstreetmap.fr
* Extraction de données depuis OpenStreetMap
On peut faire des extractions thématiques sur des données OpenStreetMap un peu à la façon Geodatamine.
https://geodatamine.fr/
Ça se passe dans les extractors, ce sont des scripts bash qui utilisent une requête overpass et une fonction bash pour faire un geojson d'export, que l'on stocke dans osm_output.
https://forge.chapril.org/tykayn/wololo/src/branch/main/mappings/extractors/planning_familiaux.sh
La fonction extract_from_osm :
https://forge.chapril.org/tykayn/wololo/src/branch/main/update_scripts/functions.sh
On peut lancer tous les extracteurs à la suite avec ce script de mise à jour pour ensuite travailler sur des données fraîches.
#+begin_src bash
bash update_scripts/run_all_extractors.sh
#+end_src
Pour ne pas blinder l'espace disque de la forge logicielle, les extractions et les fichiers geojson transformés ne sont pas versionnés, merci au Gitignore.

View file

@ -30,7 +30,7 @@
<body>
<div id="page" class="page_article page__{{template_content['PAGE_SLUG']}} ">
<header id=" masthead" class="site-header">
<div class="header-image" style="background-image: url('{{template_content[" BANNIERE_ENTETE"]}}');
<div class="header-image" style="background-image: url('{{template_content["BANNIERE_ENTETE"]}}');
background-repeat: no-repeat; background-size: cover;">
<a href="/">
<img src="{{template_content['SITE_ICON']}}" class="site-icon img">

View file

@ -30,7 +30,7 @@
<body>
<div id="page" class="page__{{template_content[" PAGE_SLUG"]}}">
<header id="masthead" class="site-header">
<div class="header-image" style="background-image: url('{{template_content[" BANNIERE_ENTETE"]}}');
<div class="header-image" style="background-image: url('{{template_content["BANNIERE_ENTETE"]}}');
background-repeat: no-repeat; background-size: cover;">
<div class="template-header">
<a href="/">

View file

@ -30,7 +30,7 @@
<body>
<div id="page" class="page_article page__{{template_content['PAGE_SLUG']}} ">
<header id=" masthead" class="site-header">
<div class="header-image" style="background-image: url('{{template_content[" BANNIERE_ENTETE"]}}');
<div class="header-image" style="background-image: url('{{template_content["BANNIERE_ENTETE"]}}');
background-repeat: no-repeat; background-size: cover;">
<a href="/">
<img src="{{template_content['SITE_ICON']}}" class="site-icon img">

0
utils/__init__.py Normal file
View file

24
utils/maker.py Normal file
View file

@ -0,0 +1,24 @@
# génère le contenu d'un nouvel article avec les propriétés spécifiées en config.
def make_article(config):
"""
Génère le contenu d'un nouvel article org-mode
"""
template = f"""#+TITLE: {config['title']}
#+DATE: {config['date_string_full']}
#+AUTHOR:
#+EMAIL:
#+LANGUAGE: {config['lang'] if 'lang' in config else 'fr'}
#+OPTIONS: toc:nil num:nil
#+STARTUP: showall
#+PERMALINK: {config['schema_slug']}
#+ID: {config['uuid']}
* Article
:PROPERTIES:
:ID: {config['uuid']}
:END:
** {config['title']}
"""
return template

View file

@ -3,7 +3,6 @@ import os
import re
import shutil
from datetime import datetime
import unicodedata
import pypandoc
import subprocess
import tempfile