Prendre un thé
Tapoter des doights
Monsieur fantôme
Quelle heure est-il ?
BazinGa's Tips & tuto IT
BazinGa's - Tips & tuto IT

osm2pgsql – Exploiter les données d’OpenStreetMap dans PostGIS

OpenStreeMap (OSM) constitue une formidable base de données cartographiques. La multitude de participants au projet permet actuellement d’avoir des données particulièrement à jour (en tout cas en France).

J’ai récemment eu besoin de créer des cartes de randonnées sur le territoire de la commune de Lucinges (74). Je me suis donc basée sur les données d’OSM qui étaient plus à jour que celles fournies par l’IGN. Voici les différentes étapes que j’ai suivit pour exporter puis exploiter les données.

Export des données

Les données d’OSM peuvent être exportées de différentes façons, soit depuis la base de données officielle, soit à partir de sites qui effectuent des copies de cette base.

Voici plusieurs sources :

Voici un exemple à partir du site officiel d’OSM et de l’API Overpass :

  1. Sur le site https://www.openstreetmap.org/export, tracez l’emprise d’export souhaité.
  2. Cliquez sur le bouton « exporter » situé sur le panneau de gauche.
    Notez que si l’emprise est trop grande, un message vous indique que vous ne pouvez exporter. Cliquez alors sur le lien « API Overpass » sur le panneau de gauche.
    Ce lien génère une requête Overpass sur l’emprise dessinée ce qui permet de télécharger des données sur une emprise plus importante.
  3. Télécharger le fichier proposé. Attention, le fichier ne possède aucune extension, il faut l’ajouter manuellement en renommant le fichier : .osm.

Il est également possible de faire un export sur une emprise spécifique via le site protomaps, l’intérêt de celui-ci est de pouvoir utiliser une emprise plus précise que le rectangle englobant de l’outil précédent :

  1. Sur le site https://protomaps.com/extracts/ cliquez sur « draw polygon » afin de tracer l’emprise d’extraction des données.
  2. Cliquez sur « Create extract ».
  3. Téléchargez le fichier .osm.pbf.
    Notez que vous pouvez également récupérer l’emprise de votre polygone en geojson.

Import des données dans PostgreSQL

Prérequis

L’import des données implique d’avoir à disposition une base PostgreSQL (de préférence en version 11 ou supérieur) dont l’encodage est l’UTF8 et avec les extensions suivantes :

  • PostGIS (de préférence en version 2.4 ou supérieur).
  • hstore : type de stockage de données sous la forme clé:valeur (similaire au json pour ceux qui connaissent).

Le traitement des données se fait via un utilitaire en ligne de commande : osm2pgsql.

osm2pgsql requiert au minimum 2 Go de mémoire vive et un système 64 bit.

Principes de fonctionnement

Pour correctement utiliser osm2pgsql, il faut bien comprendre son fonctionnement.

L’import des données se déroule en trois grandes étapes :

  • La lecture des données.
  • L’analyse des relations entre les différents objets (points, lignes, relations).
  • La transformation des données et leur chargement dans la BDD.

Il faudra donc préciser différentes options pour chacune de ces étapes dans la ligne de commande.

voici Voici deux exemples d’utilisation :

# Exemple d'import
osm2pgsql.exe --host 127.0.0.1 --port 5432 --username postgres  --database osm --slim --hstore --input-reader pbf "C:\Users\emplacement\mon_fichier.osm.pbf"

# Exemple de mise à jour
osm2pgsql.exe --host 127.0.0.1 --port 5432 --username postgres --password --database osm --append --slim --hstore --input-reader pbf "C:\Users\emplacement\mon_fichier.osm.pbf"

Lecture des données (input)

Plusieurs modes de fonctionnement sont possibles :

  • La création d’une base de données.
  • La mise à jour d’une base de données (non abordé dans cet article).

La mise à jour des données d’une base ne sera pas abordée, elle repose sur le téléchargement des « fichiers diff », qui contiennent les modifications apportées dans OSM.

Le format des fichiers lus est déduit de l’extension mais il peut être forcé si besoin.

osm2pgsql permet d’importer plusieurs fichiers en une seule fois, par exemple plusieurs fichiers contenant différentes emprises, le tout sans dupliquer les objets qui seraient contenus dans plusieurs fichiers à la fois (objets en limite d’emprise par exemple). Cependant, tous les fichiers doivent partager le même horodatage d’extraction des données. Sinon, il serait possible d’avoir plusieurs version d’un même objet ce qui ferait échouer l’import.

