Pt:Formato PBF

From OpenStreetMap Wiki
Jump to navigation Jump to search

O formato PBF ("Protocolbuffer Binary Format") tem por finalidade, principalmente, ser uma alternativa para o formato XML. Possui cerca da metade do tamanho de um planet gzip(ado) e é cerca de 30% menor que um planet bzip(ado). Também é cerca de cinco vezes mais rápido para escrever e seis vezes mais rápido para ler que um planet gzip(ado). O formato foi projetado para permitir extensibilidade e flexibilidade futuras.

O formato do arquivo subjacente foi escolhido para permitir acesso aleatório com a granularidade de 'bloco-de-arquivo' (fileblock). Cada bloco-de-arquivo é decodificável independentemente e contém aproximadamente oito mil entidades OSM na configuração padrão. Não há marcador com codificação rígida usado; todas as chaves e valores são armazenados em sua totalidade como strings opacas. Para escalabilidade futura, são assumidos identificadores de nó/caminho/relação de 64 bits. O serializador atual (Osmosis) preserva a ordem das entidades OSM, e os marcadores nas entidades OSM. Para lidar de maneira flexível com várias resoluções, a granularidade ou resolução usada para representar localizações e registros de tempo é ajustável em múltiplos de 1 milissegundo e 1 nanograu. O fator de escala padrão é de 1000 milissegundos e 100 nanograus, correspondendo a cerca de 1 cm no equador. Estes são os valores da resolução atual do banco de dados do OSM.

Os arquivo possuem a extensão *.osm.pbf.

No momento, a implementação de referência do PBF é a implementação do Osmosis, dividida em duas partes, a parte específica do osmosis, presente no repositório Osmosis em [1], e uma parte genérica para aplicativos em [2]. Esta parte genérica para aplicativos é utilizada para construir o osmpbf.jar (conforme usada no osmosis e outros leitores PBF baseados em Java) e, também, contém a definição principal das definições de buffer do protocolo dos arquivos PBF (*.proto).

Suporte de software para o PBF

Muitos softwares usados no projeto OSM já suportam o PBF, além do formato XML original, além de várias ferramentas para converter de PBF para XML OSM e vice-versa.

Veja PBF/Conformidade de software para obter detalhes sobre quais tipos de arquivos PBF são suportados pelos vários programas.

Projeto

Codificação de baixo nível

O Protocol Buffers da Google é utilizado para o armazenamento de baixo nível. Dado um arquivo de especificação de uma ou mais mensagens, o compilador do Protocol Buffers escreve um código de serialização de baixo nível. As mensagens podem conter outras mensagens, formando estruturas hierárquicas. O Protocol Buffers também permite extensibilidade; novos campos podem ser adicionados a uma mensagem e clientes antigos podem ler essas mensagens sem recompilar. Para obter mais detalhes, por favor, veja http://code.google.com/p/protobuf/ ou leia o leia o artigo no Google Opensource Blog. A Google suporta oficialmente C++, Java e Python, mas existem compiladores para outras linguagens. Um exemplo de especificação de mensagem é:

message Node {
  required sint64 id = 1;
  // Parallel arrays.
  repeated uint32 keys = 2 [packed = true]; // String IDs.
  repeated uint32 vals = 3 [packed = true]; // String IDs.
  optional Info info = 4; // May be omitted in omitmeta
  required sint64 lat = 8;
  required sint64 lon = 9;
}

O Protocol Buffers usa uma codificação de bit variável para inteiros. Um inteiro é codificado em 7 bits por byte, onde o bit alto indica se o próximo byte deve ou não ser lido. Quando as mensagens contêm inteiros pequenos, o tamanho do arquivo é minimizado. Existem duas codificações, uma destinada principalmente a inteiros positivos e uma a inteiros com sinal. Na codificação padrão, inteiros de [0,127] requerem um byte, de [128,16383] requerem dois bytes, etc. Na codificação de número com sinal, o bit de sinal é colocado na posição menos significativa; números de [-64,63] requerem um byte, de [-8192,8191] requerem dois bytes, e assim por diante. Para obter mais detalhes sobre o formato serializado de mensagens de Protocol Buffers, consulte o respectivo site.

Os arquivos gerados usam um pacote Java de crosby.binary. Em outras linguagens, os arquivos gerados estão no pacote OSMPBF.

