In der OpenStreetMap-Datenbank sind die Informationen über Verwaltungseinheiten (Gemeinden, Kreise, Länder) meist auf die Grenzrelation und den Knoten mit der Rolle admin_centre verteilt. Will man zum Rendern der Grenze oder Fläche einer Verwaltungseinheit auf Informationen zurückgreifen, die beim Knoten gespeichert sind, z.B. das Attribut population oder name:xx, dann stellt man fest, dass üblichen Werkzeuge diese Informationen nicht bereit stellen. Nachfolgend werden Methoden beschrieben, wie man dieses Problem bei mit osm2pgsql erstellten Datenbanken und bei aus OSM erzeugten Shape-Dateien lösen kann.
PostGis / osm2pgsql
osm2pgsql überträgt zwar Attribute der äußeren Wege, also der Wege mit Rolle outer, auf das für die Relation erzeugte Polygon, für den Knoten mit der Rolle admin_centre wird nichts vergleichbares gemacht. Es wird auch kein Link vom Polygon zum Knoten erzeugt. Die im Slim-Modus von osm2pgsql erzeugte Tabelle<prefix>_rels
enthält zwar in der Spalte members sämtliche Mitglieder der Relation und deren OSM-ID, aber leider in einer nicht direkt und effizient mit SQL auswertbaren Form. Beispiel:
psql -d osm -c "select members from planet_osm_rels\
where hstore(tags)->'name'='Corneilla-la-Rivière';"
members
----------------------------------------------------------------------------
{w121772197,outer,w100006391,outer,w121772204,outer,n287557758,admin_centre}
Die Member der Relation werden also in einem Array abgelegt, zwei aufeinander folgende Array-Elemente pro Member. Mit einer kleinen Funktion kann man die Node-Id des Admin_Centre extrahieren:CREATE OR REPLACE FUNCTION admin_centre(anyarray) RETURNS bigint LANGUAGE sql AS $function$ SELECT cast(substring($1[i] from 2) as bigint) FROM generate_series(array_lower($1,1),array_upper($1,1),2) i WHERE $1[i+1]='admin_centre';$function$Damit kann man nun die Polygone der Verwaltungsgrenzen zusammen mit den Attributen des Admin_Centre abfragen:
select poly.name, point.name, point."name:ca", point.population from planet_osm_rels r, planet_osm_polygon poly, planet_osm_point point where poly.admin_level='8' and admin_centre(r.members) = point.osm_id and r.id = -poly.osm_id ;Je nach Anwendungsfall und Performanceanforderung kann man damit auch die benötigten Attribute in die jeweils andere Tabelle übertragen oder eine Tabelle mit der Zuordnung von Knoten zu Relationen erstellen:
create table admin_rel as select -id as rel_id, admin_centre(members) as admin_centre from planet_osm_rels where hstore(tags)->'boundary'='administrative' and hstore(tags)->'admin_level' in ('8','6','4');
Shape-Dateien
Hierfür wird die Osmium-Bibliothek, das darauf basierende Javascript-Toolosmjs
und QGis benutzt. Mit osmjs
können auf einfache Weise Shape-Dateien aus den Objekten einer OSM-Datei zu erzeugt werden. Wie bei osm2pgsql
werden dabei zwar Polygone aus Grenzrelationen erstellt, aber ohne Bezug zum admin_centre
. Es ist jedoch möglich, eine CSV-Datei zu erstellen, welche diesen Bezug enthält:/* Osmium Javascript Example: shape_export.js run with: osmjs -2 -m -l sparsetable -j shape_export.js OSMFILE */ var shp_places = Osmium.Output.Shapefile.open('./places', 'point'); shp_places.add_field('id', 'string', 12); shp_places.add_field('type', 'string', 32); shp_places.add_field('name', 'string', 32); var shp_boundaries = Osmium.Output.Shapefile.open('./boundaries', 'polygon'); shp_boundaries.add_field('id', 'integer', 10); shp_boundaries.add_field('type', 'string', 32); shp_boundaries.add_field('boundary', 'string', 255); shp_boundaries.add_field('admin_level', 'string', 255); var csv_file = Osmium.Output.CSV.open("./rels.csv"); var node_tags = { place: { village: 'villaget', city: 'city', town: 'town'} //, //shop: { supermarket: 'supermarket' } } Osmium.Callbacks.init = function() { print("Init"); } Osmium.Callbacks.node = function() { for (var key in this.tags) { if (node_tags[key]) { var type = node_tags[key][this.tags[key]]; if (type) { shp_places.add(this.geom, { id: this.id, type: type, name: this.tags.name }); } } } } Osmium.Callbacks.area = function() { if (this.tags.boundary) { shp_boundaries.add(this.geom, { id: this.id, type: this.tags.boundary, admin_level: this.tags.admin_level}); } } Osmium.Callbacks.relation = function() { for (var key in this.members) { for (var key2 in this.members[key]) { if (key2=='role' && this.members[key].role=='admin_centre'){ csv_file.print(this.id*2+1, this.members[key].ref, this.members[key].role, this.members[key].type); } } } } Osmium.Callbacks.end = function() { shp_places.close(); shp_boundaries.close(); csv_file.close(); print("Done"); }Das Skript erstellt eine Shape-Datei mit den Grenzpolygonen, eine Shape-Datei mit den Place-Knoten und eine CSV-Datei, welche die Ids der Grenzpolygone und der zugehörigen Place-Knoten enthält. Dabei wird berücksichtigt, dass
osmjs
den Polygonen eine Pseudo-ID mit dem Wert [2 * OSM-ID der Relation + 1
zuweist.
Diese drei Dateien lädt man als Layer in QGis. Dann verknüpft man den CSV-Daten-Layer über die Knoten-ID mit dem Knoten-Layer und anschliessend den Polygon-Layer über die Pseudo-ID mit dem Knoten-Layer. Will man die Daten mit Tilemill / Mapnik verenden, kann man die Polygon-Layer als Shape-Datei speichern.