Analyse des relations (middle)

osm2pgsql réalise une analyse des relations entre les différents objets constituants la données : les lignes, les points et les relations entre ces éléments. Le but est de pouvoir créer les objets voulus dans la sortie.

Pour cela, les données vont être stockées dans un cache temporaire qui peut être dont l’emplacement peut être définit. Il sera ainsi :

  • Soit en mémoire vive (par défaut).
  • Soit en BDD PostgreSQL.
  • Soit dans un fichier à un emplacement défini (recommandé uniquement pour des imports de planète entière).

La création du cache entraine une consommation d’espace, selon la quantité de mémoire vive disponible sur la machine, il pourra être préférable de stocker le cache dans la BDD ou dans un fichier.

Afin de pouvoir mettre à jour la base, il est nécessaire de conserver le cache. Cela implique donc là aussi le stockage de celui-ci dans la BDD ou dans un fichier.

Différentes options permettent donc de gérer où stocker le cache et de quelle façon.

Import des données en BDD (output)

L’import des données peut être fait de différentes façons. On dit qu’il existe plusieurs sorties :

  • La sortie flex : il s’agit de la sortie avec le plus de possibilités et permettant de choisir exactement la façon dont les données doivent être importer en base.
  • La sortie pgsql : sortie originale de l’utilitaire, les possibilités offertes sont limitées.
  • La sortie gazetteer : les données sont préparées pour leur utilisation avec l’outil de géocodage Nominatim uniquement.
  • La sortie null : aucune données ne sont écrites, cette sortie est réservée pour les tests. Les données de cache sont conservées.

Les sorties flex et pgsql reposent sur l’utilisation d’un fichier de configuration (appelé fichier de style dans le cas de la sortie pgsql). Ces fichiers permettent de définir exactement la structure des tables qui seront créées/alimentées par l’utilitaire.

Cette page traitera uniquement de la sortie flex.

Utilisation

Ligne de commande

Voici les principales options qui peuvent être utilisées avec l’utilitaire :

Options générales :

  • -h ou --help : aide.
  • -v ou --verbose : sortie verbale.
  • --number-processes=n : Nombre de processus parrallèles (défaut entre 1 et 4). Attention, chaque processus utilise plusieurs connexions avec la BDD, il faudra donc peut être vérifier la limite du nombre de connexion.
  • -I ou --disable-parallel-indexing : Désactiver l’indexation concurrante des tables (les index seront créés les uns après les autres).

Options de connexion :

  • -H mon_hote ou --host=mon_hote : Hôte de la BDD.
  • -P mon_port ou --port=mon_port : Port de la BDD.
  • -d ma_bdd ou --database=ma_bdd : Nom de la BDD.
  • -U mon_user ou --username=mon_user : Nom de l’utilisateur PostgreSQL.
  • -W ou --password : Force la demande du mot de passe (sinon le mot de passe utilisé sera celui stocké dans la variable d’environnement PGPASSWORD).

Mode d’import des données :

  • -c ou --create : créer les données dans la base (supprime les données déjà présentes en base). Il s’agit du mode par défaut.
  • -a ou --append : mettre à jour les données déjà présentes en base (obligatoirement avec l’option -s décrite plus bas).

Options d’entrée (input) :

  • -r mon_format ou --input-reader=mon_format : Format d’entré.
    • auto : Détection automatique (défaut).
    • o5m
    • xml
    • pbf

Gestion du cache (middle) :

  • -s ou --slim : stocker le cache dans la BDD.
  • --flat-nodes=emplacement/nom_du_fichier : stocker le cache dans un fichier.
  • --drop : supprimer le cache après l’import (valable pour les options --slim et --flat-nodes) (les mises à jour ne seront donc plus possibles).

Paramètres spécifiques pour l’option --slim :

  • --middle-schema=mon_schema : schéma dans lequel les tables seront stockées (par défaut public).
  • -p mon_prefixe ou --prefix=mon_prefixe : préfixe des tables créées (par défaut planet_osm).
  • --tablespace-slim-data=mon_tablespace : tablespace utilisé pour stocker les tables de données.
  • --tablespace-slim-index=mon_tablespace : tablespace utilisé pour stocker les index créés.
  • -C n ou --cache=n : quantité en Mo de mémoire vive utilisable (par défaut n = 800).