Formato do arquivo

O arquivo contém um cabeçalho seguido por uma sequencia de blocos de arquivo. O projeto tem por intenção permitir, no futuro, acesso aleatório ao conteúdo do arquivo e ignorar dados não compreendidos ou indesejados.

O formato é uma sequência repetida de:

  • int4: comprimento da mensagem BlobHeader na ordem de bytes da rede
  • mensagem BlobHeader serializada
  • mensagem Blob serializada (o tamanho está no cabeçalho)

O BlobHeader é atualmente definido como:

message BlobHeader {
   required string type = 1;
   optional bytes indexdata = 2;
   required int32 datasize = 3;
 }
  • type contém o tipo de dados nesta mensagem.
  • indexdata é algum blob arbitrário que pode incluir metadados sobre o blob seguinte, (por exemplo, para dados do OSM, pode conter uma caixa delimitadora.) Este é um esboço destinado a permitir projetos futuros de arquivos * .osm.pbf indexados.
  • datasize contém o tamanho serializado da mensagem Blob subsequente.

(Por favor, observe que o BlobHeader costumava ser chamado de BlockHeader. Teve o nome mudado na v1.1 para evitar confusão com HeaderBlock, abaixo)

O Blob é utilizado atualmente para armazenar um blob de dados arbitrário, sem compressão ou no formato comprimido zlib/deflate.

message Blob {
  optional bytes raw = 1; // Sem compressão
  optional int32 raw_size = 2; // Quando comprimido, o tamanho não comprimido

  // Possíveis versões compactadas dos dados.
  optional bytes zlib_data = 3;

  // funcionalidade PROPOSTA para dados comprimidos LZMA. O SUPORTE NÃO É NECESSÁRIO.
  optional bytes lzma_data = 4;

  // Anteriormente usado para dados comprimidos com bzip2. Descontinuado em 2010.
  optional bytes OBSOLETE_bzip2_data = 5 [deprecated=true]; // Não reutilize este número de marcador.
}

NOTA (12/2010): Nenhum codificador suporta atualmente lzma ou bzip2. Para simplificar as implementações do decodificador, o bzip2 foi descontinuado e o LZMA foi relegado a uma extensão proposta.

A fim de detectar de forma robusta arquivos ilegais ou corruptos, limito o tamanho máximo das mensagens BlobHeader e Blob. O comprimento do BlobHeader deve ser menor que 32 KiB (32*1024 bytes) e deve ser menor que 64 KiB. O comprimento não comprimido de um Blob deve ser menor que 16 MiB (16*1024*1024 bytes) e precisa ser menor que 32 MiB.

Codificando entidades do OSM nos blocos de arquivo

Existem atualmente dois tipos de bloco de arquivo para dados do OSM. Essas strings de tipo textual são armazenadas no campo tipo no BlobHeader:

  • OSMHeader: O Blob contém uma mensagem HeaderBlock serializada (veja osmformat.proto). Todo bloco de arquivo deve ter um destes blocos antes do primeiro bloco 'OSMData'.
  • OSMData: Contém uma mensagem PrimitiveBlock serializada. (veja osmformat.proto). Contém as entidades.

Este projeto permite que outros softwares estendam o formato para incluir blocos de arquivo de tipos adicionais para as suas próprias finalidades. Os analisadores devem ignorar e saltar blocos de arquivo que não reconheçam.

Definição dos blocos de arquivo do OSMHeader

message HeaderBlock {
  optional HeaderBBox bbox = 1;
  /* Marcadores adicionais para ajudar na análise deste conjunto de dados */
  repeated string required_features = 4;
  repeated string optional_features = 5;

  optional string writingprogram = 16;
  optional string source = 17; // Do campo bbox.

  /* Marcadores que permitem a continuação de uma replicação do Osmosis */

  // replicação do registro de tempo, expressa em segundos desde
  // a era (Unix), senão o mesmo valor do campo "timestamp=..."
  // no arquivo state.txt utilizado pelo Osmosis
  optional int64 osmosis_replication_timestamp = 32;

  // número sequencial da replicação (sequenceNumber no state.txt)
  optional int64 osmosis_replication_sequence_number = 33;

  // URL base da replicação (do arquivo configuration.txt do Osmosis)
  optional string osmosis_replication_base_url = 34;
}

