Programmvorstellung: Datenkacheln erzeugen mit Mapsplit

Hallo zusammen!

Peda, der zurzeit gemeinsam mit mir an OSM2World-Anwendungen bastelt, hat als “Nebenprodukt” ein Werkzeug zum Splitten von OSM-Dateien veröffentlicht:

Mapsplit

Wikiseite: http://wiki.openstreetmap.org/Mapsplit
Github: https://github.com/PedaB/mapsplit

Mapsplit ist ein Kommandozeilentool, das eine pbf-Datei einliest und daraus zahlreiche kleine pbf-Datenkacheln erzeugt. Diese Kacheln entsprechen der bekannten Kachelnummerierung in Slippymaps und umfassen die vollständigen Daten aller Nodes, Ways und Relations, die zumindest teilweise in der Kachel liegen.

Fokus der bisherigen Entwicklung war die Verarbeitungsgeschwindigkeit bei “mittelgroßen” OSM-Dateien. Die Software speichert Zwischenergebnisse komplett im Arbeitsspeicher und nutzt für diesen Zweck handoptimierte Datenstrukturen. Die Größe des Arbeitsspeichers ist daher auch die entscheidende Beschränkung für die Größe der verarbeiteten Dateien.

Das Programm ist in Java geschrieben und Open Source unter CC0. Da der Autor selbst nicht im Forum aktiv ist, hat er es mir überlassen, Mapsplit hier vorzustellen.


$ ./mapsplit -h
Exception in thread "main" java.lang.NoClassDefFoundError: MapSplit
Caused by: java.lang.ClassNotFoundException: MapSplit
        at java.net.URLClassLoader$1.run(URLClassLoader.java:217)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:205)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:321)
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:294)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:266)
        at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:334)
Could not find the main class: MapSplit. Program will exit.

Mmh, zu kompliziert für mich :wink:

mapsplit.jar → …/dist/mapsplit.jar
das wird nicht klappen :frowning:

Man sollte daher im wiki erwähnen, zuvor ein
ant dist
auszuführen.


./mapsplit ../mittelfranken.osm.pbf /tmp/tile
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
        at HeapMap.<init>(HeapMap.java:52)
        at MapSplit.<init>(MapSplit.java:99)
        at MapSplit.run(MapSplit.java:450)
        at MapSplit.main(MapSplit.java:606)

Immer das gleiche mit diesen JAVA-proggies, unter 1 TB RAM läuft nix :wink:

Dann kennst du das ja vermutlich schon: -Xmx hilft. :wink:

Ein -Xmx2G reicht bei mir für Bayern gut, da sollte auch Mittelfranken allein machbar sein. Den Parameter am Besten in das Startskript reinschreiben.

Nein, -Xmx hilft leider nicht.
Nur weil Mfr = 1/7 BY ist, heisst es nicht, dass auch nur 14 % des Speichers braucht.

Mein netbook hat nur 1 Gig und bei -xmx1G gibt es immer noch einen OutOfHeap.
Und bei -Xmx2G geht - logischerweise - ins Nirvana.

. falscher thread .

Soooo kleine Eingabedateien…Mittelfranken besteht doch schon kaum aus mehr als einem Tile :slight_smile:

Kellerma, bzgl deines erster ersten Antwort: Ich habe jetzt das dist-target als das default-target für ant gesetzt. Danke für den Hinweis.

Zu deinem netbook: 1GB ist natürlich wirklich wenig, die defaults reichen mindestens für Bayern und Ziel von mapsplit war ja gerade, dass alles in den Heap passt. Es gibt allerdings das -s/–size switch, mit dem du die Größe der Hashmaps angeben kannst. Jetzt besteht Mittelfranken aus ungefähr 2,5 Mio Nodes, 400k Wegen und 100k Relationen. Mit

mapsplit mittelfranken.pbf /tmp/tile -s=4000000,500000,200000

brauchst du <500MB Speicher zum Einlesen und Splitten der Daten. Wenn du nun aber ohne Datum alle Tiles abspeicherst, brauchst du noch etwas extra Speicher zum Verwalten der ganzen Ausgabefiles und du brauchst ein ulimit, das mehr als 1024 offene Dateien erlaubt (siehe Wiki) da Mittelfranken aus 1175 Zoom-13-Tiles besteht. Xmx750m sollte auf jeden Fall reichen, genaue Grenze weiß ich nicht :wink:

Ich bin als am Überlegen was mir die Daten ganau auf die Tiles geschnitten an “Mehrwert” bringen?

Für Rendern sind solche Tiles nicht wirklich brauchbar.
Ich sag nur Ludwigshafen und Mannheim - die bekommt man so nicht wirklich ordentlich gerendert!

Aber ich lass mich gerne von dem “Mehrwert” gegenüber dem normalen Ausschneiden überzeugen!

Danke für den Tipp, Peda.

Mittels “-Xmx700M” und fast keinen swappen scheint es zu funkionieren:


$ ulimit -Sn
4096
$ ulimit -Hn
10240
$ ./mapsplit ./mittelfranken.osm.pbf /tmp/tile -s=4000000,500000,200000
Compressed buffers are too short, causing extra copy
Compressed buffers are too short, causing extra copy
Compressed buffers are too short, causing extra copy
Compressed buffers are too short, causing extra copy
Compressed buffers are too short, causing extra copy
Compressed buffers are too short, causing extra copy
Compressed buffers are too short, causing extra copy
Compressed buffers are too short, causing extra copy
Compressed buffers are too short, causing extra copy
Compressed buffers are too short, causing extra copy
Compressed buffers are too short, causing extra copy
$ echo $?
0
$ ls -l tile43*| wc -l
1175

Interessant ist, das ein aus mittelfranken.osm.pbf per osmconvert geschnittenes Nürnberg nicht funktioniert:


$ ./mapsplit ./Nürnberg.osm.pbf /tmp/nbg -s=4000000,500000,200000
Exception in thread "Thread-0" java.lang.ArrayIndexOutOfBoundsException: -1
        at HeapMap.update(HeapMap.java:188)
        at MapSplit.addWayToMap(MapSplit.java:198)
        at MapSplit.access$200(MapSplit.java:50)
        at MapSplit$1.process(MapSplit.java:288)
        at crosby.binary.osmosis.OsmosisBinaryParser.parseWays(OsmosisBinaryParser.java:172)
        at crosby.binary.BinaryParser.parse(Unknown Source)
        at crosby.binary.BinaryParser.handleBlock(Unknown Source)
        at crosby.binary.file.FileBlock.process(Unknown Source)
        at crosby.binary.file.BlockInputStream.process(Unknown Source)
        at crosby.binary.osmosis.OsmosisReader.run(OsmosisReader.java:37)
        at java.lang.Thread.run(Thread.java:636)
Exception in thread "main" java.io.IOException: Could not read file fully
        at MapSplit.setup(MapSplit.java:318)
        at MapSplit.run(MapSplit.java:453)
        at MapSplit.main(MapSplit.java:606)

Ich habe osmconvert bislang noch nicht benutzt, aber ich denke ich kenne das Problem: Das passiert wenn Wege nicht komplett gespeichert sind. Es sollte also ein Weg in den Daten sein für den nicht alle zugehörigen Nodes im Datensatz enthalten sind, kann das sein?

Der Mehrwert bzw. der Grund das Tool zu schreiben war recht einfach: Wir hatten nach einem Weg gesucht, alle Tiles neu zurendern die sich geändert haben. Dafür brauchten wir erstens die Information welche Tiles sich verändert haben und zweitens brauchten wir die Daten der Kachel um sie zu rendern. Anfangs hatten wir das mit osmosis ausgeschnitten und die Überlegung war: Warum zig mal osmosis aufrufen, wenn das ganze auch viel schneller mit nur einem Aufruf zu erledigen ist (bei dieser Gelegenheit können wir auch gleich prüfen, welche Tiles sich verändert haben und nur diese abspeichern).

Entschuldige bitte meine Unwissenheit, aber kannst du mir auch erklären, wieso man sie dafür nicht gebrauchen kann? Genau dafür benutzen wir (Tordanik und ich) sie nämlich: Um eine 3D-Karte von Bayern zu rendern. Wäre also nett, wenn du das noch etwas ausführen könntest wo du das Problem siehst.