Options de sortie (output) :

  • -O sortie ou --output=sortie : format de sortie à choisir parmi flex, pgsql (défaut), gazetteer, multi et null.
  • -S emplacement_fichier_style ou --style=emplacement_fichier_style : emplacement du fichier de style (de configuration) pour les sorties flex et pgsql.

Paramètres spécifiques pour la sortie pgsql :

  • Paramètres généraux :
    • --output-pgsql-schema=mon_schema : nom du schéma dans lequel seront stockées les données finales (par défaut public).
    • -G ou --multi-geometry : créer des objets avec des multi-geometries.
    • -K ou --keep-coastlines : conserver les lignes de côte (supprimées par défaut).
    • --reproject-area : ajouter une colonne contenant l’aire de chaque objet calculée en utilisant les coordonnées mercator sphérique.
  • Projection des données :
    • -l ou --latlong : stockage des données en latitude longitude (degrés).
    • -m ou --merc : stockage des données en coordonnées mercator sphérique (défaut).
    • -E ou --proj num : stockage des données dans la projection indiquée.
  • Options hstore (stockage clé/valeur) :
    • -k ou --hstore : ajouter les tags sans colonne à une colonne additionnel de type hstore.
    • --hstore-match-only : ne conserver que les objets qui ont une valeur dans au moins une colonne (autre que la colonne hstore).
    • -j ou --hstore-all : ajouter tous les tags dans une colonne de type hstore.
    • -z "nom_du_tag" ou --hstore-column="nom_du_tag" : ajouter uniquement les tags correspondant à une colonne de type hstore (par exemple --hstore-column="name:" permet de ne stocker que les tags name:xx dans cette colonne spécifique).
    • --hstore-add-index : ajouter un index à la colonne de type hstore.
  • Options de performance :
    • -i mon_tablespace ou --tablespace-index=mon_tablespace : nom du tablespace contenant tous les index (cette option affecte aussi les index créés lors de l’étape d’analyse).
    • --tablespace-main-data=mon_tablespace : nom du tablespace contenant les tables.
    • --tablespace-main-index=mon_tablespace : nom du tablespace contenant les index des tables principales.

Voici un exemple de ligne de commande avec la sortie flex (l’ordre des paramètres n’a pas d’importance sauf le fichier à traiter qui doit être en dernier) :

osm2pgsql --verbose --create --input-reader=pbf --slim --middle-schema=osm_import --cache=8192 --output=flex --style="emplacement/config.lua" --host=127.0.0.1 --port=5432 --database=data_sig --user=postgres -W "emplacement/mon_fichier.osm.pbf"

Fichier de configuration flex

La sortie flex ne possède pas d’options spécifiques à passer en ligne de commande. Tous se fait donc dans un fichier de configuration rédigé en lua.

La documentation officielle contient de préciseuses informations mais voici un fichier de configuration permettant de démarrer rapidement.

Le principe du fichier est de décrire les éléments qui seront créés en base (les tables) et leur structure ainsi que la façon dont les objets issus du fichier à importer doivent être analysés et traités. Pour cela plusieurs éléments doivent être présents dans le fichier de configuration :

La définition des tables qui contiendront les données grâce aux fonctions suivantes :

  • osm2pgsql.define_table(NAME, COLUMNS[, OPTIONS])) : définition d’une table (le type de table sera à déterminer dans les paramètres)
  • osm2pgsql.define_node_table(NAME, COLUMNS[, OPTIONS]) : définition d’une table de point.
  • osm2pgsql.define_way_table(NAME, COLUMNS[, OPTIONS]) : définition d’une table de ligne.
  • osm2pgsql.define_relation_table(NAME, COLUMNS[, OPTIONS]) : définition d’une table de relation.
  • osm2pgsql.define_area_table(NAME, COLUMNS[, OPTIONS]) : définition d’une table de surface.

Il est possible de définir autant de tables que nécessaire. Il n’y a pas de limite, le but est de générer votre propre modèle de données avec sa structure spécifique.

Le traitement à appliquer à chaque type d’objet à importer avec les fonctions suivantes :

  • osm2pgsql.process_node(object) : fonction appelée pour le traitement de chaque point.
  • osm2pgsql.process_way(object) : fonction appelée pour le traitement de chaque ligne.
  • osm2pgsql.process_relation(object) : fonction appelée pour le traitement de chaque relation.

