QVINTVS · SCRIBET

OpenStreetMap-Karten mit dem deutschen Stil selbst rendern

Dieser Artikel erläutert die Erstellung von druckfähigen Karten auf der Basis des OpenStreetMap-Kartenmaterials mit dem sog. „deutschen“ OpenStreetMap-Stil.

In zwei vorangegangenen Artikeln habe ich mich damit beschäftigt, wie man auf unterschiedliche Arten aus den OpenStreetMap-Daten auf seinem eigenen Rechner Kartengraphiken beliebiger Größe erstellen kann. In beiden Fällen kam der internationale OpenStreetMap-Stil, openstreetmap-carto, zum Einsatz. Hier soll es nunmehr darum gehen, nach der Methode des zweiten Artikels den deutschen OpenStreetMap-Stil — wie er etwa auf der Karte von openstreetmap.de zu sehen ist — für eigene Karten einzusetzen.

Vorbereitungen

Wie üblich empfiehlt es sich, ein eigenes Verzeichnis für die folgenden Arbeiten anzulegen.

$ mkdir ~/osmtest
$ cd ~/osmtest

Danach sollte man sogleich den Download des Kartenmaterials anstoßen, da dies erfahrungsgemäß sehr lange dauern kann. Auch dieses Mal kommen bei mir wieder die OSM-Auszüge von Geofabrik zum Einsatz.

$ wget http://download.geofabrik.de/europe/germany/nordrhein-westfalen/arnsberg-regbez-latest.osm.pbf

Virtuelenv

Da einige Python-Software zum Einsatz kommt, empfiehlt es sich, zur sauberen Abschottung von Software virtualenv zu benutzen.

$ virtualenv -p /usr/bin/python2.7 env
$ . ./env/bin/activate

Mapnik

Ohne Mapnik kein OpenStreetMap. Mapnik muss daher installiert werden, sollte aber auch in den Repositories aller gängigen Linux-Distributionen enthalten sein. Ich habe für die Zwecke dieses Artikels mit Mapnik 3.0.9 gearbeitet.

PostgreSQL + PostGIS

Für die Arbeit mit dem Kartenmaterial ist PostgreSQL mit der Erweiterung PostGIS erforderlich. Beides ist in den Repositories der üblichen Linux-Distributionen enthalten; speziell im Falle von Gentoo ist es allerdings erforderlich, das PostGIS-Paket von seiner Keyword-Markierung zu befreien, da das als stabil markierte dev-db/postgis nicht mit der als stabil markierten PostgreSQL-Version 9.5 zusammenarbeitet.

Die Standardeinstellungen von PostgreSQL sollten verändert werden, da sie auf Systeme mit eher kleinen Arbeitsspeichern zugeschnitten zu sein scheinen; im Zweifel sollten sie eher zu groß als zu klein bemessen werden. Dabei sollte man aber nicht aus den Augen verlieren, dass Mapnik beim Rendern der Karten ebenfalls erhebliche Mengen Arbeitsspeicher für sich beanspricht.

Die relevanten Einstellungen befinden sich in der Konfigurationsdatei postgresql.conf:

work_mem=16MB
maintenance_work_mem=128MB

Noch Memory Overcomitting zulassen und PostgreSQL neu starten:

# sysctl -w vm/overcommit_memory=1
# /etc/init.d/postgresql-9.5 restart

Abhängigkeiten des deutschen OSM-Stils

An dieser Stelle ging der vorige Artikel bereits zur Erstellung der Datenbank über. Dies ist hier noch nicht ratsam, da zunächst einige spezielle Abhängigkeiten des deutschen OpenStreetMap-Stils berücksichtigt werden müssen. Die offizielle Installationsanleitung fällt eher mau aus, weshalb ich den Installationsprozess hier im Einzelnen festhalte.

Zunächst ergibt sich folgender Abhängigkeitsbaum:

Diese Abhängigkeiten sind nicht alle dokumentiert und nicht immer einfach zu installieren. Insbesondere kakasi und utf8proc sind nicht immer in den Repositorien der gängigen Linux-Distributionen enthalten. Schlimmer noch, Gentoo liefert eine derart veraltete Version von utf8proc aus, dass eine Verwendung mit dem deutschen OSM-Stil unmöglich ist. Es scheint, als ob utf8proc irgendwann einmal wegen Inaktivität geforkt wurde und Gentoo noch das alte, inaktive Projekt im Portage-Tree hat. kakasi im Portage-Tree ist aber aktuell.

Sofern die eigene Linux-Distribution kakasi und utf8proc also nicht oder nicht in der aktuellen Version bereitstellt, sind sie aus den Quellen zu kompilieren und zu installieren. Dabei bitte darauf achten, ein stabiles Release zu installieren. ldconfig nicht vergessen.

