2011-01-11 20 views
6

Por mi vida no puedo deserializar el archivo protobuf de Open Street Maps.Protobuf-net Deserialize Open Street Maps

Estoy tratando de deserializar el siguiente extracto: http://download.geofabrik.de/osm/north-america/us-northeast.osm.pbf para obtener nodos y estoy usando http://code.google.com/p/protobuf-net/ como la biblioteca. Intenté deserializar un montón de objetos diferentes, pero todos son nulos.

Los archivos proto se pueden encontrar aquí: http://trac.openstreetmap.org/browser/applications/utils/export/osm2pgsql/protobuf

¿Alguna sugerencia?

+0

Soy el autor de protobuf-net; Estoy en el momento de "trabajo" en este momento, pero trataré de verlo más tarde hoy, para ver cuál es el problema –

+0

Sé quién eres Marc, descargué tu software. Me gusta el trabajo entre paréntesis jaja. Gracias por su ayuda (y el marco)! – jonperl

Respuesta

8

Derecha; el problema es que esto no es simplemente protobuf - es un formato de archivo híbrida (defined here que incluye protobuf entre varios formatos internamente También incorpora compresión (aunque eso parece ser opcional)

I'.. he separado lo que pude de la especificación, y tengo un lector de C# aquí que usa protobuf-net para manejar los fragmentos, felizmente lee ese archivo hasta el final, puedo decir que hay 4515 bloques (BlockHeader) Cuando se trata del Blob, estoy un poco confundido sobre cómo las especificaciones marcan OSMHeader y OSMData - ¡Estoy abierto a sugerencias aquí! También he usado ZLIB.NET para manejar la compresión zlib que se está utilizando. En ausencia de Entendiendo esto, me he conformado con procesar los datos de ZLIB y validarlos contra el tamaño reclamado, para verificar que estén al menos sanos.

Si puede averiguar (o preguntarle al autor) cómo están separando OSMHeader y OSMData, con mucho gusto le pondré otra cosa. Espero que no le importe que me haya detenido aquí, pero ha sido un unas horas; p

using System; 
using System.IO; 
using OpenStreetMap; // where my .proto-generated entities are living 
using ProtoBuf; // protobuf-net 
using zlib; // ZLIB.NET  

class OpenStreetMapParser 
{ 

    static void Main() 
    { 
     using (var file = File.OpenRead("us-northeast.osm.pbf")) 
     { 
      // from http://wiki.openstreetmap.org/wiki/ProtocolBufBinary: 
      //A file contains a header followed by a sequence of fileblocks. The design is intended to allow future random-access to the contents of the file and skipping past not-understood or unwanted data. 
      //The format is a repeating sequence of: 
      //int4: length of the BlockHeader message in network byte order 
      //serialized BlockHeader message 
      //serialized Blob message (size is given in the header) 

      int length, blockCount = 0; 
      while (Serializer.TryReadLengthPrefix(file, PrefixStyle.Fixed32, out length)) 
      { 
       // I'm just being lazy and re-using something "close enough" here 
       // note that v2 has a big-endian option, but Fixed32 assumes little-endian - we 
       // actually need the other way around (network byte order): 
       uint len = (uint)length; 
       len = ((len & 0xFF) << 24) | ((len & 0xFF00) << 8) | ((len & 0xFF0000) >> 8) | ((len & 0xFF000000) >> 24); 
       length = (int)len; 

       BlockHeader header; 
       // again, v2 has capped-streams built in, but I'm deliberately 
       // limiting myself to v1 features 
       using (var tmp = new LimitedStream(file, length)) 
       { 
        header = Serializer.Deserialize<BlockHeader>(tmp); 
       } 
       Blob blob; 
       using (var tmp = new LimitedStream(file, header.datasize)) 
       { 
        blob = Serializer.Deserialize<Blob>(tmp); 
       } 
       if(blob.zlib_data == null) throw new NotSupportedException("I'm only handling zlib here!"); 

       using(var ms = new MemoryStream(blob.zlib_data)) 
       using(var zlib = new ZLibStream(ms)) 
       { // at this point I'm very unclear how the OSMHeader and OSMData are packed - it isn't clear 
        // read this to the end, to check we can parse the zlib 
        int payloadLen = 0; 
        while (zlib.ReadByte() >= 0) payloadLen++; 
        if (payloadLen != blob.raw_size) throw new FormatException("Screwed that up..."); 
       } 
       blockCount++; 
       Console.WriteLine("Read block " + blockCount.ToString()); 


      } 
      Console.WriteLine("all done"); 
      Console.ReadLine(); 
     } 
    } 
} 
abstract class InputStream : Stream 
{ 
    protected abstract int ReadNextBlock(byte[] buffer, int offset, int count); 
    public sealed override int Read(byte[] buffer, int offset, int count) 
    { 
     int bytesRead, totalRead = 0; 
     while (count > 0 && (bytesRead = ReadNextBlock(buffer, offset, count)) > 0) 
     { 
      count -= bytesRead; 
      offset += bytesRead; 
      totalRead += bytesRead; 
      pos += bytesRead; 
     } 
     return totalRead; 
    } 
    long pos; 
    public override void Write(byte[] buffer, int offset, int count) 
    { 
     throw new NotImplementedException(); 
    } 
    public override void SetLength(long value) 
    { 
     throw new NotImplementedException(); 
    } 
    public override long Position 
    { 
     get 
     { 
      return pos; 
     } 
     set 
     { 
      if (pos != value) throw new NotImplementedException(); 
     } 
    } 
    public override long Length 
    { 
     get { throw new NotImplementedException(); } 
    } 
    public override void Flush() 
    { 
     throw new NotImplementedException(); 
    } 
    public override bool CanWrite 
    { 
     get { return false; } 
    } 
    public override bool CanRead 
    { 
     get { return true; } 
    } 
    public override bool CanSeek 
    { 
     get { return false; } 
    } 
    public override long Seek(long offset, SeekOrigin origin) 
    { 
     throw new NotImplementedException(); 
    } 
} 
class ZLibStream : InputStream 
{ // uses ZLIB.NET: http://www.componentace.com/download/download.php?editionid=25 
    private ZInputStream reader; // seriously, why isn't this a stream? 
    public ZLibStream(Stream stream) 
    { 
     reader = new ZInputStream(stream); 
    } 
    public override void Close() 
    { 
     reader.Close(); 
     base.Close(); 
    } 
    protected override int ReadNextBlock(byte[] buffer, int offset, int count) 
    { 
     // OMG! reader.Read is the base-stream, reader.read is decompressed! yeuch 
     return reader.read(buffer, offset, count); 
    } 

} 
// deliberately doesn't dispose the base-stream  
class LimitedStream : InputStream 
{ 
    private Stream stream; 
    private long remaining; 
    public LimitedStream(Stream stream, long length) 
    { 
     if (length < 0) throw new ArgumentOutOfRangeException("length"); 
     if (stream == null) throw new ArgumentNullException("stream"); 
     if (!stream.CanRead) throw new ArgumentException("stream"); 
     this.stream = stream; 
     this.remaining = length; 
    } 
    protected override int ReadNextBlock(byte[] buffer, int offset, int count) 
    { 
     if(count > remaining) count = (int)remaining; 
     int bytesRead = stream.Read(buffer, offset, count); 
     if (bytesRead > 0) remaining -= bytesRead; 
     return bytesRead; 
    } 
} 
+0

Esto es absolutamente maravilloso. ¡Gracias por la ventaja, voy a ver qué puedo obtener! (Tu eres el hombre). – jonperl

+0

Voy a intentar retroceder desde https://github.com/scrosby/OSM-binary/tree/master/src.java/crosby/binary – jonperl

0

¿Has intentado obtener un área más pequeña? como us-pacific.osm.pbf

Eventualmente sería útil publicar los mensajes de error.

+0

Aún aparece nulo.Intenté var f = Serializer.Deserialize (archivo); – jonperl

1

Después de la configuración contorno de Mark me di cuenta de la última parte examinado http://git.openstreetmap.nl/index.cgi/pbf2osm.git/tree/src/main.c?h=35116112eb0066c7729a963b292faa608ddc8ad7

Aquí está el código final.

using System; 
using System.Diagnostics; 
using System.IO; 
using crosby.binary; 
using OSMPBF; 
using PerlLLC.Tools; 
using ProtoBuf; 
using zlib; 

namespace OpenStreetMapOperations 
{ 
    class OpenStreetMapParser 
    { 
     static void Main() 
     { 
      using (var file = File.OpenRead(StaticTools.AssemblyDirectory + @"\us-pacific.osm.pbf")) 
      { 
       // from http://wiki.openstreetmap.org/wiki/ProtocolBufBinary: 
       //A file contains a header followed by a sequence of fileblocks. The design is intended to allow future random-access to the contents of the file and skipping past not-understood or unwanted data. 
       //The format is a repeating sequence of: 
       //int4: length of the BlockHeader message in network byte order 
       //serialized BlockHeader message 
       //serialized Blob message (size is given in the header) 

       int length, blockCount = 0; 
       while (Serializer.TryReadLengthPrefix(file, PrefixStyle.Fixed32, out length)) 
       { 
        // I'm just being lazy and re-using something "close enough" here 
        // note that v2 has a big-endian option, but Fixed32 assumes little-endian - we 
        // actually need the other way around (network byte order): 
        length = IntLittleEndianToBigEndian((uint)length); 

        BlockHeader header; 
        // again, v2 has capped-streams built in, but I'm deliberately 
        // limiting myself to v1 features 
        using (var tmp = new LimitedStream(file, length)) 
        { 
         header = Serializer.Deserialize<BlockHeader>(tmp); 
        } 
        Blob blob; 
        using (var tmp = new LimitedStream(file, header.datasize)) 
        { 
         blob = Serializer.Deserialize<Blob>(tmp); 
        } 
        if (blob.zlib_data == null) throw new NotSupportedException("I'm only handling zlib here!"); 

        HeaderBlock headerBlock; 
        PrimitiveBlock primitiveBlock; 

        using (var ms = new MemoryStream(blob.zlib_data)) 
        using (var zlib = new ZLibStream(ms)) 
        { 
         if (header.type == "OSMHeader") 
          headerBlock = Serializer.Deserialize<HeaderBlock>(zlib); 

         if (header.type == "OSMData") 
          primitiveBlock = Serializer.Deserialize<PrimitiveBlock>(zlib); 
        } 
        blockCount++; 
        Trace.WriteLine("Read block " + blockCount.ToString()); 


       } 
       Trace.WriteLine("all done"); 
      } 
     } 

     // 4-byte number 
     static int IntLittleEndianToBigEndian(uint i) 
     { 
      return (int)(((i & 0xff) << 24) + ((i & 0xff00) << 8) + ((i & 0xff0000) >> 8) + ((i >> 24) & 0xff)); 
     } 
    } 

    abstract class InputStream : Stream 
    { 
     protected abstract int ReadNextBlock(byte[] buffer, int offset, int count); 
     public sealed override int Read(byte[] buffer, int offset, int count) 
     { 
      int bytesRead, totalRead = 0; 
      while (count > 0 && (bytesRead = ReadNextBlock(buffer, offset, count)) > 0) 
      { 
       count -= bytesRead; 
       offset += bytesRead; 
       totalRead += bytesRead; 
       pos += bytesRead; 
      } 
      return totalRead; 
     } 
     long pos; 
     public override void Write(byte[] buffer, int offset, int count) 
     { 
      throw new NotImplementedException(); 
     } 
     public override void SetLength(long value) 
     { 
      throw new NotImplementedException(); 
     } 
     public override long Position 
     { 
      get 
      { 
       return pos; 
      } 
      set 
      { 
       if (pos != value) throw new NotImplementedException(); 
      } 
     } 
     public override long Length 
     { 
      get { throw new NotImplementedException(); } 
     } 
     public override void Flush() 
     { 
      throw new NotImplementedException(); 
     } 
     public override bool CanWrite 
     { 
      get { return false; } 
     } 
     public override bool CanRead 
     { 
      get { return true; } 
     } 
     public override bool CanSeek 
     { 
      get { return false; } 
     } 
     public override long Seek(long offset, SeekOrigin origin) 
     { 
      throw new NotImplementedException(); 
     } 
    } 
    class ZLibStream : InputStream 
    { // uses ZLIB.NET: http://www.componentace.com/download/download.php?editionid=25 
     private ZInputStream reader; // seriously, why isn't this a stream? 
     public ZLibStream(Stream stream) 
     { 
      reader = new ZInputStream(stream); 
     } 
     public override void Close() 
     { 
      reader.Close(); 
      base.Close(); 
     } 
     protected override int ReadNextBlock(byte[] buffer, int offset, int count) 
     { 
      // OMG! reader.Read is the base-stream, reader.read is decompressed! yeuch 
      return reader.read(buffer, offset, count); 
     } 

    } 
    // deliberately doesn't dispose the base-stream  
    class LimitedStream : InputStream 
    { 
     private Stream stream; 
     private long remaining; 
     public LimitedStream(Stream stream, long length) 
     { 
      if (length < 0) throw new ArgumentOutOfRangeException("length"); 
      if (stream == null) throw new ArgumentNullException("stream"); 
      if (!stream.CanRead) throw new ArgumentException("stream"); 
      this.stream = stream; 
      this.remaining = length; 
     } 
     protected override int ReadNextBlock(byte[] buffer, int offset, int count) 
     { 
      if (count > remaining) count = (int)remaining; 
      int bytesRead = stream.Read(buffer, offset, count); 
      if (bytesRead > 0) remaining -= bytesRead; 
      return bytesRead; 
     } 
    } 
} 
+0

¿Tuvo algún problema al leer los nodos durante la deserialización? Este código se ejecuta sin errores, sin embargo, cuando busco datos en primitiveBlock no obtengo nada. – ninehundredt

+0

Lo siento, nunca recibí una notificación. ¿Lo entendiste? Recuerdo que pude acceder a los datos. Aunque ya no estamos usando este código. – jonperl

+0

Finalmente logré que el código funcionara después de mirar otro proyecto, pero decidimos ir con otra solución después de tener más problemas con los mapas de calles abiertas. – ninehundredt

1

Sí, vinieron de Protogen en Fileformat.cs (basado en el archivo OSM Fileformat.proto .. a continuación código.)

package OSM_PROTO; 
    message Blob { 
    optional bytes raw = 1; 
    optional int32 raw_size = 2; 
    optional bytes zlib_data = 3; 
    optional bytes lzma_data = 4; 
    optional bytes bzip2_data = 5; 
    } 

    message BlockHeader { 
    required string type = 1; 
    optional bytes indexdata = 2; 
    required int32 datasize = 3; 
    } 

Aquí está la declaración de BlockHeader de archivo generado:

public sealed partial class BlockHeader : pb::GeneratedMessage<BlockHeader, BlockHeader.Builder> {...} 

-> usando pb = global :: Google.ProtocolBuffers;

(ProtocolBuffers.dll) vinieron con este paquete:

http://code.google.com/p/protobuf-csharp-port/downloads/detail?name=protobuf-csharp-port-2.4.1.473-full-binaries.zip&can=2&q=