Une (ou plusieurs) fonction peut ne pas être définit, le type d’objet associé ne sera alors pas traité.

Le contenu de la fonction permet d’indiquer ce qui doit être fait avec l’objet en cours de manipulation :

  • Écrire l’objet dans une ou plusieurs tables.
  • Ne rien renvoyer (l’objet n’est pas intéressant).
  • Transformer l’objet avant de l’écrire.

Attention, aucune fonction n’est appelée pour la suppression des objets (cas des mises à jour uniquement). L’utilitaire supprime automatiquement tout objet de la base dérivant d’un objet supprimé dans le fichier d’import.

Différentes fonctions de traitement que vous aurez créées spécifiquement selon vos besoins.

-- Fichier de configuration pour l'import des données OSM avec osm2pgsql

-- Variables
------------
-- Projection utilisée
local var_srid = 3857

-- Schéma de destination des données
local var_schema = 'osm_data'

-- Liste des tags non utiles (principale des tags utilisés pour la numérisations)
-- Le caractère '*' permet de spécifier "n'importe quel(s) charactère(s) (possiblement au pluriel)"
local var_delete_keys = {
	-- "mapper" keys
	'attribution',
	'comment',
	'created_by',
	'fixme',
	'note',
	'note:*',
	'odbl',
	'odbl:note',
	'source',
	'source:*',
	'source_ref',

	-- Corine Land Cover (CLC) (Europe)
	'CLC:*',

	-- Geobase (CA)
	'geobase:*',
	-- CanVec (CA)
	'canvec:*',

	-- osak (DK)
	'osak:*',
	-- kms (DK)
	'kms:*',

	-- ngbe (ES)
	-- See also note:es and source:file above
	'ngbe:*',

	-- Friuli Venezia Giulia (IT)
	'it:fvg:*',

	-- KSJ2 (JA)
	-- See also note:ja and source_ref above
	'KSJ2:*',
	-- Yahoo/ALPS (JA)
	'yh:*',

	-- LINZ (NZ)
	'LINZ2OSM:*',
	'linz2osm:*',
	'LINZ:*',
	'ref:linz:*',

	-- WroclawGIS (PL)
	'WroclawGIS:*',
	-- Naptan (UK)
	'naptan:*',

	-- TIGER (US)
	'tiger:*',
	-- GNIS (US)
	'gnis:*',
	-- National Hydrography Dataset (US)
	'NHD:*',
	'nhd:*',
	-- mvdgis (Montevideo, UY)
	'mvdgis:*',

	-- EUROSHA (Various countries)
	'project:eurosha_2012',

	-- UrbIS (Brussels, BE)
	'ref:UrbIS',

	-- NHN (CA)
	'accuracy:meters',
	'sub_sea:type',
	'waterway:type',
	-- StatsCan (CA)
	'statscan:rbuid',

	-- RUIAN (CZ)
	'ref:ruian:addr',
	'ref:ruian',
	'building:ruian:type',
	-- DIBAVOD (CZ)
	'dibavod:id',
	-- UIR-ADR (CZ)
	'uir_adr:ADRESA_KOD',

	-- GST (DK)
	'gst:feat_id',

	-- Maa-amet (EE)
	'maaamet:ETAK',
	-- FANTOIR (FR)
	'ref:FR:FANTOIR',

	-- 3dshapes (NL)
	'3dshapes:ggmodelk',
	-- AND (NL)
	'AND_nosr_r',

	-- OPPDATERIN (NO)
	'OPPDATERIN',
	-- Various imports (PL)
	'addr:city:simc',
	'addr:street:sym_ul',
	'building:usage:pl',
	'building:use:pl',
	-- TERYT (PL)
	'teryt:simc',

	-- RABA (SK)
	'raba:id',
	-- DCGIS (Washington DC, US)
	'dcgis:gis_id',
	-- Building Identification Number (New York, US)
	'nycdoitt:bin',
	-- Chicago Building Import (US)
	'chicago:building_id',
	-- Louisville, Kentucky/Building Outlines Import (US)
	'lojic:bgnum',
	-- MassGIS (Massachusetts, US)
	'massgis:way_id',
	-- Los Angeles County building ID (US)
	'lacounty:*',
	-- Address import from Bundesamt für Eich- und Vermessungswesen (AT)
	'at_bev:addr_date',

	-- "import" keys
	'import',
	'import_uuid',
	'OBJTYPE',
	'SK53_bulk:load',
	'mml:class'
}