Para oferecer compatibilidade com versões anteriores e posteriores, o analisador precisa saber se pode analisar o arquivo. Esta é uma das funcionalidades requeridas. Se o arquivo contém uma funcionalidade requerida que o analisador NÃO entende, deve rejeitar o arquivo emitindo um código de erro, e informar quais são as funcionalidades requeridas que não dá suporte.

Atualmente estão definidas as seguintes funcionalidades:

  • "OsmSchema-V0.6" — O arquivo contém dados do esquema OSM v0.6.
  • "DenseNodes" — O arquivo contém nós densos e informações densas.
  • "HistoricalInformation" — Arquivo contém dados históricos do OSM.

Além disso, um arquivo pode ter propriedades opcionais que o analisador pode tirar partido. Por exemplo, o arquivo pode estar pré-classificado e não precisar de ordenação antes de ser utilizado. Ou, os caminhos no arquivo podem possuir caixas limitadoras pré-calculadas. Se o programa encontrar uma funcionalidade que não conhece, pode assim mesmo continuar a ler o arquivo com segurança. Se o programa espera por uma funcionalidade opcional que não está presente, pode terminar com erro. As seguintes funcionalidades foram propostas:

  • "Has_Metadata" – O arquivo possui metadados de autor e registro de tempo.
  • "Sort.Type_then_ID" – As entidades são ordenadas pelo tipo e, depois, pelo ID.
  • "Sort.Geographic" – As entidades estão em alguma forma de ordenação geométrica (atualmente não utilizado)
  • "timestamp=2011-10-16T15:45:00Z" – Solução provisória para armazenar um registro de tempo do arquivo. Por favor, use osmosis_replication_timestamp em seu lugar.

Para que servem os campos de replicação?

Os campos osmosis_replication_* têm por finalidade permitir aos usuários dos arquivos PBF anexarem dados de um servidor de atualização gerenciado pelo Osmosis, a fim de manter o arquivo atualizado. Osmosis é o software utilizado para produzir diffs diários, horários e de minuto no site http://planet.openstreetmap.org. Para juntar atualizações a um arquivo PBF, deve-se saber qual é o estado de replicação que o arquivo representa, para que o ponto de sincronização correto possa ser encontrado.

  • osmosis_replication_timestamp - o registro de tempo da replicação (como valor de era do Unix), tirado do arquivo state.txt escrito pelo Osmosis (onde não está gravado como um valor de era do Unix, mas como uma cadeia de caracteres ISO de tempo). Tecnicamente é o registro de tempo interno do banco de dados da última transação inteiramente contida no arquivo; isto não significa, necessariamente, que cada objeto com com um registro de tempo menor ou igual a este registro de tempo esteja contido no arquivo.
  • osmosis_replication_sequence_number - o número sequencial da última transação do banco de dados presente no arquivo. Geralmente corresponde ao registro de tempo - se você conhece um pode obter o outro, entretanto torna as coisas mais fáceis para o usuário conhecer ambos.
  • osmosis_replication_base_url - o URL base para os diffs de replicação como, por exemplo, https://planet.openstreetmap.org/replication/minute/, para que o usuário saiba qual servidor (e, portanto, qual banco de dados) o IDs está relacionado.

Ao processar o arquivo PBF, você normalmente mantém esses campos intactos (por exemplo, copia da entrada para a saída) da mesma maneira como você copiaria um bloco bbox, a menos que o tipo particular de processamento que você aplica torne impossível, ou inútil, querer aplicar atualizações posteriores ao arquivo.

Definição do bloco de arquivo OSMData

Para codificar as entidades OSM em protocol buffers, são coletadas 8k entidades para formar um PrimitiveBlock, o qual é serializado na porção Blob de um bloco de arquivo OsmData.

message PrimitiveBlock {
  required StringTable stringtable = 1;
  repeated PrimitiveGroup primitivegroup = 2;

  // Granularidade, unidades de nanograus, usada para armazenar as coordenanadas neste bloco
  optional int32 granularity = 17 [default=100]; 

  // Valor de deslocamento entre as coordenadas de saída  e a grade de granularidade, em unidades de nanograus.
  optional int64 lat_offset = 19 [default=0];
  optional int64 lon_offset = 20 [default=0]; 

  // Granularidade das datas, representada normalmente em unidades de millisegundos desde a era 1970.
  optional int32 date_granularity = 18 [default=1000]; 


  // Extensão proposta
  //optional BBox bbox = XX;
}