Ludwigshafen (Rheinland-Pfalz) und Mannheim (Baden-Württemberg) liegen in verschiedenen Bundesländern. Das bedeutet, dass sie in zwei verschiedenen Extrakten der Geofabrik liegen. Das leidige Problem von grenzüberschreitenden Gebieten eben.

Keine Ahnung, ob Mapsplit damit umgehen kann oder ob es das überhaupt können soll.

Edbert (EvanE)

Das Problem ist vielleicht nicht das Auschneiden - in z.B. Germany ist ja alles drin.

Sondern das Ludwigshafen und Mannheim erst ab Zoom kleiner 7 in einer Kachel liegen aber beim Rendern der Name Ludwigshafen in die östliche Kacheln hineinragt (das ist hier nur ein (offensichtliches) Beispiel es gibt etliche stellen wo Text in die Nachbarkachel ragt).
Also brauch ich auch vom Umland der Kachel Daten damit sowas mitgerendet wird und nicht am Rand der kachel abgeschnitten ist!
Wenn jemand wirklich egal ist was ausserhalb jeder Kachel ist - ists vielleicht egal , aber spätestens wenn man in verschiednenen Arbeitsschritten die Nachbarkacheln mit hinzunimmt gibts da zumindestens unschöne Effekte!

Ähnliche Probleme haben wir vom Prinzip her auch bei unserem Projekt. Wir sind aber eigentlich gut mit der simplen Lösung gefahren, jeder Kachel noch einen Datensaum rings um die eigentlich enthaltenen Daten zu spendieren, also Daten nahe der Kachelgrenze zusätzlich auch noch einmal in der Nachbarkachel abzuspeichern. Diese Option ist für Mapsplit geplant und steht auf der im Wiki verlinkten TODO-Liste unter der vielleicht nicht selbsterklärenden Bezeichnung “border treatment”.

Ich will auch noch mal etwas auf geeignete Anwendungsszenarien eingehen. Wie Peda erklärt hat: Wir verwenden diese Kacheln für “realistisches” 3D-Rendering auf einem Tileserver. Dieser Anwendungsfall hat bestimmte Eigenheiten, deretwegen so etwas wie Mapsplit besonders nützlich ist:

  • Ein Objekt hat in der Regel nur in einem eher kleinen Umkreis Auswirkungen. Eine Baumkrone mag ein Stück in die Nachbarkachel ragen, aber da hilft der angesprochene Datensaum.

  • In einer Kachel werden fast alle Daten für die Darstellung benötigt.

Kurz: Fürs Rendern einer Kachel braucht man die dortigen Daten - und nur diese.

Ich denke, dass gerade bei den detaillierten Zoomstufen unserer gewohnten Kartenstile eigentlich sehr ähnliche Bedingungen vorliegen. Bei Aufgaben mit einer komplett anderen Sicht auf die Daten (wie Routing) halte ich Datenkacheln für ungeeignet, obwohl ich mich gerne überraschen lasse. :wink:

Supi, funktionokelt :slight_smile:


$ osmconvert --complete-ways ./mittelfranken.osm.pbf -B=./polygon.62780.poly --out-pbf > nürnberg.osm.pbf
$ ./mapsplit ./nürnberg.osm.pbf /tmp/nbg -s=4000000,500000,200000 -t
Compressed buffers are too short, causing extra copy
Saving 135 tiles took 18963ms

Overall runtime: 30349ms == 0min
$ echo $?
0
$

Große Freude auch in ganz anderer Richtung:
Zusammen mit dem Schweizer, was bekamen wir “Haue” hier im Forum, als wir ähnliches angedacht haben vor weit über 1 Jahr:
Was soll das und überhaupt!

:wink:

Super das es auch mit osmconvert - Auschnitten funktioniert.

Allerdings würde ich bei osmconvert die Optionen : --complex-ways --emulate-osmosis

verwenden.