-- Fonctions
------------

-- Fonction de nettoyage des tags
-- La fonction make_clean_tags_func() créé une nouvelle fonction à partir d'une liste de tag, qui permet de supprimer ces tags d'un objet.
-- Cette nouvelle fonction renvoie "vrai" si tous les tags ont été retirés.
local clean_tags = osm2pgsql.make_clean_tags_func(var_delete_keys)


-- Fonction permettant de déterminer si l'objet est une surface
function has_area_tags(tags)
	if tags.area == 'yes' then
		return true
	end
	if tags.area == 'no' then
		return false
	end

	-- Si le tag "area" n'existe pas, on retourne la valeur du premier tag possédant une valeur
	return tags.aeroway
		or tags.amenity
		or tags.building
		or tags.harbour
		or tags.historic
		or tags.landuse
		or tags.leisure
		or tags.man_made
		or tags.military
		or tags.natural
		or tags.office
		or tags.place
		or tags.power
		or tags.public_transport
		or tags.shop
		or tags.sport
		or tags.tourism
		or tags.water
		or tags.waterway
		or tags.wetland
		or tags['abandoned:aeroway']
		or tags['abandoned:amenity']
		or tags['abandoned:building']
		or tags['abandoned:landuse']
		or tags['abandoned:power']
		or tags['area:highway']
		or tags['building:part']
end





-- Définition des tables de destintation
----------------------------------------

-- Variable de type tableau qui contiendra la définition des tables à créer en base
local var_tables = {}

-- Table des ponctuels
var_tables.ponctuel = osm2pgsql.define_table({
	-- Schéma contenant la table
	schema = var_schema,
	-- Nom de la table
	name = 'ponctuel',
	-- Gestion de la colonne d'identifiant osm
	ids = { type = 'node', id_column = 'osm_id' },
	-- Liste des colonnes à créer
	columns = {
		-- Colonne d'identifiant (de type sérial donc gérée automatiquement)
		{ column = 'id', type = 'serial', create_only = true },
		-- Colonne contenant les tags
		{ column = 'tags', type = 'hstore' },
		-- Colonnes supplémentaires (qui reprendront la valeur de certains tages)
		{ column = 'amenity', type = 'text' },
		{ column = 'historic', type = 'text '},
		{ column = 'highway', type = 'text' },
		{ column = 'name', type = 'text' },
		{ column = 'natural', type = 'text' },
		{ column = 'place', type = 'text' },
		{ column = 'power', type = 'text' },
		{ column = 'shop', type = 'text' },
		{ column = 'tourism', type = 'text' },
		-- Colonne géométrique
		{ column = 'geom', type = 'point', projection = var_srid },
	}
})

-- Table des lignes
var_tables.ligne = osm2pgsql.define_table({
	-- Schéma contenant la table
	schema = var_schema,
	-- Nom de la table
	name = 'ligne',
	-- Gestion de la colonne d'identifiant osm
	ids = { type = 'way', id_column = 'osm_id' },
	-- Liste des colonnes à créer
	columns = {
		-- Colonne d'identifiants (de type sérial donc gérée automatiquement)
		{ column = 'id', type = 'serial', create_only = true },
		-- Colonne contenant les tags
		{ column = 'tags', type = 'hstore' },
		-- Colonnes supplémentaires (qui reprendront la valeur de certains tages)
		{ column = 'access', type = 'text' },
		{ column = 'admin_level', type = 'text' },
		{ column = 'barrier', type = 'text' },
		{ column = 'boundary', type = 'text' },
		{ column = 'highway', type = 'text' },
		{ column = 'intermittent', type = 'text' },
		{ column = 'name', type = 'text' },
		{ column = 'power', type = 'text' },
		{ column = 'tracktype', type = 'text' },
		{ column = 'waterway', type = 'text' },
		-- Colonne géométrique
		{ column = 'geom', type = 'linestring', projection = var_srid },
	}
})