Ao se criar um arquivo PBF, é necessário extrair todas as strings (chave, valor, função, usuário) em uma tabela de strings separada. Depois disso, serão referenciadas por seu índice nesta tabela, exceto que index=0 é usado como delimitador ao se codificar DenseNodes. Isto significa que você não pode armazenar com segurança uma string nesta posição. Portanto, uma string vazia é armazenada em index=0 e esta posição nunca é utilizada. Não é necessário, mas pode ter um efeito positivo no desempenho, se as strings na tabela forem classificadas de tal forma que as strings utilizadas com maior frequência tenham índices menores. Também é possível melhorar a compressibilidade da deflação da tabela de strings, se as strings que possuírem a mesma frequência estiverem classificadas por ordem lexicográfica.

Cada PrimitiveBlock é descomprimível independentemente, contendo toda a informação necessária para descomprimir as entidades contidas no mesmo. Contém a tabela de strings e, também, codifica a granularidade, tanto para a posição quanto para o registro de tempo.

Um bloco pode conter qualquer número de entidades, desde que os limites de tamanho de bloco sejam obedecidos. Vai resultar em tamanhos de arquivo menor, se forem colocadas tantas entidades quanto for possível em cada bloco. Entretanto, por simplicidade, certos programas (por exemplo, osmosis 0.38) limitam o número de entidades em cada bloco a 8.000 ao escrever no formato PBF.

Adicionalmente à granularidade, o bloco primitivo também codifica o valor de deslocamento da latitude e da longitude. Estes valores, medidos em nanograus, devem ser adicionados a cada coordenada.

latitude = .000000001 * (lat_offset + (granularidade * lat))
longitude = .000000001 * (lon_offset + (granularidade * lon))

onde latitude é a latitude em graus, granularidade é a granularidade dada no PrimitiveBlock, lat_offset é o deslocamento dado no PrimitiveBlock, e lat/lon é codificado no Node, ou delta-codificado no DenseNode. A explicação para a equação da longitude é análoga.

O motivo da existência de lat_offset e lon_offset é representar de forma concisa os dados das isolinhas (linhas de contorno), ou outros dados que aparecem em uma grade regular. Digamos que se deseja representar dados que estavam numa grade de 100 micrograus. Gostaríamos de usar uma granularidade de 100.000 nanograus para a compressão mais alta, exceto que poderíamos apenas representar pontos na forma (.0001*x,.0001*y), quando os dados reais da grade podem estar na forma (.00003345+.0001*x, .00008634+.0001*y). Utilizando lat_offset=3345 e lon_offset=8634, podemos representar esta grade de 100 micrograus de forma exata.

Para registros de tempo,

millisec_stamp = timestamp*date_granularity.

Onde timestamp é o registro de tempo codificado em um Info ou delta codificado no DenseInfo, date_granularity é dado em PrimitiveBlock, e millisec_stamp é a data da entidade, medida em número de milissegundos desde a era 1970. Para obter a data medida em segundos desde a era 1970, divida millisec_stamp por 1000.

Dentro de cada bloco primitivo, então divide-se as entidades em grupos que contém mensagens consecutivas do mesmo tipo (nó/caminho/relação).

message PrimitiveGroup {
  repeated Node     nodes = 1;
  optional DenseNodes dense = 2;
  repeated Way      ways = 3;
  repeated Relation relations = 4;
  repeated ChangeSet changesets = 5;
}

Um PrimitiveGroup NUNCA DEVE conter objetos de tipos diferentes. Então, ou contém muitas mensagens de Node, ou uma mensagem de DenseNode, ou muitas mensagens de Way, ou muitas mensagens de Relation, ou muitas mensagens de ChangeSet. Mas não pode conter qualquer mistura destes. O motivo é que da forma como a codificação do Protocol Buffer funciona, seria impossível obter os objetos na mesma ordem em que foram escritos no arquivo. Isso pode bastante confuso para os usuários.

Após ser serializado em uma string, cada bloco primitivo é, opcionalmente, comprimido com gzip/deflate individualmente quando armazenado no bloco de arquivo Blob.

Caminhos e Relações

