DE:PBF Format
Das PBF Format ("Protocolbuffer Binary Format") ist in erster Linie dazu bestimmt, das XML Format zu ersetzen. Es benötigt lediglich die Hälfte des Speichers im Vergleich zu gzip und etwa 70% gegenüber bzip beim Planet.osm. Es kann fünf mal schneller als gzip und sechs fach schneller als bzip gelesen und geschrieben werden. Das Format wurde entwickelt, um zukünftige Erweiterbarkeit und Flexibilität zu unterstützen.
Das zugrunde liegende Dateiformat ist darauf ausgelegt, wahlfreien Zugriff auf Dateiblockebene zu unterstützen. Jeder Datei-Block ist unabhängig dekodierbar und enthält etwa achttausend OSM Einträge in der Standardkonfiguration. Es wird kein Tag-Hardcoding verwendet, alle Schlüssel und Werte werden in voller Länge als Strings gespeichert. Für zukünftige Skalierbarkeit, werden 64-Bit IDs für Nodes / Ways / Relations verwendet. Der aktuelle Konverter (Osmosis) erhält die OSM Einträge und Tags in Eingabereihenfolge. Zur flexiblen Handhabung verschiedener Genauigkeiten von Orten und Zeitstempel können Auflösungen als Vielfaches von Millisekunden und Nanograd angegeben werden. Die Standardwerte sind 1000 Millisekunden und 100 Nanograd; dies entspricht etwa 1 cm am Äquator und ist die aktuell verwendete Auflösung innerhalb der OSM-Datenbank.
Dateien haben die Endung *.osm.pbf
Zur Zeit ist die Referenz-Implementierung von PBF in zwei Teile geteilt, die Osmosis-spezifischen Teil, in dem Osmosis-Repository enthalten [1], und eine Implementierung für andere Anwendungen unter [2]. Dieser Teil wird verwendet, um die osmpbf.jar (wie in Osmosis und anderen Java PBF Anwendungen) zu erstellen die auch die Master-Definition des Dateiformats enthält (*.proto-Dateien).
Software die PBF unterstützt
Eine Menge von Software des OSM-Projekts unterstützt bereits PBF in Ergänzung zum ursprünglichen XML-Format, dazu gibt es mehrere Werkzeuge, um PBF in XML konvertieren und umgekehrt.
In PBF/Software_Compliance ist dokumentiert welche Arten von PBF-Dateien von den verschiedenen Programmen unterstützt werden.
Design
Low-Level-Encoding
Die Google Protocol-Buffer werden als Low-Level-Speicher verwendet. Den Spezifikationsdateien folgend, schreibt der Protocol-Buffer-Compiler Low-Level Serialisierung-Code. Einzelne Nachrichten können anderen untergeordnet werden, und so hierarchische Strukturen bilden. Der Protocol-Buffer unterstützt Erweiterungen, so können neue Felder zu einer Nachricht hinzugefügt werden und Anwendungen können diese Nachrichten ohne Neukompilierung lesen. Weitere Details finden sich auf der Projektseite oder in Googles Opensource Blog. Google unterstützt offiziell C++, Java und Python, aber es gibt auch Compiler für andere Sprachen. Eine beispielhafte Definition einer Nachricht ist:
message Node {
required sint64 id = 1;
repeated uint32 keys = 2 [packed = true]; // bezeichnede Zeichenkette
repeated uint32 vals = 3 [packed = true]; // bezeichnede Zeichenkette
optional Info info = 4; // Enthält Metadaten
required sint64 lat = 8;
required sint64 lon = 9;
}
Protocol Buffer verwenden eine variable Bitbreiten-Kodierung für ganze Zahlen. Die Zahl wird in 7 Bits pro Byte kodiert das höchste Bit zeigt an ob das nächste Bit auch zu der Zahl gehört. Wenn Nachrichten kleine Zahlen enthalten minimiert dies die Dateigröße. PBF ermöglicht zwei verschiedene Kodierungen, eine für ganze Zahlen mit meist positivem Vorzeichen und eine für gemischte Vorzeichen. Die erste Kodierung benötigt für kleine Zahlen (0-127) ein Byte. Größere (128-16383) zwei Byte usw. Die alternative Kodierung speichert die Vorzeichen an der niedrigsten Bit-Position; Zahlen zwischen -64 und 63 erfordern ein Byte, -8192 bis 8191 zwei Byte usw. Weitere Informationen zum Integer-Datenformat findet man auf der Website.
Die erzeugten Dateien verwenden ein Java-Paket von crosby.binary. In anderen Sprachen sind die erzeugten Dateien im Paket OSMPBF.
Dateiformat
Eine Datei enthält einen Dateikopf, gefolgt von einer Sequenz von Dateiblöcken. Der Entwurf ermöglicht zukünftigen wahlfreien Zugriff und ein Überspringen von nicht benötigten oder nicht lesbaren Informationen.
Die Dateiblöcke (Blob) sind Sequenz von:
- int4: Länge des BlobHeaders in Netzwerk Byte-Order
- serialisierte Blob-Kopf Nachricht
- serialisierte Blobnachricht (Größe im Kopf angegeben)
Ein BlobHeader ist derzeit definiert als:
message BlobHeader {
required string type = 1;
optional bytes indexdata = 2;
required int32 datasize = 3;
}
- type enthält den Typ der Daten im Blob.
- indexdata hier können Metadaten zu den folgenden Blob enthalten sein, (z.B. für OSM-Daten, es könnte eine Bounding Box enthalten.) Dieser Eintrag soll die zukünftige Gestaltung der indizierten *.osm.pbf Dateien ermöglichen.
- datasize enthält die serialisierte Größe der anschließenden Blob-Nachricht.
Anmerkung: Beachte dass der BlobHeader früher BlockHeader genannt wurde. In Version 1.1 wurde dieser Umbenannt um Verwechslungen mit dem HeaderBlock auszuschließen; s.u.
In einem Blob werden derzeit alle Daten gespeichert entweder unkomprimiert oder in zlib / deflate komprimierten Format.
message Blob {
optional bytes raw = 1; // keine Kompression
optional int32 raw_size = 2; // Nur gesetzt, wenn komprimiert, auf die unkomprimierte Größe
optional bytes zlib_data = 3;
// optional bytes lzma_data = 4; // GEPLANT.
// optional bytes OBSOLETE_bzip2_data = 5; // Veraltet.
}
Hinweis (12/2010): Kein Encoder unterstützt derzeit lzma oder bzip2. Zur Vereinfachung der Decoder-Implementierung, ist bzip2 als veraltet markiert worden und LZMA ist der derzeitige vorschlag für eine Erweiterung.
Um robust beschädigte Daten zu erkennen, wurde die maximale Größe der BlobHeader und Blob-Nachrichten beschränkt. Die Länge der BlobHeader *sollte* weniger als 32 KiB und muss weniger als 64 KiB sein. Die unkomprimierte Länge eines Blob *sollte* weniger als 16 MiB und *muss* weniger als 32 MiB sein.
OSM Einträge in Dateiblöcke kodieren
Es gibt zur Zeit zwei Dateiblockarten für OSM-Daten. Diese Text-Strings werden im Typenfeld im BlobHeader gespeichert.
- 'OSMHeader': Dieser Blob enthält eine serialisierte HeaderBlock Nachricht (siehe osmformat.proto). Jede Datei muss einen dieser Blöcke vor dem ersten "OSMData" Block besitzen.
- 'OSMData': Enthält eine serialisierte PrimitiveBlock Nachricht (siehe osmformat.proto). Diese enthalten die Einträge.
Dieses Design ermöglicht es das Software das Format durch weitere Dateiblöcke anderer Typen für ihre eigenen Zwecke erweitern. Parser sollten ihnen nicht bekannte Dateiblocktypen überspringen und ignorieren.
Definition des OSMHeader Dateiblocks
message HeaderBlock {
optional HeaderBBox bbox = 1;
/* Zusätzliche Tags zum Parsen dieses Datensatzes */
repeated string required_features = 4;
repeated string optional_features = 5;
optional string writingprogram = 16;
optional string source = 17; // Vom bbox-Feld.
}
Um eine Auf-und Abwärtskompatibilität zu bieten muss ein Parser wissen ob er in der Lage ist eine Datei zu lesen. Alle besonderen Eigenschaften werden im OSMHeader festgehalten. Sollte das Lesen Datei eine Fähigkeit erfordern, die der Parser nicht besitzt, muss er das Lesen der Datei mit einer Fehlermeldung quittieren, die besagt, welche Funktion ihm fehlt.
Gegenwärtig sind die folgenden Eigenschaften definiert:
- "OsmSchema-V0.6" – Die Datei enthält Daten, im OSM-Schema v0.6.
- "DenseNodes" – Die Datei enthält Kniten-Objekte in dicht gepackter Form.
Darüber hinaus kann eine Datei optionale Eigenschaften besitzen, die ein Parser nutzen kann. Beispielsweise kann der Inhalt einer Datei bereits sortiert sein, so dass sie vor der Verwendung nicht mehr sortiert werden muss. Oder die Weg-Objekte besitzen vorberechnete Begrenzungsrechtecke. Wenn ein lesendes Programm auf eine optionale Eigenschaft trifft, die es nicht unterstützt, kann es trotzdem die Datei sicher lesen. Sollte ein Programm eine optionale Eigenschaft voraussetzen, die die Datei nicht erfüllt, es das Lesen der Datei verweigern und mit einer Fehlermeldung quittieren.
Folgende optionale Eigenschaften sind vorgeschlagen:
- "Has_Metadata" – Die Datei enthält Zeitstempel und Informationen über letzte Änderung und Autor.
- "Sort.Type_then_ID" – Einträge sind nach Typ und untergeordnet nach ID sortiert.
- "Sort.Geographic" – Einträge sind auf bestimmte Art geometrisch sortiert.
- "timestamp=2011-10-16T15:45:00Z" – Übergangslösung zum Speichern eines Datei-Zeitstempels (verwendet von osmconvert).
Definition des OSMData Dateiblocks
Um die OSM-Einträge im Protokoll-Buffer zu kodieren, werden achttausend Einträge gesammelt zu einem PrimitiveBlock, welcher im 'OsmData' Blob-Eintrag serialisiert wird.
message PrimitiveBlock {
required StringTable stringtable = 1;
repeated PrimitiveGroup primitivegroup = 2;
// Einheit der Auflösung: Nanograd, zur Speicherung Koordinaten in diesem Block
optional int32 granularity = 17 [default=100];
// Offset-Wert zwischen der Koordinaten-Ausgabe den Koordinaten und dem Auflösungsraster - in Nanograd.
optional int64 lat_offset = 19 [default=0];
optional int64 lon_offset = 20 [default=0];
// Genauigkeit des Zeitpunkts, üblicherweise seit 1970, in Millisekunden.
optional int32 date_granularity = 18 [default=1000];
// Vorgeschlagene Erweiterung:
//optional BBox bbox = XX;
}
Innerhalb jedes Blocks werden alle Strings (key, value, role, user) in seperate String-Tabellen kopiert. Danach werden die Strings per Index in dieser Tabelle referiert, ausgenommen Index=0, dieser wird als Trennzeichen für DenseNodes verwendet. Leere Strings werden zur Zeit mit Index=0 kodiert. Um sicher zu stellen, dass für häufig auftretende Strings keine große Indexzahlen kodiert werden müssen wird die Tabelle nach Häufigkeit sortiert. Zur besseren Kompression mit zlib werden Strings mit der selben Häufigkeit alphabetisch sortiert.
Jeder PrimitiveBlock ist einzeln lesbar und enthält alle Informationen um die Einträge zu dekomprimieren die er enthält. Er enthält eine String-Tabelle und auch die Auflösung für die Position und die Zeitstempel. Ein Block mag jede Anzahl von Einträgen enthalten, solange er die Größenlimits beachtet. Allerdings der Einfachheitshalber werden von Osmosis (0.38) immer achttausend Einträge je Block gespeichert.
In addition to granularity, the primitive block also encodes a latitude
and longitude offset value. These values, measured in units of nanodegrees,
must be added to each coordinate.
latitude = .000000001 * (lat_offset + (granularity * lat)) longitude = .000000001 * (lon_offset + (granularity * lon))
Where latitude is the latitude in degrees, granularity is the granularity given in the PrimitiveBlock, lat_offset is the offset given in the PrimitiveBlock, and lat/lon is encoded in a Node or delta-encoded in a DenseNode. The explanation of the equation for longitude is analogous.
The reason that lat_offset and lon_offset exist is for concisely representing isohypsis data (contour lines) or other data that occurs in a regular grid. Say we wished to represent such data that was at a 100 microdegree grid. We would like to use a granularity of 100000 nanodegrees for the highest compression, except that that could only represent points of the form (.0001*x,.0001*y), when the real gridded data may be of the form (.00003345+.0001*x, .00008634+.0001*y). By using lat_offset=3345 and lon_offset=8634, we can represent this 100 microdegree grid exactly.
For datestamps,
millisec_stamp = timestamp*date_granularity.
Where timestamp is the timestamp encoded in an Info or delta encoded in a DenseInfo, date_granularity is given in the PrimitiveBlock, and millisec_stamp is the date of the entity, measured in number of seconds since the 1970 epoch. To get the date measured in seconds since the 1970 epoch, divide millisec_stamp by 1000.
Within each primitiveblock, I then divide entities into groups that contain consecutive messages all of the same type (node/way/relation).
message PrimitiveGroup {
repeated Node nodes = 1;
optional DenseNodes dense = 2;
repeated Way ways = 3;
repeated Relation relations = 4;
repeated ChangeSet changesets = 5;
}
After being serialized into a string, each primitiveblock is optionally gzip/deflate compressed individually when stored in the Blob fileblock.
Ways and Relations
For ways and relations, which contain the ID's of other nodes, I exploit the tendency of consecutive nodes in a way or relation to have nearby node ID's by using delta compression, resulting in small integers. (I.E., instead of encoding x_1, x_2, x_3, I encode x_1, x_2-x_1, x_3-x_2, ...). Except for that, ways and relations are mostly encoded in the way one would expect. Tags are encoded as two parallel arrays, one array of string-id's of the keys, and the other of string-id's of the values.
message Way {
required int64 id = 1;
// Parallel arrays.
repeated uint32 keys = 2 [packed = true];
repeated uint32 vals = 3 [packed = true];
optional Info info = 4;
repeated sint64 refs = 8 [packed = true]; // DELTA coded
}
Relations use an enum to represent member types.
message Relation {
enum MemberType {
NODE = 0;
WAY = 1;
RELATION = 2;
}
required int64 id = 1;
// Parallel arrays.
repeated uint32 keys = 2 [packed = true];
repeated uint32 vals = 3 [packed = true];
optional Info info = 4;
// Parallel arrays
repeated int32 roles_sid = 8 [packed = true];
repeated sint64 memids = 9 [packed = true]; // DELTA encoded
repeated MemberType types = 10 [packed = true];
}
Metadata includes non-geographic information about an object, such as:
message Info {
optional int32 version = 1 [default = -1];
optional int32 timestamp = 2;
optional int64 changeset = 3;
optional int32 uid = 4;
optional int32 user_sid = 5; // String IDs
// The visible flag is used to store history information. It indicates that
// the current object version has been created by a delete operation on the
// OSM API.
// When a writer sets this flag, it MUST add a required_features tag with
// value "HistoricalInformation" to the HeaderBlock.
// If this flag is not available for some object it MUST be assumed to be
// true if the file has the required_features tag "HistoricalInformation"
// set.
optional bool visible = 6;
}
Nodes
Nodes can be encoded one of two ways, as a Node (defined above) and a special dense format. In the dense format, I store the group 'columnwise', as an array of ID's, array of latitudes, and array of longitudes. Each column is delta-encoded. This reduces header overheads and allows delta-coding to work very effectively.
Keys and values for all nodes are encoded as a single array of stringid's. Each node's tags are encoded in alternating <keyid> <valid>. We use a single stringid of 0 to delimit when the tags of a node ends and the tags of the next node begin. The storage pattern is: ((<keyid> <valid>)* '0' )*
message DenseNodes {
repeated sint64 id = 1 [packed = true]; // DELTA coded
//repeated Info info = 4;
optional DenseInfo denseinfo = 5;
repeated sint64 lat = 8 [packed = true]; // DELTA coded
repeated sint64 lon = 9 [packed = true]; // DELTA coded
// Special packing of keys and vals into one array. May be empty if all nodes in this block are tagless.
repeated int32 keys_vals = 10 [packed = true];
}
DenseInfo does a similar delta coding on metadata.
Format example
In the following, we will have a look into the bytes of an OSM PBF file. The small regional extract bremen.osm.pbf (geofabrik.de, 2011-01-13) is used as an example.
Every data is preceded by a variable identifier. This identifier consists of type and id; the bits 0 through 2 stand for the type, bits 3 and above for the id. These types may be used:
- 0: V (Varint) int32, int64, uint32, uint64, sint32, sint64, bool, enum
- 1: D (64-bit) fixed64, sfixed64, double
- 2: S (Length-delimited) string, bytes, embedded messages, packed repeated fields
- 5: I (32-bit) fixed32, sfixed32, float
00000000 00 00 00 0d - length in bytes of the BlobHeader in network-byte order 00000000 __ __ __ __ 0a - S 1 'type' 00000000 __ __ __ __ __ 09 - length 9 bytes 00000000 __ __ __ __ __ __ 4f 53 4d 48 65 61 64 65 72 - "OSMHeader" 00000000 __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ 18 - V 3 'datasize' 00000010 7c - 124 bytes long 00000010 __ 10 - V 2 'raw_size' 00000010 __ __ 71 - 113 bytes long 00000010 __ __ __ 1a - S 3 'zlib_data' 00000010 __ __ __ __ 78 - length 120 bytes --- compressed section: 00000010 __ __ __ __ __ 78 9c e3 92 e2 b8 70 eb da 0c 7b ||.q.xx.....p...{| 00000020 81 0b 7b 7a ff 39 49 34 3c 5c bb bd 9f 59 a1 61 |..{z.9I4<\...Y.a| 00000030 ce a2 df 5d cc 4a 7c fe c5 b9 c1 c9 19 a9 b9 89 |...].J|.........| 00000040 ba 61 06 7a 66 4a 5c 2e a9 79 c5 a9 7e f9 29 a9 |.a.zfJ\..y..~.).| 00000050 c5 4d 8c fc c1 7e 8e 01 c1 1e fe 21 ba 45 46 26 |.M...~.....!.EF&| 00000060 96 16 26 5d 8c 2a 19 25 25 05 56 fa fa e5 e5 e5 |..&].*.%%.V.....| 00000070 7a f9 05 40 a5 25 45 a9 a9 25 b9 89 05 7a f9 45 |z..@.%E..%...z.E| 00000080 e9 fa 89 05 99 fa 40 43 00 c0 94 29 0c --- decompressed ---> 00000000 0a - S 1 'type' 00000000 __ 1a - length 26 bytes 00000000 __ __ 08 d0 da d6 98 3f 10 d0 bc 8d fe 42 18 80 00000010 e1 ad b7 8f 03 20 80 9c a2 fb 8a 03 - BBOX (4*Varint) 00000010 __ __ __ __ __ __ __ __ __ __ __ __ 22 - S 4 'required_features' 00000010 __ __ __ __ __ __ __ __ __ __ __ __ __ 0e - length 14 bytes 00000010 __ __ __ __ __ __ __ __ __ __ __ __ __ __ 4f 73 00000020 6d 53 63 68 65 6d 61 2d 56 30 2e 36 - "OsmSchema-V0.6" 00000020 __ __ __ __ __ __ __ __ __ __ __ __ 22 - S 4 'required_features' 00000020 __ __ __ __ __ __ __ __ __ __ __ __ __ 0a - length 10 bytes 00000020 __ __ __ __ __ __ __ __ __ __ __ __ __ __ 44 65 00000030 6e 73 65 4e 6f 64 65 73 - "DenseNodes" 00000030 __ __ __ __ __ __ __ __ 82 01 - S 16 'writingprogram' 00000030 __ __ __ __ __ __ __ __ __ __ 0f - length 15 bytes 00000030 __ __ __ __ __ __ __ __ __ __ __ 53 4e 41 50 53 00000040 48 4f 54 2d 72 32 34 39 38 34 - "SNAPSHOT-r24984" 00000040 __ __ __ __ __ __ __ __ __ __ 8a 01 - S 17 'source' 00000040 __ __ __ __ __ __ __ __ __ __ __ __ 24 - length 36 bytes 00000040 __ __ __ __ __ __ __ __ __ __ __ __ __ 68 74 74 00000050 70 3a 2f 2f 77 77 77 2e 6f 70 65 6e 73 74 72 65 00000060 65 74 6d 61 70 2e 6f 72 67 2f 61 70 69 2f 30 2e 00000070 36 - "http://www.openstreetmap.org/api/0.6" <--- decompressed --- 00000080 __ __ __ __ __ __ __ __ __ __ __ __ __ 00 00 00 00000090 0d - length in bytes of the BlobHeader in network-byte order 00000090 __ 0a - S 1 'type' 00000090 __ __ 07 - length 7 bytes 00000090 __ __ __ 4f 53 4d 44 61 74 61 "OSMData" 00000090 __ __ __ __ __ __ __ __ __ __ 18 - V 3 'datasize' 00000090 __ __ __ __ __ __ __ __ __ __ __ 90 af 05 - 87952 bytes long 00000090 __ __ __ __ __ __ __ __ __ __ __ __ __ __ 10 - V 2 'raw_size' 00000090 __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ 8f 000000a0 84 08 - 131599 bytes long 000000a0 __ __ 1a - S 3 'zlib_data' 000000a0 __ __ __ 88 af 05 - length 87944 bytes --- compressed section: 000000a0 __ __ __ __ __ __ 78 9c b4 bc 09 5c 14 57 ba 28 |......x....\.W.(| 000000b0 5e 75 aa ba ba ba ba 69 16 11 d1 b8 90 b8 1b 41 |^u.....i.......A| 000000c0 10 11 97 98 c4 2d 31 1a 27 b9 9a 49 ee 64 ee 8c |.....-1.'..I.d..| 000000d0 69 a0 95 8e 40 9b 06 62 32 f7 dd f7 5c 00 01 11 |i...@..b2...\...| 000000e0 11 05 11 11 71 43 45 44 40 05 44 54 14 17 44 44 |....qCED@.DT..DD| 000000f0 40 16 15 dc 00 37 50 44 05 c4 05 7d df 39 55 dd |@....7PD...}.9U.| etc. --- decompressed ---> 00000000 0a - S 1 'stringtable' 00000000 __ d4 2e - length 5972 bytes 00000000 __ __ __ 0a - S 1 00000000 __ __ __ __ 00 length 0 bytes 00000000 __ __ __ __ __ 0a - S 1 00000000 __ __ __ __ __ __ 07 length 7 bytes 00000000 __ __ __ __ __ __ __ 44 65 65 6c 6b 61 72 - "Deelkar" 00000000 __ __ __ __ __ __ __ __ __ __ __ __ __ __ 0a 0a |.......Deelkar..| 00000010 63 72 65 61 74 65 64 5f 62 79 0a 04 4a 4f 53 4d |created_by..JOSM| 00000020 0a 0b 45 74 72 69 63 43 65 6c 69 6e 65 0a 04 4b |..EtricCeline..K| 00000030 6f 77 61 0a 05 55 53 63 68 61 0a 0d 4b 61 72 74 |owa..UScha..Kart| 00000040 6f 47 72 61 70 48 69 74 69 0a 05 4d 75 65 63 6b |oGrapHiti..Mueck| etc. --- decompressed part form offset 5975 ---> 00000000 12 - S 2 'primitivegroup' 00000000 __ ad d5 07 - length 125613 bytes 00000000 __ __ __ __ 12 - S 2 -- Tag #2 in a 'PrimitiveGroup', containing a serialized DenseNodes. 00000000 __ __ __ __ __ a9 d5 07 - of length 125609 bytes 00000000 __ __ __ __ __ __ __ __ 0a - S 1 - Tag #1 in a DenseNodes, which is an array of packed varints. 00000000 __ __ __ __ __ __ __ __ __ df 42 - of length 8543 bytes 00000000 __ __ __ __ __ __ __ __ __ __ __ ce ad 0f 02 02 |..........B.....| 00000010 02 02 04 02 02 02 02 02 02 02 02 02 02 02 02 02 |................| 00000020 02 02 02 c6 8b ef 13 02 02 02 02 02 02 02 02 f0 |................| 00000030 ea 01 02 02 02 02 02 02 02 02 02 02 02 02 02 02 |................| Each varint is stored consecutively. We process until we have read 8543 bytes worth, then resume parsing the DenseNodes. The varints are delta-encoded node id numbers. 00000040 02 02 02 04 02 04 02 02 04 02 02 02 02 02 02 02 |................| 00000050 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 |................| 00000060 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 04 |................| 00000070 02 02 06 44 02 02 02 02 02 02 02 02 02 02 02 02 |...D............| 00000080 68 02 02 02 02 02 02 02 02 02 02 02 04 02 02 06 |h...............| 00000090 02 02 0c 02 02 02 0a 02 02 02 02 06 0c 06 02 04 |................| 000000a0 02 06 02 02 02 02 02 02 02 02 02 02 02 02 02 02 |................| 000000b0 02 02 02 02 02 02 02 02 04 04 02 06 04 04 10 02 |................| 000000c0 04 02 04 18 0a 02 02 02 02 02 02 02 02 02 02 02 |................| 000000d0 04 06 02 02 04 02 02 02 02 04 02 02 02 02 08 02 |................| 000000e0 02 02 02 02 02 02 02 02 02 02 02 cc 06 02 02 02 |................| 000000f0 02 02 02 02 02 02 02 02 04 02 02 02 02 02 02 02 |................| 00000100 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 |................| 00000110 02 02 02 02 02 02 02 02 36 02 02 04 04 04 02 02 |........6.......| 00000120 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 |................| etc. <--- decompressed ---
Of course, the protocol buffer library handles all of these low-level encoding details.
The code
The codebase is split into two pieces. Common code that is application-independent exists on github. This includes the protocol buffer definitions, and Java code that operates at the fileblock level. http://github.com/scrosby/OSM-binary
Unfortunately, osmosis, mkgmap, and the splitter each use a different internal representation for OSM entities. This means that I have to reimplement the entity serialization and parsing code for each program and there is less common code between the implementations than I would like. The serializer and deserializer for osmosis are in trunk.
A deserializer for an older version of the mkgmap splitter (circa 5/2010) is available on github at: http://github.com/scrosby/OSM-splitter
Miscellaneous notes
Downloads
Geofabrik.de offers OSM extracts in protobuf binary format for many regions.
See also
- Tutorial how to parse PBF using Python [3]
- Protocol Buffers at Wikipedia