-- Table des surfaces
var_tables.surface = osm2pgsql.define_table({
	-- Schéma contenant la table
	schema = var_schema,
	-- Nom de la table
	name = 'surface',
	-- Gestion de la colonne d'identifiant osm
	ids = { type = 'area', id_column = 'osm_id' },
	-- Liste des colonnes à créer
	columns = {
		-- Colonne d'identifiants (de type sérial donc gérée automatiquement)
		{ column = 'id', type = 'serial', create_only = true },
		-- Colonne contenant les tags
		{ column = 'tags', type = 'hstore' },
		-- Colonnes supplémentaires (qui reprendront la valeur de certains tages)
		{ column = 'amenity', type = 'text' },
		{ column = 'building', type = 'text' },
		{ column = 'landuse', type = 'text' },
		{ column = 'leaf_type', type = 'text' },
		{ column = 'leisure', type = 'text' },
		{ column = 'natural', type = 'text' },
		-- Colonne géométrique
		{ column = 'geom', type = 'geometry', projection = var_srid },
	}
})

-- Table des itineraires
var_tables.itineraire = osm2pgsql.define_table({
	-- Schéma contenant la table
	schema = var_schema,
	-- Nom de la table
	name = 'itineraire',
	-- Gestion de la colonne d'identifiant osm
	ids = { type = 'relation', id_column = 'osm_id' },
	-- Liste des colonnes à créer
	columns = {
		-- Colonne d'identifiants (de type sérial donc gérée automatiquement)
		{ column = 'id', type = 'serial', create_only = true },
		-- Colonne contenant les tags
		{ column = 'tags', type = 'hstore' },
		-- Colonne géométrique
		{ column = 'geom', type = 'linestring', projection = var_srid },
	}
})

-- Table des frontières
var_tables.limite = osm2pgsql.define_table({
	-- Schéma contenant la table
	schema = var_schema,
	-- Nom de la table
	name = 'limite',
	-- Gestion de la colonne d'identifiant osm
	ids = { type = 'relation', id_column = 'osm_id' },
	-- Liste des colonnes à créer
	columns = {
		-- Colonne d'identifiants (de type sérial donc gérée automatiquement)
		{ column = 'id', type = 'serial', create_only = true },
		-- Colonne contenant les tags
		{ column = 'tags', type = 'hstore' },
		-- Colonne géométrique
		{ column = 'geom', type = 'linestring', projection = var_srid },
	}
})





-- Fonction de traitement des objets
------------------------------------