$ git clone git://github.com/JuliaLang/utf8proc.git
Cloning into 'utf8proc'...
remote: Counting objects: 1050, done.
remote: Compressing objects: 100% (19/19), done.
remote: Total 1050 (delta 5), reused 0 (delta 0), pack-reused 1031
Receiving objects: 100% (1050/1050), 3.57 MiB | 1.03 MiB/s, done.
Resolving deltas: 100% (635/635), done.
Checking connectivity... done.
$ cd utf8proc
$ git checkout v2.0.2
Note: checking out 'v2.0.2'.
$ make
cc -O2 -fPIC -std=c99 -Wall -pedantic -DUTF8PROC_EXPORTS -c -o utf8proc.o utf8proc.c
rm -f libutf8proc.a
ar rs libutf8proc.a utf8proc.o
ar: creating libutf8proc.a
cc  -shared -o libutf8proc.so.2.0.2 -Wl,-soname -Wl,libutf8proc.so.2 utf8proc.o
chmod a-x libutf8proc.so.2.0.2
ln -f -s libutf8proc.so.2.0.2 libutf8proc.so
ln -f -s libutf8proc.so.2.0.2 libutf8proc.so.2
# make install
mkdir -m 755 -p /usr/local/include
install -m 644 utf8proc.h /usr/local/include
mkdir -m 755 -p /usr/local/lib
install -m 644 libutf8proc.a /usr/local/lib
install -m 755 libutf8proc.so.2.0.2 /usr/local/lib
ln -f -s libutf8proc.so.2.0.2 /usr/local/lib/libutf8proc.so
ln -f -s libutf8proc.so.2.0.2 /usr/local/lib/libutf8proc.so.2
# ldconfig
# emerge app-i18n/kakasi
[...Portage installiert kakasi...]

mapnik-german-l10n ist eine Erweiterung (extension) für PostgreSQL, die insbesondere Transkriptionsfunktionen für asiatische Ortsnamen zur Verfügung stellt. Zwar ist es wohl möglich, den Stil irgendwie ohne sie zu verwenden und so die Abhängigkeiten von kakasi und utf8proc zu vermeiden (wenn man auf Karten asiatischer Lande verzichten kann), aber dieses Vorgehen ist nicht dokumentiert (im Gegensatz zum hier vorgestellten, das zumindest in Ansätzen dokumentiert ist) und wird daher hier nicht weiter verfolgt.

Die README von mapnik-german-l10n ist auf Nicht-Debian-Systemen vollständig zu ignorieren. Die korrekte Installationsanleitung befindet sich in der Datei INSTALL.md. Dabei ist zwingend darauf zu achten, eine stabile Version von mapnik-german-l10n zu installieren und nicht einfach das zu bauen, was aktuell in master ist. Diese läsige Angewohnheit legt man sich nämlich zu, wenn man zu viel mit NodeJS-Projekten zu tun hat, die gar nicht erst Gedanken an stabile Versionen verschwenden. Das kann hier zuschlagen, weil dann die später noch auszuführenden SQL-Skripte des deutschen OpenStreetMap-Stils fehlschlagen. Als dieser Artikel verfasst wurde, war Version 2.1.5 aktuell.

Den Inhalt und die Implikationen der INSTALL-Datei kurz zusammengefasst läuft die Installation so ab:

$ git clone git://github.com/giggls/mapnik-german-l10n.git
$ cd mapnik-german-l10n.git
$ git checkout v2.1.5
$ make
# make install

Das installiert die PostgreSQL-Erweiterung unmittelbar nach /usr/share/postgresql-9.5/extension, was recht misslich ist, wenn man sie irgendwann einmal wieder deinstallieren möchte. Ich kenne mich nicht ausreichend mit PostgreSQL aus, um sagen zu können, ob eine Installation unterhalb von /usr/local möglich gewesen wäre oder ob PostgreSQL dann die Erweiterung nicht finden würde — in diesem Fall wäre dennoch wünschenswert gewesen, dass die Makefile ein Target uninstall bereitstellt. So bleibt es jedem überlassen, die Ausgabe von make install kleinlich zu verfolgen oder die Paketeigentumsverhältnisse unterhalb des Verzeichnisses /usr/share/postgresql-9.5/extension manuell festzustellen. Die installierten Dateien sind dann eben diejenigen, die keinem Paket zugeordnet sind.

Zu pyyaml und carto sogleich.

Datenbank erstellen

Jetzt kann mit der Erstellung der Datenbank weitergemacht werden. Dazu zunächst zum Datenbank-Superuser wechseln:

