Cet article sous sa forme complète (codes et liens) sera un des résultats du travail résultant du financement collaboratif que nous avons mis en place: https://www.sigterritoires.fr/index.php/financement-collaboratif-pour-lintegration-de-donnees-marines-dans-qgis/. Dès que le montant visé sera atteint, l’article sera mis à jours dans sa version complète.
Dans le cas où nous n’atteindrions pas le montant visé, nous enverrons la version complète à tous les contributeurs.
…………………………………………………………………………………………………..
Le format S57 est complexe et ne respecte pas totalement les standards des formats SIG. Sa conversion avec GDAL est donc soit complexe, soit incomplète. Dans cet article nous allons rentrer dans le détail de la transformation complète du S57 en tables de fichier Geopackage.
Les types de géométrie
Les « couches » S57 sont des classes d’objets. Par exemple, les différentes zones terrestres sont codées dans la classe d’objets LNDARE.
La définition de cette classe d’objets est:
Geo object: Land area (LNDARE) (P,L,A)
Attributes: CONDTN OBJNAM NOBJNM STATUS INFORM NINFOM
Si tous les objets LNDARE possèdent les mêmes attributs, il n’en est pas de même du type de géométrie. L’information (P,L,A) indique que, dans cette classe d’objets on peut trouver des points, des lignes et des polygones. Contrairement aux standards SIG, les trois types de géométrie coexistent au sein de la même classe d’objets.
Quel qu’il soit le format choisi pour l’intégrer dans QGis, nous aurons à créer une couche par type de géométrie. Si on ne le fait pas, GDAL va créer le type de couche en fonction de la première entité trouvée lors de la conversion. Si elle est de type point, la couche créée sera de type point et les entités lignes et polygones seront ignorées. De même, si la première est de type ligne, ce seront les points et les polygones qui seront ignorés.
Il faut aussi noter que le format S57 n’a aucune contrainte sur les classes d’objets: si dans la zone couverte par le fichier S57 il n’y a pas une classe d’objet, il n’y aura rien la concernant dans le fichier (pas de couche vide). De même, s’il n’y a qu’un type de géométrie présente, alors que les trois types sont possibles, il n’y aura pas de trace des autres types de géométrie.
Il faudra alors décomposer le traitement d’un fichier S57 avec ogr2ogr en trois étapes, une pour chaque type de géométrie. Les options suivantes permettent de traiter chaque classe d’objet S57 en ne sélectionnant qu’un type de géométrie :
-where « OGR_GEOMETRY=’POINT’ or OGR_GEOMETRY=’MULTIPOINT’ »
-where « OGR_GEOMETRY=’LINESTRING’ or OGR_GEOMETRY=’MULTILINESTRING’ »
-where « OGR_GEOMETRY=’POLYGON’ or OGR_GEOMETRY=’MULTIPOLYGON’ »
GDAL permet pour certains types de drivers de créer des préfixes dans les tables en sortie. Dans ce cas on pourrait créer toutes les tables (points, lignes, polygones) dans un seul geopackage, en les préfixant par exemple avec pt_, li_ et pl_. Le problème est que le driver S57 de GDAL ne permet pas cette option. On doit alors créer trois geopackages distincts, dans lesquels les tables seront créées selon le type de géométrie. Dans chaque geopackage il y aura alors une table de même nom mais de géométrie différente. Si cette solution est la plus simple, elle n’est pas la plus élégante et pratique. Nous allons utiliser ici trois geopackages d’import pour les commandes ogr2ogr dans lesquels nous allons importer les tables d’un fichier S57. Nous les appellerons pointsENC, LinesENC et PolysENC. D’autre part nous allons créer un geopackage unique appelé ENC pour la base de données complète. Pour chaque carte ENC nous allons importer son contenu dans les trois geopackages d’import avec ogr2ogr puis nous allons exécuter des scripts Python pour mettre à jour la base de données du geopackage ENC avec les nouvelles tables importées. Nous sommes obligés d’utiliser les scripts Python au lieu de requêtes et fonctions SQL car la version SQL de SQLite, utilisée pour les geopackages, est très limitée.
Pour le premier fichier S57 à importer le processus est le suivant:
- Création du geopackage ENC, vide.
- Chargement des classes d’objets Point dans le geopackage pointsENC avec ogr2ogr
- Chargement des classes d’objets Lignes dans le geopackage LinesENC avec ogr2ogr
- Chargement des classes d’objets Polygone dans le geopackage PolysENC avec ogr2ogr
- Suppression des tables vides des trois geopackages.
- Clonage des tables des trois geopackages d’import dans le geopackage ENC, en préfixant les tables de pointsENC par pt_, de LinesENC par li_ et celles de PolysENC par pl_.
Pour les fichiers S57 suivants, le processus est le suivant:
- Chargement des classes d’objets Point dans le geopackage pointsENC avec ogr2ogr
- Chargement des classes d’objets Lignes dans le geopackage LinesENC avec ogr2ogr
- Chargement des classes d’objets Polygone dans le geopackage PolysENC avec ogr2ogr
- Suppression des tables vides des trois geopackages.
- Mise à jour des tables existantes dans le geopackage ENC à partir des trois geopackages d’import ET
- Clonage des tables des trois geopackages d’import qui n’étaient pas présentes dans le geopackage ENC,
- Détection et effacement des doublons éventuels résultant de la mise à jour des tables du geopackage ENC
Le traitement des sondes bathymétriques
Les sondes bathymétriques posent plusieurs problèmes lors de la conversion de format. Le premier est que la valeur de profondeur n’est pas une contenue dans un attribut, mais en tant que Z dans la géométrie (XYZ). Le deuxième c’est que les sondes ne sont pas du type Point mais Multipoint. Pour avoir directement la valeur des sondes dans un champ attributaire, il faut donc ajouter deux paramètres à la ligne de commande ogr2ogr:
Récupération des identifiants « parents »
Certains objets peuvent être regroupés par un objet non géographique parent. Par exemple, les secteurs de feu sont codés avec un secteur par enregistrement de la table « LIGHTS ». Les différents secteurs d’un même feu n’ont aucun attribut particulier qui puisse indiquer qu’ils correspondant à la même entité. Cette information se trouve dans un enregistrement « parent ». Pour récupérer cet identifiant en tant qu’attribut de la table LIGTHS, il faut ajouter une option à la ligne de commande:
Cette option crée un attribut name_rcid qui est commun à tous les secteurs d’un même feu, mais créé aussi une série de champs (name_rcnm,ORNT,USAG,MASK).
Le traitement des type « Liste »
Le format S57 utilise, en plus des types de champ classiques (entier, réel, texte) un type particulier: les listes, de chaînes de caractères ou d’entiers. Si on retrouve ces types de champ dans PostgreSQL, il n’en va pas de même dans les shapefiles et le geopackage.
En absence des options spécifiques, il y aura des messages d’avertissement indiquant que le format n’est pas supporté et le contenu de ces champs sera ignoré. Pour ne pas perdre le contenu, il faut ajouter dans la ligne de commande ogr2ogr l’option :
Cette option prend une liste {..,…} et génère une chaîne de texte avec un premier chiffre indiquant le nombre d’éléments de la liste, deux points, puis les éléments de la liste séparés par des virgules:
{3} -> ‘(1:3)’
{3,1} -> ‘(2:3,1)’
Forcément, ceci complique un peu le traitement de ces champs mais l’information n’est pas perdue. Par contre, le driver GDAL présente un problème pour la gestion du format String résultant.
Tout d’abord, pour ce qui est des attributs des objets S57 il n’y a aucun problème. Le champ de la table geopackage est de type ‘Text’, tout court, ce qui permet la création d’une chaîne de caractères de n’importe quelle longueur.
Par contre, pour les « identifiants parents », la définition utilisée par le driver est ‘Text()’, c’est à dire une chaîne de caractères avec une longueur maximum. Cette longueur est, a priori, déterminée par le contenu du premier enregistrement traité. C’est dire que ce n’est que du hasard si vous avez un champ avec la longueur nécessaire pour intégrer toutes les valeurs du fichier.
Vous verrez alors des messages du type:
WARNING : Value of field ‘NAME_RCID’ has 371 characters, whereas maximum allowed is 30
La solution n’est pas compliquée mais implique pas mal de temps de travail. Partez de la base que pour l’affichage classique des cartes ENC, ceci ne représente pas un problème et vous pouvez tout simplement ignorer ces messages d’avertissement.
Par contre si vous avez besoin de traitements plus poussés incluant ces informations, voici le seul moyen que j’ai trouvé, pour le moment:
Commandes ogr2ogr pour créer les geopackages d’import
Fort de tous ces éléments, voici donc les trois lignes de commandes ogr2ogr pour créer les tables dans les geopackages d’import:
Commandes ogr2ogr
Pour exécuter ces lignes de commande il suffit d’ouvrir la fenêtre de shell d’OSGeo4W:
La fenêtre de Shell s’ouvre
Rentrez alors les lignes de commande, en faisant attention à bien rentrer le chemin et nom de fichier avec l’extension .000 .
On aura alors comme résultat une série de tables créées dans chaque geopackage d’import. Par contre il y aura des tables complètement vides. En effet, si le type de géométrie est possible pour une classe d’objet, la table sera créée mais s’il n’y a aucune occurrence dans le fichier S57 traité, la table n’aura aucun enregistrement.
Charger plusieurs fichiers S57 en même temps
Les lignes de commandes du paragraphe précédent assument que vous avez un seul fichier S57 à charger. Mais vous pouvez charger plusieurs fichiers simultanément. L’utilisation des options -append -update font que vous pouvez traiter en boucle tous les fichiers .000 d’un répertoire. Vous aurez ainsi tous les fichiers chargés dans les geopackages d’import.
Les fichiers .bat pour réaliser cette tâche sont les suivants:
Fichier .bat pour charger les géométries point
Fichier .bat pour charger les géométries ligne
Fichier .bat pour charger les géométries polygone
Faites attention à modifier les chemins pour qu’ils correspondent à vos geopackages d’import et au répertoire à charger. On pourrait faire un seul fichier.bat pour les trois traitements, mais il convient de bien vérifier le résultat de chacun des types de géométrie.
Création du geopackage ENC
Nous allons créer un geopackage vide qui recevra les tables importées, puis les mises à jour successives lors des chargements d’autres fichiers S57.
Dans le panneau Explorateur de QGis, faites un clic droit sur Geopackages et sélectionnez Créer une base de données
Dans la fenêtre qui s’ouvre, renseignez le nom du geopackage, laissez l’option par défaut pour le nom de la table et sélectionnez Pas de géométrie dans Type de géométrie.
Cliquez sur OK pour avoir le nouveau geopackage vide ajouté aux connexions geopackage de QGis. Il est conseillé d’effacer la table vide ENC qui est créée lors de la création du geopackage. Pour cela ouvrez le gestionnaire de base de données de QGis à partir du menu principal, positionnez-vous sur la table ENC du geopackage ENC.gpkg et avec un clic droit sélectionnez Effacer…
Utilisation des scripts Python pour la gestion de la base de données
Pour toutes les opérations de gestion de la base de données nous allons utiliser la console Python de QGis.
Pour l’ouvrir, allez dans le menu Extensions -> Console Python. Les panneaux de la console Python s’ouvrent:
Pour exécuter un script Python, chargez le code dans le panneau Éditeur. Vous pouvez faire un copier-coller ou bien utiliser l’icône Répertoire pour pointer sur le fichier .py. L’indentation étant cruciale pour les scripts Python, privilégiez cette deuxième option.
Suppression des tables vides
Le code Python suivant permet de supprimer les tables vides d’un schéma d’import:
Attention à respecter l’indentation du code Python. Pour simplifier la tâche, téléchargez directement le fichier .py avec ce lien: delete_empty_tables_in_geopackages.py.
N’oubliez pas de modifier les chemins et noms de fichiers pour qu’ils correspondent aux vôtres.
Clonage et/ou ajout des tables importées dans le geopackage ENC
Le clonage est réalisé avec le code suivant:
Vous pouvez télécharger le fichier .py avec ce lien: clone_or_append_tables_with_prefix.py.
Dans le cas du premier chargement S57, le script créé toutes les tables présentes dans les trois geopackages d’import. Pour les chargements ultérieurs, si la table existe déjà dans le geopackage ENC, les enregistrements de la table du geopackage d’import sont ajoutés aux enregistrements déjà présents dans ENC. Si, par contre, la table n’existe pas encore, elle est créée et alimentée par les enregistrements du geopackage d’import. Dans le fichier geopackage ENC les tables sont préfixées par pt_, li_ et pl_ pour indiquer leur type de géométrie.
Gestion des doublons
Quand deux cartes ENC se superposent, les entités présentes dans la zone de superposition vont être insérées autant de fois que de superpositions il y a. Il faut donc, pour éviter des doublons et plus, nettoyer la base de données après chaque import de fichier S57.
Le code suivant crée, pour chaque table du geopackage ENC, une table temporaire (temp_table
). Puis il remplit cette table avec les enregistrements distincts basés sur name_rcid
. Ensuite, il supprime la table d’origine et renomme la table temporaire avec le nom de la table d’origine.
Ce traitement s’applique à toutes les tables S57, sauf les tables des feux (LIGHTS) et des sondes bathymétriques (SOUNDG). En effet, chaque secteur de feu a le même name_rcid
mais correspond à une couleur est un secteur géographique différents. De même, le name_rcid
des sondes bathymétriques est le même pour tous les points de sondes de l’entité multipoint d’origine. Dans le premier cas nous devons chercher les couleurs et secteurs en double pour un même name_rcid
, et pour les sondes nous devons trouver les sondes ayant les mêmes XY dans leur géométrie.
Vous pouvez télécharger le fichier .py à partir de ce lien : supprime_doublons.py. N’oubliez pas de modifier les chemins et noms de fichiers pour qu’ils correspondent aux vôtres. Remarquez aussi la ligne :
if table_name not in [« pt_LIGHTS », « pt_SOUNDG », »layer_styles »]:
En principe, vous aurez que les tables préfixées et la table layer_styles. Cette ligne permet de traiter différemment les tables LIGTHS et SOUNDG, mais surtout, elle empêche de vider la table layer_styles. Si vous êtes amené à créer d’autres tables que celles issues du fichier s57 dans le geopackage, il faut prendre en compte les noms de ces tables dans ce script, en les ajoutant aux exceptions.
Bonjour,
Nous sommes intéressés par cet outil de conversion, et nous souhaiterions participer au financement du développement.
Afin de s’assurer que l’outil et ses modalités correspondent à nos attentes, j’aurais quelques questions à vous poser au préalable. Pourriez-vous s’il vous plait me contacter par email pour cela ? Je vous remercie par avance.