Para caminhos e relações que contém os identificadores de outros nós no campo refs, é explorada a tendencia dos nós consecutivos de um caminho, ou relação, possuírem identificadores de nós próximos usando a compressão delta, resultando em inteiros pequenos. (ou seja, em vez de codificar x_1, x_2, x_3, codifica-se x_1, x_2-x_1, x_3-x_2, ...). Exceto neste caso, os caminhos e relações são codificados, principalmente, da maneira como se espera. Os marcadores (tags) são codificados em dois arrays paralelos, um array de tstring-IDs para as chaves, e outro de string-IDs para os valores.

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
}

As relações usam um enumerador para representar os tipos dos membros.

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];
}

Os metadados incluem informações não geográficas sobre o objeto, tais como:

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

   // O sinalizador 'visible' é usado para armazenar informações
   // de história. Indica que a versão do objeto atual foi criada
   // por uma operação de exclusão na API do OSM.
   // Quando este sinalizador é marcado na escrita, DEVE ser
   // adicionado o marcador 'required_features' com o valor
   // "HistoricalInformation" no HeaderBlock.
   // Se o sinalizador não estiver presente em algum objeto,
   // DEVE ser assumido como sendo verdadeiro, se o arquivo
   // possuir o marcador 'required_features' definido como
   // "HistoricalInformation"
   optional bool visible = 6;
}

Nós

Os nós podem ser codificados de duas maneiras, como um Node (definido acima) e como um formato denso especial. No formato denso, é armazenado o grupo 'columnwise' como um array de IDs, array de latitudes e array de longitudes. cada coluna é delta-codificada. Isto reduz o excesso de carga no cabeçalho e permite a delta-codificação funcionar muito efetivamente.

Chaves e valores para todos os nós são codificados como um array simples de stringIDs. Os marcadores de cada nó são codificados em <keyid> <valid> alternados. É utilizado um único stringid de 0 como delimitador, quando o marcador de um nó termina e os marcadores do próximo nó começam. O padrão de armazenamento é: ((<keyid> <valid>)* '0' )* Como exceção, se nenhum nó do bloco corrente possuir algum par de chave/valor, este array não contém qualquer delimitadors, mas é simplesmente vazio.

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

   // Empacotamento especial de chaves e valores em um array.
   // Pode estar vazio se nenhum nó do bloco tiver marcador.
   repeated int32 keys_vals = 10 [packed = true]; 
}

DenseInfo realiza uma codificação delta similar nos metadados.

message DenseInfo {
   repeated int32 version = 1 [packed = true]; 
   repeated sint64 timestamp = 2 [packed = true]; // DELTA coded
   repeated sint64 changeset = 3 [packed = true]; // DELTA coded
   repeated sint32 uid = 4 [packed = true]; // DELTA coded
   repeated sint32 user_sid = 5 [packed = true]; // String IDs for usernames. DELTA coded

   // O sinalizador 'visible' é usado para armazenar informações
   // de história. Indica que a versão do objeto atual foi criada
   // por uma operação de exclusão na API do OSM.
   // Quando este sinalizador é marcado na escrita, DEVE ser
   // adicionado o marcador 'required_features' com o valor
   // "HistoricalInformation" no HeaderBlock.
   // Se o sinalizador não estiver presente em algum objeto,
   // DEVE ser assumido como sendo verdadeiro, se o arquivo
   // possuir o marcador 'required_features' definido como
   // "HistoricalInformation"
   repeated bool visible = 6 [packed = true];
}

Exemplo do formato

A seguir, vamos dar uma olhada nos bytes de um arquivo PBF do OSM. O pequeno extrato regional bremen.osm.pbf (geofabrik.de, 2011-01-13) é utilizado como exemplo.

Todos os dados são precedidos por um identificador de variável. Este identificador consiste de tipo e id; os bits de 0 a 2 representam o tipo, os bits 3 e acima o id. Esses tipos podem ser usados:

  • 0: V (Varint) int32, int64, uint32, uint64, sint32, sint64, bool, enum
  • 1: D (64-bit) fixed64, sfixed64, double
  • 2: S (comprimento-delimitado) string, bytes, mensagens incorporadas, campos repetidos empacotados
  • 5: I (32-bit) fixed32, sfixed32, float