$ su -
# su - postgres
$ psql

Anschließend mit den folgenden Befehlen einen Nutzer erstellen und die Datebank namens osm einrichten. Der Datenbankname ist zwingend, es sei denn, man möchte alle Vorkomnisse dieses Datenbanknamens im Stil ändern (was vermutlich wegen der „Allerweltszeichenfolge“ osm, die in OpenStreetMap-Projekten überall vorkommen dürfte, nicht ganz einfach wäre).

CREATE USER quintus WITH PASSWORD 'geheimespasswort';
CREATE DATABASE osm WITH OWNER quintus;
\q

Die verschiedenen CREATE EXTENSION-Anweisungen gehen zum Teil auf meine letzte Anleitung, zum anderen Teil auf die Installationsbeschreibung von mapnik-german-l10n zurück, wobei die unaccent-Extension vermutlich zZt. noch unnötig ist, da sie erst im master-Branch erwähnt wird. Ich dokumentiere sie hier, um sie für die nähere Zukunft präsent zu haben.

$ psql osm
CREATE EXTENSION hstore;
CREATE EXTENSION postgis;
CREATE EXTENSION unaccent;
CREATE EXTENSION osml10n;
\q

Python-Software

Die erforderliche Python-Software lässt sich wie folgt installieren (bitte darauf achten, dass das oben eingerichtete Virtualenv aktiv ist):

$ pip install pyyaml lxml mapnik nik4

Wie oben schon erwähnt, ist pyyaml erforderlich, aber nicht dokumentiert. Es wird vom später aufzurufenden Skript yaml2mml.py des deutschen OSM-Stils benötigt. Die übrige Python-Software ist Nik4 mit seinem Abhängigkeiten, dazu schon im letzten Artikel.

carto

Das offizielle carto

Auch der deutsche OpenStreetMap-Stil ist in CartoCSS realisiert, also einem Format, das durch Übersetzen zunächst in das von Mapnik verstandene XML-Format übersetzt werden muss. Dies erfolgt herkömmlicherweise mit dem NodeJS-Werkzeug carto, einer für NodeJS geschriebenen Software. Installation wie gehabt:

$ git clone git://github.com/mapbox/carto.git
$ cd carto
$ git checkout v0.16.3
$ npm install
$ export PATH="$PWD/bin:$PATH"

Magnacarto?

Alternativ soll es möglich sein, eine Alternativimplementation von CartoCSS, Magnacarto, zu benutzen, die aber schon laut Selbstbeschreibung Probleme mit dem offiziellen OSM-Stil hat, und zwar insbesondere im Bereich der Variablennamensubstitution. Versucht man die Anwendung, quittiert Magnacarto auch tatsächlich mit den folgenden Warnungen:

$ magnacarto -mml project-de.mml -builder mapnik3 > de.xml
2016/12/10 12:29:23 File not found: symbols-de/shields/motorway_[width]x[height].svg
2016/12/10 12:29:23 File not found: symbols-de/shields/motorway_[width]x[height]_z16.svg
2016/12/10 12:29:23 File not found: symbols-de/shields/motorway_[width]x[height]_z18.svg
2016/12/10 12:29:23 File not found: symbols-de/shields/primary_[width]x[height].svg
2016/12/10 12:29:23 File not found: symbols-de/shields/primary_[width]x[height]_z16.svg
2016/12/10 12:29:23 File not found: symbols-de/shields/primary_[width]x[height]_z18.svg
2016/12/10 12:29:23 File not found: symbols-de/shields/secondary_[width]x[height].svg
2016/12/10 12:29:23 File not found: symbols-de/shields/secondary_[width]x[height]_z16.svg
2016/12/10 12:29:23 File not found: symbols-de/shields/secondary_[width]x[height]_z18.svg

Es schließt aber trotzdem erfolgreich ab. Auf den ersten Blick konnte ich keine Fehler in den generierten Karten entdecken, aber um subtile Probleme zu vermeiden, rate ich daher bis auf weiteres von der Verwendung ab. Magnacarto ist in Go geschrieben, weshalb es für jemanden, der unter der unfassbar schnellen Entwicklung von NodeJS leidet, durchaus seinen Charme hat. Solange dieses Problem aber nicht gelöst ist, will ich darauf nicht weiter eingehen. Der Rest des Artikels benutzt das offizielle carto.

Deutschen OpenStreetMap-Stil herunterladen