-- Traitement des noeuds
function osm2pgsql.process_node(object)
	-- Si le nettoyage des tags ne laisse aucun tag : on ne renvoie rien : on ne conserve pas l'objet
	if clean_tags(object.tags) then
		return
	end

	-- Ajout d'un objet à la table des ponctuels
	-- Définition de la valeur des colonnes qui ont été ajoutées :
	--		La colonne id est de type serial donc pas besoin de définir la valeur
	-- 		Récupération de la valeurs d'un tag :
	--			object.tags.mon_tag = valeur du tag indiqué (le tag reste présent dans l'objet object.tags)
	--			object:grab_tag('mon_tag') = valeur du tag indiqué (le tag est supprimé de l'objet object.tags, pratique pour éviter d'avoir deux fois la donnée)
	--		Le champ géométrique contient la définition du type de géométrie souhaitée pour la table : un point
	var_tables.ponctuel:add_row({
		amenity = object:grab_tag('amenity'),
		historic = object:grab_tag('historic'),
		highway = object:grab_tag('highway'),
		name = object:grab_tag('name'),
		natural = object:grab_tag('natural'),
		place = object:grab_tag('place'),
		power = object:grab_tag('power'),
		shop = object:grab_tag('shop'),
		tourism = object:grab_tag('tourism'),

		tags = object.tags,

		geom = { create = 'point' }
	})
end



-- Traitement des chemins
function osm2pgsql.process_way(object)
	-- Si le nettoyage des tags ne laisse aucun tag : on ne conserve pas l'objet
	if clean_tags(object.tags) then
		return
	end

	-- Si l'objet est fermé et qu'il est considéré comme une surface
	if object.is_closed and has_area_tags(object.tags) then

		-- Ajout d'un objet à la table des surface
		-- Définition de la valeur des colonnes qui ont été ajoutées :
		--		La colonne id est de type serial donc pas besoin de définir la valeur
		-- 		Récupération de la valeurs d'un tag :
		--			object.tags.mon_tag = valeur du tag indiqué (le tag reste présent dans l'objet object.tags)
		--			object:grab_tag('mon_tag') = valeur du tag indiqué (le tag est supprimé de l'objet object.tags, pratique pour éviter d'avoir deux fois la donnée)
		--		Le champ géométrique contient la définition du type de géométrie souhaitée pour la table : une surface (conversion des lignes en surface)
		var_tables.surface:add_row({
			amenity = object:grab_tag('amenity'),
			building = object:grab_tag('building'),
			landuse = object:grab_tag('landuse'),
			leaf_type = object:grab_tag('leaf_type'),
			leisure = object:grab_tag('leisure'),
			natural = object:grab_tag('natural'),

			tags = object.tags,

			geom = { create = 'area' }
		})
	else

		-- Ajout d'un objet à la table des lignes
		-- Définition de la valeur des colonnes qui ont été ajoutées :
		--		La colonne id est de type serial donc pas besoin de définir la valeur
		-- 		Récupération de la valeurs d'un tag :
		--			object.tags.mon_tag = valeur du tag indiqué (le tag reste présent dans l'objet object.tags)
		--			object:grab_tag('mon_tag') = valeur du tag indiqué (le tag est supprimé de l'objet object.tags, pratique pour éviter d'avoir deux fois la donnée)
		--		Le champ géométrique contient la définition du type de géométrie souhaitée pour la table : une ligne (ne contenant pas plus de 10 000 sommets sinon l'objet est découpé)
		var_tables.ligne:add_row({
			access = object:grab_tag('access'),
			admin_level = object:grab_tag('admin_level'),
			barrier = object:grab_tag('barrier'),
			boundary = object:grab_tag('boundary'),
			highway = object:grab_tag('highway'),
			intermittent = object:grab_tag('intermittent'),
			name = object:grab_tag('name'),
			power = object:grab_tag('power'),
			tracktype = object:grab_tag('tracktype'),
			waterway = object:grab_tag('waterway'),

			tags = object.tags,

			geom = { create = 'line', split_at = 10000 }
		})
	end
end



-- Traitement des relations
function osm2pgsql.process_relation(object)
	-- Si le nettoyage des tags ne laisse aucun tag : on ne conserve pas l'objet
	if clean_tags(object.tags) then
		return
	end

	-- Récupération du tag "type"
	local type = object.tags.type

	-- Les relations de type "route" sont envoyées dans la table des itineraires
	if type == 'itineraire' then
		-- On créé une ligne à partir de la relation

		-- Ajout d'un objet à la table des lignes
		-- Définition de la valeur des colonnes qui ont été ajoutées :
		--		La colonne id est de type serial donc pas besoin de définir la valeur
		--		Le champ géométrique contient la définition du type de géométrie souhaitée pour la table : une ligne (ne contenant pas plus de 10 000 sommets sinon l'objet est découpé)
		var_tables.itineraire:add_row({
			tags = object.tags,

			geom = { create = 'line', split_at = 10000 }
		})
		return
	end

	-- Les relations de type "frontière" ou "multipolygon" mais avec le tags "boundary" sont envoyées dans la table des limites
	if type == 'boundary' or (type == 'multipolygon' and object.tags.boundary) then
		-- On créé une ligne à partir de la relation

		-- Ajout d'un objet à la table des lignes
		-- Définition de la valeur des colonnes qui ont été ajoutées :
		--		La colonne id est de type serial donc pas besoin de définir la valeur
		--		Le champ géométrique contient la définition du type de géométrie souhaitée pour la table : une ligne (ne contenant pas plus de 10 000 sommets sinon l'objet est découpé)
		var_tables.limite:add_row({
			tags = object.tags,

			geom = { create = 'line', split_at = 10000 }
		})

		-- On créé également un polygone à partir de la relation (les limites seront donc à la fois dans la table limite ET dans la table des surfaces)

		-- Ajout d'un objet à la table des surfaces
		-- Définition de la valeur des colonnes qui ont été ajoutées :
		--		La colonne id est de type serial donc pas besoin de définir la valeur
		-- 		Récupération de la valeurs d'un tag :
		--			object.tags.mon_tag = valeur du tag indiqué (le tag reste présent dans l'objet object.tags)
		--			object:grab_tag('mon_tag') = valeur du tag indiqué (le tag est supprimé de l'objet object.tags, pratique pour éviter d'avoir deux fois la donnée)
		--		Le champ géométrique contient la définition du type de géométrie souhaitée pour la table : une surface
		var_tables.surface:add_row({
			amenity = object:grab_tag('amenity'),
			building = object:grab_tag('building'),
			landuse = object:grab_tag('landuse'),
			leaf_type = object:grab_tag('leaf_type'),
			leisure = object:grab_tag('leisure'),
			natural = object:grab_tag('natural'),

			tags = object.tags,

			geom = { create = 'area' }
		})
		return
	end

	-- Les relations de type "multipolygon" sont envoyées dans la table des surfaces
	if object.tags.type == 'multipolygon' then
		-- On créé un polygone à partir de la relation

		-- Ajout d'un objet à la table des surfaces
		-- Définition de la valeur des colonnes qui ont été ajoutées :
		--		La colonne id est de type serial donc pas besoin de définir la valeur
		-- 		Récupération de la valeurs d'un tag :
		--			object.tags.mon_tag = valeur du tag indiqué (le tag reste présent dans l'objet object.tags)
		--			object:grab_tag('mon_tag') = valeur du tag indiqué (le tag est supprimé de l'objet object.tags, pratique pour éviter d'avoir deux fois la donnée)
		--		Le champ géométrique contient la définition du type de géométrie souhaitée pour la table : une surface
		var_tables.surface:add_row({
			amenity = object:grab_tag('amenity'),
			building = object:grab_tag('building'),
			landuse = object:grab_tag('landuse'),
			leaf_type = object:grab_tag('leaf_type'),
			leisure = object:grab_tag('leisure'),
			natural = object:grab_tag('natural'),

			tags = object.tags,

			geom = { create = 'area' }
		})
	end
end

Résultat

Après toutes ces étapes, le résultat devrait être le suivant :

Un schéma osm_import contenant le cache d’osm2pgsql et constitué des trois tables suivantes :

  • planet_osm_nodes
  • planet_osm_rels
  • planet_osm_ways

Le paramètre --drop permet de supprimer celui-ci automatiquement à la fin de l’import.

Un schéma osm_data contenant les données importées selon le modèle décrit dans le fichier de configuration et constitué des tables et colonnes suivantes :

  • itineraire
    • osm_id (int8) : identifiant OSM
    • id (int4) : identifiant
    • tags (hstore) : liste des tags
    • geom (geometry) : géométrie
  • ligne
    • osm_id (int8) : identifiant OSM
    • id (int4) : identifiant
    • tags (hstore) : liste des tags
    • access (text)
    • admin_level (text)
    • barrier (text)
    • boundary (text)
    • highway (text)
    • intermittent (text)
    • name (text)
    • power (text)
    • tracktype (text)
    • waterway (text)
    • geom (geometry) : géométrie
  • limite
    • osm_id (int8) : identifiant OSM
    • id (int4) : identifiant
    • tags (hstore) : liste des tags
    • geom (geometry) : géométrie
  • ponctuel
    • osm_id (int8) : identifiant OSM
    • id (int4) : identifiant
    • tags (hstore) : liste des tags
    • amenity (text)
    • historic (text)
    • highway (text)
    • name (text)
    • natural (text)
    • place (text)
    • power (text)
    • shop (text)
    • tourism (text)
    • geom (geometry) : géométrie
  • surface
    • osm_id (int8) : identifiant OSM
    • id (int4) : identifiant
    • tags (hstore) : liste des tags
    • amenity (text)
    • building (text)
    • landuse (text)
    • leaf_type (text)
    • leisure (text)
    • natural (text)
    • geom (geometry) : géométrie

Il est maintenant possible d’utiliser les données d’OSM directement dans un logiciel de SIG comme QGIS par exemple.

Selon les besoins, n’hésitez pas à adapter votre fichier de configuration pour ajouter ou retirer les champs nécessaires à votre projet. Notez qu’il est plus rapide de travailler sur une valeur stockée dans son propre champ plutôt que d’avoir à l’extraire du champ tags (de type hstore) à chaque requête.

Pour aller plus loin

Vous pourrez trouver plus d’informations sur ces sites :

Cet article fait parti du cours sur PostgreSQL, partie 8 du Vade-mecum.


Cet article vous a plu ?

N'hésitez pas à le partager, il interessera surement certains de vos contacts.

Les thèmes suivants contiennent des articles en lien avec celui-ci, allez faire un tour :

BDDPostGISPostgreSQLSIG flexluaOpenStreetMap

50%