00000000  00 00 00 0d - comprimento em bytes do BlobHeader na ordem de byte da rede
00000000  __ __ __ __ 0a - S 1 'tipo'
00000000  __ __ __ __ __ 09 - comprimento 9 bytes
00000000  __ __ __ __ __ __ 4f 53  4d 48 65 61 64 65 72 - "OSMHeader"
00000000  __ __ __ __ __ __ __ __  __ __ __ __ __ __ __ 18 - V 3 'datasize'
00000010  7c - 124 bytes de comprimento
00000010  __ 10 - V 2 'raw_size'
00000010  __ __ 71 - 113 bytes de comprimento
00000010  __ __ __ 1a - S 3 'zlib_data'
00000010  __ __ __ __ 78 - comprimento de 120 bytes

--- seção comprimida:
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
--- descomprimido --->
00000000  0a - S 1 'bbox'
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 - comprimento de 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 - comprimento de 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 - comprimento de 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 - "https://www.openstreetmap.org/api/0.6"
<--- descomprimido ---

00000080  __ __ __ __ __ __ __ __  __ __ __ __ __ 00 00 00
00000090  0d - comprimento em bytes do BlobHeader na ordem de bytes da rede
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 de comprimento
00000090  __ __ __ __ __ __ __ __  __ __ __ __ __ __ 10 - V 2 'raw_size'
00000090  __ __ __ __ __ __ __ __  __ __ __ __ __ __ __ 8f
000000a0  84 08 - 131599 bytes de comprimento
000000a0  __ __ 1a - S 3 'zlib_data'
000000a0  __ __ __ 88 af 05 - comprimento de 87944 bytes

--- sessão comprimida:
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.
--- descomprimido --->
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.
--- parte descomprimida do deslocamento 5975 --->
00000000  12 - S 2 'primitivegroup'
00000000  __ ad d5 07 - length 125613 bytes
00000000  __ __ __ __ 12 - S 2 -- Tag #2 em um 'PrimitiveGroup', contendo DenseNodes serializado.
00000000  __ __ __ __ __ a9 d5 07 - of length 125609 bytes
00000000  __ __ __ __ __ __ __ __  0a - S 1 - Tag #1 in a DenseNodes, que é um array de varints empacotado. 
00000000  __ __ __ __ __ __ __ __  __ df 42 - de comprimento 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  |................|

    Cada varint é armazenado consecutivamente. 
    É processado até que tenhamos lido o valor de 8543 bytes e, em seguida, voltamos a analisar o DenseNodes. 
    Os varints são números de identificação de nó delta-codificados.

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.
<--- descomprimido ---

É claro que a biblioteca do Protocol Buffer trata de todos estes detalhes de codificação de baixo nível.

O código

O código base está dividido em duas partes. O código comum, que é independente de aplicativo, está no GitHub. Isto inclui as definições do Protocol Buffer e o código Java que opera no nível de bloco de arquivo. http://github.com/scrosby/OSM-binary

Infelizmente, osmosis, mkgmap, e o splitter cada um deles utiliza uma representação interna diferente para as entidades do OSM. Isso significa que é necessário reimplementar o código de serialização e análise de entidade para cada programa e há menos código comum entre as implementações do que se gostaria. O serializador e o desserializador para o osmosis estão no tronco.

Um desserializador para uma versão mais antiga do mkgmap splitter (por volta de 5/2010) está disponível no github em: http://github.com/scrosby/OSM-splitter

Notas variadas

Downloads

O osm planet completo (estado atual, sem histórico) no formato pbf está disponível aqui: https://planet.openstreetmap.org/pbf/.

Outros locais para fazer o download de extrações do OSM no formato PBF: Planet.osm

Veja também

  • PBF Perl Parser
  • OSMCompiler
  • Tutorial de como analisar o PBF usando Python [3]
  • Protocol Buffers no Wikipedia
  • Osmium Framework C++/Javascript para trabalhar com os arquivos OSM
  • Imposm Biblioteca Python que analisa dados do OpenStreetMap em formato XML e PBF
  • libosmpbfreader Uma biblioteca C ++ simples para ler arquivos binários do OpenStreetMap
  • osm-read Biblioteca node.js para analisar os dados do OpenStreetMap data nos formatos XML e PBF
  • pbf_parser Uma Ruby gem para analisar arquivos PBF facilmente
  • osmpbf2sqlite Conversão de osm pbf para o banco de dados sqlite.
  • osm4scala Biblioteca de alto desempenho para iterar sobre o elemento osm a partir de um arquivo pbf.

Referências externas