–complete-ways : Läd zwar alle Ways die in der Kachel einen Node haben - aber bei Relationen die aus Ways Flächen erstellen ist grad bei kleinen Kacheln die Gefahr groß das die Fläche durch fehlende Ways nicht geschlossen werden kann und deshalb nicht gezeichnet wird (z.B der Harz ist fast eine einzige Wald-Relation - wenn da ein Way fehlt wird kein Wald gepinselt).

deshalb besser : --complex-ways

Einige Programme motzen bei der osmconvert-Standard-Syntax rum Abhilfe schafft da : --emulate-osmosis

Mmh, dadurch hat sich aber die Anzahl der Kacheln mehr als verdopppelt:

--complete-ways --emulate-osmosis
Saving 135 tiles took 18571ms

--complex-ways --emulate-osmosis
Saving 296 tiles took 21584ms

Es sind schon bei “–complete-ways” z. B. “weit ausufernde Stromleitungen” enthalten,
das wird mit “–complex-ways” noch viel schlimmer, ich will ja nur Nürnberg haben:
Fläche Nbg: 186.38 km2
Fläche Mfr: 7244.85 km2
somit Anzahl Kacheln Nbg: 1175 * (186.38/7244.85) = 31

Dann läuft das nicht so wie gedacht: Mit complex-ways sollen die “ausufernden” Wege und Relationen in der jeweiligen Kachel mit gespeichert werden, nicht in zusätzlichen Kacheln.

@Tordanik:
Habt ihr denn schon mal versucht, den Harz zu rendern? Da ist es mit “kleinem Saum” nicht getan.

Gruß,
ajoessen

Nö, “–complex-ways” ist eine Option von osmconvert, davon und vom Begrenzungspolygon hat mapsplit ja keine Ahnung.

Das beste - für mich :wink: - wäre, wenn ich ganz normal mit osmconvert schneiden könnte (also ohne “–complete-ways” und ohne
“–complex-ways”) und mapsplit sich an den “fehlenden” nodes von Wegen nicht stören würde.

Alternativ dazu:
mapsplit kann selbst mit Begrezungspolygonen umgehen, z. B. “-B=./polygon.62780.poly”

Noch 'ne Alternative:
Ein proggie sagt mit, dass “nbg4347_2797.pbf” aufgrund seiner Kachelnummer “4347” sowie “2797” innerhalb des Begrenzunspolygon polygon.62780.poly liegt bzw. tangiert jenes
und die (echt) außerhalb davon kann ich wegschmeissen.

Das finde ich eine gute Idee, das bau ich ein, bzw. habe es mal auf meine TODO-Liste gesetzt :wink:

Wir rendern derzeit nur Bayern und somit natürlich nicht den Harz. Ich denke aber, dass wir das Problem lösen können und demnächst werden, da wir auf dieses Problem schon selbst gestoßen sind: Wenn ein geschlossener Weg um eine Kachel herumläuft (also kein Knoten in der Kachel liegt) wird der Weg derzeit nicht in die Kachel gespeichert. Das ist also lösbar und hoffentlich demnächst gefixt.

So, hab ein paar Updates auf github master bzw. ein neues Release online gestellt.

Erstens gibt es Unterstützung für den “kleinen Saum”. Mit der Option

-b=0.1

kann man z.B. einen 10% Rand einfügen. Das sorgt dafür, dass Beschriftungen (oder in unserem Fall 3D-Gebäude) die in 2 Kacheln sichtbar sind nun korrekt gerendert werden, da sie eben in beiden Kacheln liegen.

Zweitens habe ich Unterstützung für das “Füllen von Löchern” hinzugefügt. Geht ein geschlossener Weg komplett um eine Kachel herum, so wird dieser Weg trotzdem mit in die Kachel aufgenommen. Damit sollten sich auch solche Dinge wie der Harz korrekt rendern lassen, bzw. in unserem Fall ein paar Waldstücke rund um Aschaffenburg.

Vielen Dank für das Feedback.
Begrenzungspolygon kommt demnächst… :wink:

Super Sache, Kompliment!

Zu --complex-ways: Manchmal reicht auch ein --drop-broken-refs, um Daten, die nicht so aufwändig ausgeschnitten sind, für andere Programme verwendbar zu machen. Mit allen bekannten Nachteilen, natürlich.