Der deutsche OpenStreetMap-Stil ist ein Fork vom internationalen openstreetmap-carto, der versucht, mit ihm möglichst Schritt zu halten. Ich möchte in diesem Kontext besonders darauf hinweisen, dass die auf GitHub angegebenen Releases genau gar nichts mit dem deutschen Stil zu tun haben, sondern einfach nur die Tags aus dem internationalen Stil sind. Sie zu verwenden, würde dazu führen, dass man den internationalen Stil benutzt (das ist der Eigenart von Git geschuldet). Der deutsche Stil ist daher zumindest momentan immer vom Branch master aus zu benutzen; er tätigt keine eigenen Releases.

$ git clone git://github.com/giggls/openstreetmap-carto-de.git
$ cd openstreetmap-carto-de

Daten importieren

Nun kann dazu übergegangen werden, die OpenStreetMap-Daten in die neue Datenbank osm zu importieren. Da der deutsche Stil einen anderen Gebrauch von HSTORE-Spalten in PostgreSQL macht, unterscheidet sich das Import-Kommando von demjenigen für den internationalen Stil; Details dazu finden sich in der Installationsbeschreibung des Stils. Zusammen mit den Angaben aus meinem vorigen Artikel ergibt sich folgendes Bild:

$ osm2pgsql --style hstore-only.style \
  --database osm --hstore --hstore-match-only \
  --prefix planet_osm_hstore --number-processes 3 \
  arnsberg-regbez-latest.osm.pbf

Im Gegensatz zu meinem letzten Artikel empfehle ich ausdrücklich, die Option --slim (und die davon abhängige Option --cache) wegzulassen, wenn man genug Arbeitsspeicher (vermutlich ab ca. 8 GiB) hat; dies führt zu einem überaus erheblichen Geschwindigkeitszuwachs beim Import. Die im letzten Artikel noch verwandte Option --multi-geometry scheint zwischenzeitlich aus osm2pgsql entfernt worden zu sein und muss daher wohl nicht mehr übergeben werden.

Die Shapefiles können ganz normal (also wie beim internationalen Stil) heruntergeladen werden (dafür einige Zeit einplanen, es werden ca. 700 MiB an Daten heruntergeladen):

$ ./get-shapefiles.sh

Views erzeugen

Der deutsche OSM-Stil benutzt SQL-Views (eine Art virtuelle Tabellen), die erst erstellt werden müssen. Dafür liefert er einige Skripte mit, die nunmehr auszuführen sind:

$ psql -d osm -f osm_tag2num.sql
$ psql -d osm -f views_osmde/view-line.sql
$ psql -d osm -f views_osmde/view-point.sql
$ psql -d osm -f views_osmde/view-polygon.sql
$ psql -d osm -f views_osmde/view-roads.sql

Das sollte anstandslos funktionieren. Taucht dagegen die folgende Fehlermeldung auf, dann wurde mapnik-german-l10n nicht in einer stabilen, sondern in einer Entwicklerversion installiert. Die Lösung des Problems besteht darin, es wieder zu deinstallieren und die letzte stabile Version zu installieren.

$ psql -d osm -f views_osmde/view-line.sql

psql:views_osmde/view-line.sql:55: FEHLER:  ungültige Eingabesyntax für Typ boolean: »de«
ZEILE 48: osml10n_get_placename_from_tags(tags,true,'de',way) as local...
                                                    ^
psql:views_osmde/view-line.sql:57: FEHLER:  Relation »planet_osm_line« existiert nicht

Anwendung

Endlich. Jetzt kann der deutsche OSM-Stil in Mapnik-XML übersetzt werden. Dazu genügt ein schlichter Aufruf von make; dieser ruft dann das Skript yaml2mml.py auf, um die MML-Datei zu erzeugen, die Carto benötigt, und sodann Carto selbst, um das Mapnik-XML zu erzeugen, welches dann in der Datei osm-de.xml abgelegt wird.

$ make
scripts/yaml2mml.py --yaml project.yaml --check >project-de.mml
carto -a 3.0.9 project-de.mml > /tmp/tmp.13CgCx3nzp
mv /tmp/tmp.13CgCx3nzp osm-de.xml

Nun kann die eigentliche Kartenerstellung beginnen. An Nik4 hat sich seit meinem letzten Artikel nichts maßgeblich verändert, sodass ich einfach auf ihn verweisen kann. Zu beachten ist lediglich, dass das zu übergebene XML das in der soeben erzeugten osm-de.xml ist. Beispielaufruf:

$ nik4.py -s 60000 --ppi 600 -a 4 \
  -c 7.6829 51.5329 --margin 10 \
  osm-de.xml print.png

Das Ergebnis sieht dann so aus wie dieses Bild (Stadt Unna und Umgebung im Maßstab 1:60000 bei 600 DPI).

Valete.