diff --git a/.github/workflows/develop.yml b/.github/workflows/develop.yml
new file mode 100644
index 0000000..d4cb39b
--- /dev/null
+++ b/.github/workflows/develop.yml
@@ -0,0 +1,40 @@
+name: .NET Core
+
+on:
+ push:
+ branches: [ develop ]
+
+jobs:
+ develop:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v2
+ with:
+ submodules: 'true'
+
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v1
+ with:
+ dotnet-version: 6.0.x
+
+ - name: Add GitHub Nuget Source
+ run: dotnet nuget add source https://nuget.pkg.github.com/osmsharp/index.json -n osmsharp -u xivk -p ${{secrets.PACKAGES_SECRET }} --store-password-in-clear-text
+
+ - name: Restore packages.
+ run: dotnet restore
+ - name: Build all projects.
+ run: dotnet build --configuration Release --no-restore
+ - name: Unittests.
+ run: dotnet test
+ working-directory: ./test/OsmSharp.IO.Binary.Test/
+ - name: Functional tests.
+ run: dotnet run -c release
+ working-directory: ./test/OsmSharp.IO.Binary.Test.Functional/
+ - name: Nuget Pack
+ run: dotnet pack -c release
+ working-directory: ./src/OsmSharp.IO.Binary/
+ - name: Nuget push
+ run: dotnet nuget push **/*.nupkg --skip-duplicate -k ${{ secrets.GITHUB_TOKEN }} -s https://nuget.pkg.github.com/osmsharp/index.json
+ working-directory: ./src/
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 38d2362..6505d84 100644
--- a/.gitignore
+++ b/.gitignore
@@ -33,7 +33,6 @@ TestResults*/
*.vspx
*.patch
*.osm.pbf
-*.osm.bin
TestResult.xml
.vs*/
.idea*/
@@ -50,9 +49,13 @@ src/OsmSharp.IO.Binary.CLI/**/*.osm
src/OsmSharp.IO.Binary.CLI/**/*.db
src/OsmSharp.IO.Binary.CLI/**/*.hash
src/OsmSharp.IO.Binary.CLI/**/*.osc
+test/OsmSharp.IO.Binary.Test.Functional/**/*.osm
+test/OsmSharp.IO.Binary.Test.Functional/**/*.osm.bin
+test/OsmSharp.IO.Binary.Test.Functional/**/*.osm.pbf
.idea*/
**/logs/*
-**/*.bin.zip
\ No newline at end of file
+**/*.bin.zip
+test*.osm
\ No newline at end of file
diff --git a/LICENSE.md b/LICENSE.md
index c7b4a4a..c4ec9be 100644
--- a/LICENSE.md
+++ b/LICENSE.md
@@ -1,6 +1,6 @@
// The MIT License (MIT)
-// Copyright (c) 2017 Ben Abelshausen
+// Copyright (c) 2021 Ben Abelshausen
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
diff --git a/README.md b/README.md
index c231e7d..7837779 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@ An IO module on top of OsmSharp that reads/writes OSM data in a **custom binary
We built this because it more efficient compared to OSM-XML and has some advantages over OSM-PBF. This can be used to:
-- Read/write indivdual objects.
+- Read/write individual objects.
- Read/write objects with negative ids
- Read/write objects missing data (even missing ids, versions, etc.).
- Can be streamed.
diff --git a/src/OsmSharp.IO.Binary.CLI/OsmSharp.IO.Binary.CLI.csproj b/src/OsmSharp.IO.Binary.CLI/OsmSharp.IO.Binary.CLI.csproj
index a61b52e..c268e6a 100644
--- a/src/OsmSharp.IO.Binary.CLI/OsmSharp.IO.Binary.CLI.csproj
+++ b/src/OsmSharp.IO.Binary.CLI/OsmSharp.IO.Binary.CLI.csproj
@@ -1,7 +1,7 @@
Exe
- netcoreapp3.1
+ net5.0
diff --git a/src/OsmSharp.IO.Binary/BinarySerializer.cs b/src/OsmSharp.IO.Binary/BinarySerializer.cs
index d3411bb..59e3291 100644
--- a/src/OsmSharp.IO.Binary/BinarySerializer.cs
+++ b/src/OsmSharp.IO.Binary/BinarySerializer.cs
@@ -37,7 +37,7 @@ public static class BinarySerializer
///
/// Appends the header byte(s).
///
- private static void AppendHeader(this Stream stream, OsmGeo osmGeo)
+ public static void AppendHeader(this Stream stream, OsmGeo osmGeo)
{
// build header containing type and nullable flags.
byte header = 1; // a node.
diff --git a/src/OsmSharp.IO.Binary/OsmSharp.IO.Binary.csproj b/src/OsmSharp.IO.Binary/OsmSharp.IO.Binary.csproj
index f1055a4..45a9bcb 100644
--- a/src/OsmSharp.IO.Binary/OsmSharp.IO.Binary.csproj
+++ b/src/OsmSharp.IO.Binary/OsmSharp.IO.Binary.csproj
@@ -3,7 +3,7 @@
netstandard2.0
OsmSharp.IO.Binary
OsmSharp.IO.Binary
- 0.2.8-alpha
+ 0.3.1-pre001
OsmSharp.IO.Binary
Ben Abelshausen
A binary IO package for OsmSharp.
@@ -15,6 +15,6 @@
default
-
+
\ No newline at end of file
diff --git a/src/OsmSharp.IO.Binary/v0_1/BinaryOsmStreamSource.cs b/src/OsmSharp.IO.Binary/v0_1/BinaryOsmStreamSource.cs
new file mode 100644
index 0000000..0dff2bb
--- /dev/null
+++ b/src/OsmSharp.IO.Binary/v0_1/BinaryOsmStreamSource.cs
@@ -0,0 +1,143 @@
+using System;
+using System.IO;
+using OsmSharp.Streams;
+
+namespace OsmSharp.IO.Binary.v0_1
+{
+
+ ///
+ /// A stream source that just reads objects in binary format.
+ ///
+ public class BinaryOsmStreamSource : OsmStreamSource
+ {
+ private readonly Stream _stream;
+ private readonly byte[] _buffer;
+ private readonly long? _initialPosition;
+
+ ///
+ /// Creates a new binary stream source.
+ ///
+ public BinaryOsmStreamSource(Stream stream)
+ {
+ _stream = stream;
+ if (_stream.CanSeek)
+ {
+ _initialPosition = _stream.Position;
+ }
+ _buffer = new byte[1024];
+ }
+
+ ///
+ /// Returns true if this source can be reset.
+ ///
+ public override bool CanReset => _stream.CanSeek;
+
+ ///
+ /// Returns the current object.
+ ///
+ ///
+ public override OsmGeo Current()
+ {
+ return _current;
+ }
+
+ private OsmGeo _current;
+ private long? _firstWayPosition;
+ private long? _firstRelationPosition;
+
+ ///
+ /// Move to the next object in this stream source.
+ ///
+ public override bool MoveNext(bool ignoreNodes, bool ignoreWays, bool ignoreRelations)
+ {
+ if (_stream.CanSeek)
+ {
+ if (_stream.Length == _stream.Position + 1)
+ {
+ return false;
+ }
+
+ // move to first way/relation position if they are known and nodes and or ways are to be skipped.
+ if (_firstWayPosition != null && ignoreNodes && !ignoreWays)
+ {
+ // if nodes have to be ignored, there was already a first pass and ways are not to be ignored jump to the first way.
+ if (_stream.Position <= _firstWayPosition)
+ {
+ // only just to the first way if that hasn't happened yet.
+ _stream.Seek(_firstWayPosition.Value, SeekOrigin.Begin);
+ }
+ }
+
+ if (_firstRelationPosition != null && ignoreNodes && ignoreWays && !ignoreRelations)
+ {
+ // if nodes and ways have to be ignored, there was already a first pass and ways are not be ignored jump to the first relation.
+ if (_stream.Position < _firstRelationPosition)
+ {
+ // only just to the first relation if that hasn't happened yet.
+ _stream.Seek(_firstRelationPosition.Value, SeekOrigin.Begin);
+ }
+ }
+ }
+
+ long? positionBefore = null;
+ if (_stream.CanSeek) positionBefore = _stream.Position;
+ var osmGeo = this.DoMoveNext();
+ while (osmGeo != null)
+ {
+ switch (osmGeo.Type)
+ {
+ case OsmGeoType.Node:
+ if (!ignoreNodes)
+ {
+ _current = osmGeo;
+ return true;
+ }
+
+ break;
+ case OsmGeoType.Way:
+ if (_firstWayPosition == null && positionBefore != null) _firstWayPosition = positionBefore;
+ if (!ignoreWays)
+ {
+ _current = osmGeo;
+ return true;
+ }
+
+ break;
+ case OsmGeoType.Relation:
+ if (_firstRelationPosition == null && positionBefore != null)
+ _firstRelationPosition = positionBefore;
+ if (!ignoreRelations)
+ {
+ _current = osmGeo;
+ return true;
+ }
+
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+
+ osmGeo = this.DoMoveNext();
+ }
+
+ return false;
+ }
+
+ private OsmGeo DoMoveNext()
+ {
+ return BinarySerializer.ReadOsmGeo(_stream, _buffer);
+ }
+
+ ///
+ /// Resets this stream.
+ ///
+ public override void Reset()
+ {
+ if (_initialPosition == null) throw new NotSupportedException(
+ $"Cannot reset this stream, source stream is not seekable, check {nameof(this.CanReset)} before calling {nameof(this.Reset)}");
+
+ _current = null;
+ _stream.Seek(_initialPosition.Value, SeekOrigin.Begin);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/OsmSharp.IO.Binary/v0_1/BinaryOsmStreamTarget.cs b/src/OsmSharp.IO.Binary/v0_1/BinaryOsmStreamTarget.cs
new file mode 100644
index 0000000..9510b22
--- /dev/null
+++ b/src/OsmSharp.IO.Binary/v0_1/BinaryOsmStreamTarget.cs
@@ -0,0 +1,55 @@
+using System.IO;
+
+namespace OsmSharp.IO.Binary.v0_1
+{
+ ///
+ /// A stream target that just writes objects in binary format.
+ ///
+ public class BinaryOsmStreamTarget : OsmSharp.Streams.OsmStreamTarget
+ {
+ private readonly Stream _stream;
+
+ ///
+ /// Creates a new stream target.
+ ///
+ public BinaryOsmStreamTarget(Stream stream)
+ {
+ _stream = stream;
+ }
+
+ ///
+ /// Adds a node.
+ ///
+ ///
+ public override void AddNode(Node node)
+ {
+ _stream.Append(node);
+ }
+
+ ///
+ /// Adds a relation.
+ ///
+ ///
+ public override void AddRelation(Relation relation)
+ {
+ _stream.Append(relation);
+ }
+
+ ///
+ /// Adds a way.
+ ///
+ ///
+ public override void AddWay(Way way)
+ {
+ _stream.Append(way);
+ }
+
+ ///
+ /// Initializes this target.
+ ///
+ public override void Initialize()
+ {
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/OsmSharp.IO.Binary/v0_1/BinarySerializer.cs b/src/OsmSharp.IO.Binary/v0_1/BinarySerializer.cs
new file mode 100644
index 0000000..532ebc4
--- /dev/null
+++ b/src/OsmSharp.IO.Binary/v0_1/BinarySerializer.cs
@@ -0,0 +1,483 @@
+using System;
+using System.IO;
+using OsmSharp.Tags;
+
+namespace OsmSharp.IO.Binary.v0_1
+{
+ ///
+ /// Contains all binary formatting code.
+ ///
+ public static class BinarySerializer
+ {
+ private static System.Text.Encoder _encoder = (new System.Text.UnicodeEncoding()).GetEncoder();
+
+ ///
+ /// Appends the header byte(s).
+ ///
+ public static int AppendHeader(this Stream stream, OsmGeo osmGeo)
+ {
+ // build header containing type and nullable flags.
+ byte header = 1; // a node.
+ if(osmGeo.Type == OsmGeoType.Way)
+ {
+ header = 2;
+ }
+ else if(osmGeo.Type == OsmGeoType.Relation)
+ {
+ header = 3;
+ }
+ if (!osmGeo.Id.HasValue) { header = (byte)(header | 4); }
+ if (!osmGeo.ChangeSetId.HasValue) { header = (byte)(header | 8); }
+ if (!osmGeo.TimeStamp.HasValue) { header = (byte)(header | 16); }
+ if (!osmGeo.UserId.HasValue) { header = (byte)(header | 32); }
+ if (!osmGeo.Version.HasValue) { header = (byte)(header | 64); }
+ if (!osmGeo.Visible.HasValue) { header = (byte)(header | 128); }
+ stream.WriteByte(header);
+
+ return 1;
+ }
+
+ ///
+ /// Writes the given node starting at the stream's current position.
+ ///
+ public static int Append(this Stream stream, Node node)
+ {
+ if (node == null) { throw new ArgumentNullException(nameof(node)); }
+
+ // appends the header.
+ var size = stream.AppendHeader(node);
+
+ // write osm geo data.
+ size += stream.AppendOsmGeo(node);
+
+ // write lat/lon with nullable flags.
+ byte header = 0;
+ if (!node.Latitude.HasValue) { header = (byte)(header | 1); }
+ if (!node.Longitude.HasValue) { header = (byte)(header | 2); }
+ size += 1;
+ stream.WriteByte(header);
+ if (node.Latitude.HasValue) { size += stream.Write(node.Latitude.Value); }
+ if (node.Longitude.HasValue) { size += stream.Write(node.Longitude.Value); }
+
+ return size;
+ }
+
+ ///
+ /// Writes the given way starting at the stream's current position.
+ ///
+ public static int Append(this Stream stream, Way way)
+ {
+ if (way == null) { throw new ArgumentNullException(nameof(way)); }
+
+ // appends the header.
+ var size = stream.AppendHeader(way);
+
+ // write data.
+ size += stream.AppendOsmGeo(way);
+
+ if (way.Nodes == null ||
+ way.Nodes.Length == 0)
+ {
+ size += stream.Write(0);
+ }
+ else
+ {
+ size += stream.Write(way.Nodes.Length);
+ for (var i = 0; i < way.Nodes.Length; i++)
+ {
+ size += stream.Write(way.Nodes[i]);
+ }
+ }
+
+ return size;
+ }
+
+ ///
+ /// Writes the given relation starting at the stream's current position.
+ ///
+ public static int Append(this Stream stream, Relation relation)
+ {
+ if (relation == null) { throw new ArgumentNullException(nameof(relation)); }
+
+ // appends the header.
+ var size = stream.AppendHeader(relation);
+
+ // write data.
+ size += stream.AppendOsmGeo(relation);
+
+ if (relation.Members == null ||
+ relation.Members.Length == 0)
+ {
+ size += stream.Write(0);
+ }
+ else
+ {
+ size += stream.Write(relation.Members.Length);
+ for (var i = 0; i < relation.Members.Length; i++)
+ {
+ size += stream.Write(relation.Members[i].Id);
+ size += stream.WriteWithSize(relation.Members[i].Role);
+ switch (relation.Members[i].Type)
+ {
+ case OsmGeoType.Node:
+ stream.WriteByte((byte)1);
+ break;
+ case OsmGeoType.Way:
+ stream.WriteByte((byte)2);
+ break;
+ case OsmGeoType.Relation:
+ stream.WriteByte((byte)3);
+ break;
+ }
+ size += 1;
+ }
+ }
+
+ return size;
+ }
+
+ private static int AppendOsmGeo(this Stream stream, OsmGeo osmGeo)
+ {
+ var size = 0;
+
+ if (osmGeo.Id.HasValue) { size += stream.Write(osmGeo.Id.Value); }
+ if (osmGeo.ChangeSetId.HasValue) { size += stream.Write(osmGeo.ChangeSetId.Value); }
+ if (osmGeo.TimeStamp.HasValue) { size += stream.Write(osmGeo.TimeStamp.Value); }
+ if (osmGeo.UserId.HasValue) { size += stream.Write(osmGeo.UserId.Value); }
+ size += stream.WriteWithSize(osmGeo.UserName);
+ if (osmGeo.Version.HasValue) { size += stream.Write((int)osmGeo.Version.Value); }
+ if (osmGeo.Visible.HasValue) { size += stream.Write(osmGeo.Visible.Value); }
+
+ if (osmGeo.Tags == null ||
+ osmGeo.Tags.Count == 0)
+ {
+ size += stream.Write(0);
+ }
+ else
+ {
+ size += stream.Write(osmGeo.Tags.Count);
+ foreach (var t in osmGeo.Tags)
+ {
+ size += stream.WriteWithSize(t.Key);
+ size += stream.WriteWithSize(t.Value);
+ }
+ }
+
+ return size;
+ }
+
+ ///
+ /// Reads the header, returns the type, and outputs the flags.
+ ///
+ public static OsmGeoType? ReadOsmGeoHeader(this Stream stream, out bool hasId, out bool hasChangesetId, out bool hasTimestamp,
+ out bool hasUserId, out bool hasVersion, out bool hasVisible)
+ {
+ var header = stream.ReadByte();
+ if (header == -1)
+ {
+ hasId = false;
+ hasChangesetId = false;
+ hasTimestamp = false;
+ hasUserId = false;
+ hasVersion = false;
+ hasVisible = false;
+ return null;
+ }
+
+ hasId = (header & 4) == 0;
+ hasChangesetId = (header & 8) == 0;
+ hasTimestamp = (header & 16) == 0;
+ hasUserId = (header & 32) == 0;
+ hasVersion = (header & 64) == 0;
+ hasVisible = (header & 128) == 0;
+
+ var type = header & 3;
+ switch (type)
+ {
+ case 1:
+ return OsmGeoType.Node;
+ case 2:
+ return OsmGeoType.Way;
+ case 3:
+ return OsmGeoType.Relation;
+ }
+ throw new Exception("Invalid header: cannot detect OsmGeoType.");
+ }
+
+ ///
+ /// Reads an OSM object starting at the stream's current position.
+ ///
+ public static OsmGeo ReadOsmGeo(this Stream stream, byte[] buffer)
+ {
+ bool hasId, hasChangesetId, hasTimestamp, hasUserId, hasVersion, hasVisible;
+ var type = stream.ReadOsmGeoHeader(out hasId, out hasChangesetId, out hasTimestamp,
+ out hasUserId, out hasVersion, out hasVisible);
+ if (type == null) return null;
+
+ // read the basics.
+ long? id = null;
+ if (hasId) { id = stream.ReadInt64(buffer); }
+ long? changesetId = null;
+ if (hasChangesetId) { changesetId = stream.ReadInt64(buffer); }
+ DateTime? timestamp = null;
+ if (hasTimestamp) { timestamp = stream.ReadDateTime(buffer); }
+ long? userId = null;
+ if (hasUserId) { userId = stream.ReadInt64(buffer); }
+ var username = stream.ReadWithSizeString(buffer);
+ int? version = null;
+ if (hasVersion) { version = stream.ReadInt32(buffer); }
+ bool? visible = null;
+ if (hasVisible) { visible = stream.ReadBool(); }
+
+ // read tags.
+ var tagsCount = stream.ReadInt32(buffer);
+ TagsCollection tags = null;
+ if (tagsCount > 0)
+ {
+ tags = new TagsCollection(tagsCount);
+ for (var i = 0; i < tagsCount; i++)
+ {
+ var key = stream.ReadWithSizeString(buffer);
+ var value = stream.ReadWithSizeString(buffer);
+ tags.AddOrReplace(key, value);
+ }
+ }
+
+ OsmGeo osmGeo = null;
+ switch (type)
+ {
+ case OsmGeoType.Node:
+ osmGeo = stream.ReadNode(buffer);
+ break;
+ case OsmGeoType.Way:
+ osmGeo = stream.ReadWay(buffer);
+ break;
+ case OsmGeoType.Relation:
+ osmGeo = stream.ReadRelation(buffer);
+ break;
+ }
+
+ osmGeo.Id = id;
+ osmGeo.ChangeSetId = changesetId;
+ osmGeo.TimeStamp = timestamp;
+ osmGeo.UserId = userId;
+ osmGeo.UserName = username;
+ osmGeo.Version = version;
+ osmGeo.Visible = visible;
+ osmGeo.Tags = tags;
+
+ return osmGeo;
+ }
+
+ private static Node ReadNode(this Stream stream, byte[] buffer)
+ {
+ var node = new Node();
+
+ var header = stream.ReadByte();
+ var hasLatitude = (header & 1) == 0;
+ var hasLongitude = (header & 2) == 0;
+
+ if (hasLatitude) { node.Latitude = stream.ReadDouble(buffer); }
+ if (hasLongitude) { node.Longitude = stream.ReadDouble(buffer); }
+
+ return node;
+ }
+
+ private static Way ReadWay(this Stream stream, byte[] buffer)
+ {
+ var way = new Way();
+
+ var nodeCount = stream.ReadInt32(buffer);
+ if (nodeCount > 0)
+ {
+ var nodes = new long[nodeCount];
+ for (var i = 0; i < nodeCount; i++)
+ {
+ nodes[i] = stream.ReadInt64(buffer);
+ }
+ way.Nodes = nodes;
+ }
+
+ return way;
+ }
+
+ private static Relation ReadRelation(this Stream stream, byte[] buffer)
+ {
+ var relation = new Relation();
+
+ var memberCount = stream.ReadInt32(buffer);
+ if (memberCount > 0)
+ {
+ var members = new RelationMember[memberCount];
+ for(var i = 0; i< memberCount; i++)
+ {
+ var id = stream.ReadInt64(buffer);
+ var role = stream.ReadWithSizeString(buffer);
+ var typeId = stream.ReadByte();
+ var type = OsmGeoType.Node;
+ switch(typeId)
+ {
+ case 2:
+ type = OsmGeoType.Way;
+ break;
+ case 3:
+ type = OsmGeoType.Relation;
+ break;
+ }
+ members[i] = new RelationMember()
+ {
+ Id = id,
+ Role = role,
+ Type = type
+ };
+ }
+ relation.Members = members;
+ }
+
+ return relation;
+ }
+
+ ///
+ /// Writes the given value to the stream.
+ ///
+ public static int Write(this Stream stream, int value)
+ {
+ stream.Write(BitConverter.GetBytes(value), 0, 4);
+ return 4;
+ }
+
+ private static int Write(this Stream stream, float value)
+ {
+ stream.Write(BitConverter.GetBytes(value), 0, 4);
+ return 4;
+ }
+
+ private static int Write(this Stream stream, double value)
+ {
+ stream.Write(BitConverter.GetBytes(value), 0, 8);
+ return 8;
+ }
+
+ private static int Write(this Stream stream, long value)
+ {
+ stream.Write(BitConverter.GetBytes(value), 0, 8);
+ return 8;
+ }
+
+ private static int Write(this Stream stream, ulong value)
+ {
+ stream.Write(BitConverter.GetBytes(value), 0, 8);
+ return 8;
+ }
+
+ private static int Write(this Stream stream, DateTime value)
+ {
+ stream.Write(BitConverter.GetBytes(value.Ticks), 0, 8);
+ return 8;
+ }
+
+ private static int Write(this Stream stream, bool value)
+ {
+ if (value)
+ {
+ stream.WriteByte(1);
+ }
+ else
+ {
+ stream.WriteByte(0);
+ }
+ return 1;
+ }
+
+ private static int WriteWithSize(this Stream stream, string value)
+ {
+ if (string.IsNullOrWhiteSpace(value))
+ {
+ stream.WriteByte(0);
+ return 1;
+ }
+ else
+ { // TODO: improve this based on the protobuf way of handling this kind of variable info.
+ var bytes = System.Text.Encoding.Unicode.GetBytes(value);
+ var position = 0;
+ while(bytes.Length - position >= 255)
+ { // write in blocks of 255.
+ stream.WriteByte(255);
+ stream.Write(bytes, position, 255);
+ position += 256; // data + size
+ }
+ stream.WriteByte((byte)(bytes.Length - position));
+ if (bytes.Length - position > 0)
+ {
+ stream.Write(bytes, position, bytes.Length - position);
+ }
+ return bytes.Length + 1;
+ }
+ }
+
+ private static DateTime ReadDateTime(this Stream stream, byte[] buffer)
+ {
+ return new DateTime(stream.ReadInt64(buffer));
+ }
+
+ private static long ReadInt64(this Stream stream, byte[] buffer)
+ {
+ stream.Read(buffer, 0, 8);
+ return BitConverter.ToInt64(buffer, 0);
+ }
+
+ private static int ReadInt32(this Stream stream, byte[] buffer)
+ {
+ stream.Read(buffer, 0, 4);
+ return BitConverter.ToInt32(buffer, 0);
+ }
+
+ private static bool ReadBool(this Stream stream)
+ {
+ var v = stream.ReadByte();
+ if (v == 0)
+ {
+ return false;
+ }
+ else if (v == 1)
+ {
+ return true;
+ }
+ else
+ {
+ throw new InvalidDataException("Cannot deserialize bool.");
+ }
+ }
+
+ private static float ReadSingle(this Stream stream, byte[] buffer)
+ {
+ stream.Read(buffer, 0, 4);
+ return BitConverter.ToSingle(buffer, 0);
+ }
+
+ private static double ReadDouble(this Stream stream, byte[] buffer)
+ {
+ stream.Read(buffer, 0, 8);
+ return BitConverter.ToDouble(buffer, 0);
+ }
+
+ private static string ReadWithSizeString(this System.IO.Stream stream, byte[] buffer)
+ {
+ var size = stream.ReadByte();
+ var position = 0;
+ while (size == 255)
+ {
+ stream.Read(buffer, position, (int)size);
+ size = stream.ReadByte();
+ position += 256;
+ }
+ if (size > 0)
+ {
+ stream.Read(buffer, position, (int)size);
+ }
+
+
+ return System.Text.UnicodeEncoding.Unicode.GetString(buffer, 0, size);
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/OsmSharp.IO.Binary.Test.Functional/OsmSharp.IO.Binary.Test.Functional.csproj b/test/OsmSharp.IO.Binary.Test.Functional/OsmSharp.IO.Binary.Test.Functional.csproj
index 9dfa6c9..780ca57 100644
--- a/test/OsmSharp.IO.Binary.Test.Functional/OsmSharp.IO.Binary.Test.Functional.csproj
+++ b/test/OsmSharp.IO.Binary.Test.Functional/OsmSharp.IO.Binary.Test.Functional.csproj
@@ -1,7 +1,7 @@
- netcoreapp3.1
+ net6.0
OsmSharp.IO.Binary.Test.Functional
Exe
diff --git a/test/OsmSharp.IO.Binary.Test.Functional/Program.cs b/test/OsmSharp.IO.Binary.Test.Functional/Program.cs
index 4c480a5..8fa35f9 100644
--- a/test/OsmSharp.IO.Binary.Test.Functional/Program.cs
+++ b/test/OsmSharp.IO.Binary.Test.Functional/Program.cs
@@ -45,7 +45,7 @@ public static void Main(string[] args)
.CreateLogger();
// download test data.
- Download.ToFile("http://files.itinero.tech/data/OSM/planet/europe/luxembourg-latest.osm.pbf", "test.osm.pbf").Wait();
+ Download.ToFile("http://planet.anyways.eu/planet/europe/luxembourg/luxembourg-latest.osm.pbf", "test.osm.pbf").Wait();
// test read/writing an existing OSM file.
Log.Information("Testing write to OSM binary formatted file...");
@@ -143,11 +143,11 @@ public static void Main(string[] args)
}
using (var sourceStream = File.OpenRead("test3.osm.bin"))
- using (var targetStream = File.Open("test2.osm.pbf", FileMode.Create))
+ using (var targetStream = File.Open("test2.osm", FileMode.Create))
{
var source = new OsmSharp.Streams.BinaryOsmStreamSource(sourceStream);
- var target = new OsmSharp.Streams.PBFOsmStreamTarget(targetStream);
+ var target = new OsmSharp.Streams.XmlOsmStreamTarget(targetStream);
target.RegisterSource(source);
target.Pull();
}
diff --git a/test/OsmSharp.IO.Binary.Test/OsmSharp.IO.Binary.Test.csproj b/test/OsmSharp.IO.Binary.Test/OsmSharp.IO.Binary.Test.csproj
index 8bca592..cf654cb 100644
--- a/test/OsmSharp.IO.Binary.Test/OsmSharp.IO.Binary.Test.csproj
+++ b/test/OsmSharp.IO.Binary.Test/OsmSharp.IO.Binary.Test.csproj
@@ -1,7 +1,7 @@
- netcoreapp3.1
+ net6.0
false
@@ -24,4 +24,9 @@
+
+
+
+
+
diff --git a/test/OsmSharp.IO.Binary.Test/Staging/TestStreamsV0_1.cs b/test/OsmSharp.IO.Binary.Test/Staging/TestStreamsV0_1.cs
new file mode 100644
index 0000000..e2dedcb
--- /dev/null
+++ b/test/OsmSharp.IO.Binary.Test/Staging/TestStreamsV0_1.cs
@@ -0,0 +1,179 @@
+using System;
+using System.IO;
+using OsmSharp.Tags;
+
+namespace OsmSharp.IO.Binary.Test.Staging
+{
+ internal static class TestStreamsV0_1
+ {
+ public static Stream GetNodeTestStream()
+ {
+ var stream = new MemoryStream();
+ var node1 = new Node()
+ {
+ Id = 1,
+ ChangeSetId = 2,
+ Latitude = 10,
+ TimeStamp = DateTime.Now,
+ Longitude = 11,
+ Tags = new TagsCollection(new Tag("name", "hu?")),
+ UserId = 12,
+ UserName = "Ben",
+ Version = 123,
+ Visible = true
+ };
+
+ v0_1.TestBinarySerializer.Append(stream, node1);
+
+ stream.Seek(0, SeekOrigin.Begin);
+ return stream;
+ }
+
+ public static Stream GetWayTestStream()
+ {
+ var stream = new MemoryStream();
+ var way1 = new Way()
+ {
+ Id = 1,
+ ChangeSetId = 1,
+ TimeStamp = DateTime.Now,
+ Tags = new TagsCollection(new Tag("name", "hu?")),
+ Nodes = new long[]
+ {
+ 1,
+ 2,
+ 3
+ },
+ UserId = 1,
+ UserName = "Ben",
+ Version = 1,
+ Visible = true
+ };
+
+ v0_1.TestBinarySerializer.Append(stream, way1);
+
+ stream.Seek(0, SeekOrigin.Begin);
+ return stream;
+ }
+
+ public static Stream GetRelationTestStream()
+ {
+ var stream = new MemoryStream();
+ var relation1 = new Relation()
+ {
+ Id = 1,
+ ChangeSetId = 1,
+ TimeStamp = DateTime.Now,
+ Tags = new TagsCollection(new Tag("name", "hu?")),
+ Members = new RelationMember[]
+ {
+ new RelationMember()
+ {
+ Id = 1,
+ Role = "node",
+ Type = OsmGeoType.Node
+ },
+ new RelationMember()
+ {
+ Id = 2,
+ Role = "way",
+ Type = OsmGeoType.Way
+ },
+ new RelationMember()
+ {
+ Id = 3,
+ Role = "relation",
+ Type = OsmGeoType.Relation
+ }
+ },
+ UserId = 1,
+ UserName = "Ben",
+ Version = 1,
+ Visible = true
+ };
+
+ v0_1.TestBinarySerializer.Append(stream, relation1);
+
+ stream.Seek(0, SeekOrigin.Begin);
+ return stream;
+ }
+
+ public static Stream GetNodeWayAndRelationTestStream()
+ {
+ var stream = new MemoryStream();
+ var node1 = new Node()
+ {
+ Id = 1,
+ ChangeSetId = 2,
+ Latitude = 10,
+ TimeStamp = DateTime.Now,
+ Longitude = 11,
+ Tags = new TagsCollection(new Tag("name", "hu?")),
+ UserId = 12,
+ UserName = "Ben",
+ Version = 123,
+ Visible = true
+ };
+
+ v0_1.TestBinarySerializer.Append(stream, node1);
+
+ var way1 = new Way()
+ {
+ Id = 1,
+ ChangeSetId = 1,
+ TimeStamp = DateTime.Now,
+ Tags = new TagsCollection(new Tag("name", "hu?")),
+ Nodes = new long[]
+ {
+ 1,
+ 2,
+ 3
+ },
+ UserId = 1,
+ UserName = "Ben",
+ Version = 1,
+ Visible = true
+ };
+
+ v0_1.TestBinarySerializer.Append(stream, way1);
+
+ var relation1 = new Relation()
+ {
+ Id = 1,
+ ChangeSetId = 1,
+ TimeStamp = DateTime.Now,
+ Tags = new TagsCollection(new Tag("name", "hu?")),
+ Members = new RelationMember[]
+ {
+ new RelationMember()
+ {
+ Id = 1,
+ Role = "node",
+ Type = OsmGeoType.Node
+ },
+ new RelationMember()
+ {
+ Id = 2,
+ Role = "way",
+ Type = OsmGeoType.Way
+ },
+ new RelationMember()
+ {
+ Id = 3,
+ Role = "relation",
+ Type = OsmGeoType.Relation
+ }
+ },
+ UserId = 1,
+ UserName = "Ben",
+ Version = 1,
+ Visible = true
+ };
+
+ v0_1.TestBinarySerializer.Append(stream, relation1);
+
+ stream.Seek(0, SeekOrigin.Begin);
+ return stream;
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/OsmSharp.IO.Binary.Test/v0_1/BinaryOsmStreamSourceTests.cs b/test/OsmSharp.IO.Binary.Test/v0_1/BinaryOsmStreamSourceTests.cs
new file mode 100644
index 0000000..846b181
--- /dev/null
+++ b/test/OsmSharp.IO.Binary.Test/v0_1/BinaryOsmStreamSourceTests.cs
@@ -0,0 +1,183 @@
+using System;
+using System.IO;
+using System.IO.Compression;
+using System.Linq;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using OsmSharp.IO.Binary.v0_1;
+using OsmSharp.Tags;
+
+namespace OsmSharp.IO.Binary.Test.v0_1
+{
+ ///
+ /// Contains tests for the binary serializer core functions.
+ ///
+ [TestClass]
+ public class BinaryOsmStreamSourceTests
+ {
+ [TestMethod]
+ public void BinaryOsmStreamSource_ShouldReadNode()
+ {
+ using (var stream = Staging.TestStreamsV0_1.GetNodeTestStream())
+ {
+ var sourceStream = new BinaryOsmStreamSource(stream);
+ var nodes = sourceStream.ToList();
+
+ Assert.AreEqual(1, nodes.Count);
+ }
+ }
+
+ [TestMethod]
+ public void BinaryOsmStreamSource_ShouldReadWay()
+ {
+ using (var stream = Staging.TestStreamsV0_1.GetWayTestStream())
+ {
+ var sourceStream = new BinaryOsmStreamSource(stream);
+ var ways = sourceStream.ToList();
+
+ Assert.AreEqual(1, ways.Count);
+ }
+ }
+
+ [TestMethod]
+ public void BinaryOsmStreamSource_ShouldReadRelation()
+ {
+ using (var stream = Staging.TestStreamsV0_1.GetRelationTestStream())
+ {
+ var sourceStream = new BinaryOsmStreamSource(stream);
+ var relations = sourceStream.ToList();
+
+ Assert.AreEqual(1, relations.Count);
+ }
+ }
+
+ [TestMethod]
+ public void BinaryOsmStreamSource_ShouldReadNodeWayAndRelation()
+ {
+ using (var stream = Staging.TestStreamsV0_1.GetNodeWayAndRelationTestStream())
+ {
+ var sourceStream = new BinaryOsmStreamSource(stream);
+ var osmGeos = sourceStream.ToList();
+
+ Assert.AreEqual(3, osmGeos.Count);
+ Assert.IsInstanceOfType(osmGeos[0], typeof(Node));
+ Assert.IsInstanceOfType(osmGeos[1], typeof(Way));
+ Assert.IsInstanceOfType(osmGeos[2], typeof(Relation));
+ }
+ }
+
+ [TestMethod]
+ public void BinaryOsmStreamSource_ShouldIgnoreNode()
+ {
+ using (var stream = Staging.TestStreamsV0_1.GetNodeWayAndRelationTestStream())
+ {
+ var sourceStream = new BinaryOsmStreamSource(stream);
+ var osmGeos = sourceStream.ToList();
+
+ sourceStream.Reset();
+ osmGeos = sourceStream.EnumerateAndIgore(true, false, false).ToList();
+
+ Assert.AreEqual(2, osmGeos.Count);
+ Assert.IsInstanceOfType(osmGeos[0], typeof(Way));
+ Assert.IsInstanceOfType(osmGeos[1], typeof(Relation));
+ }
+ }
+
+ [TestMethod]
+ public void BinaryOsmStreamSource_ShouldIgnoreWay()
+ {
+ using (var stream = Staging.TestStreamsV0_1.GetNodeWayAndRelationTestStream())
+ {
+ var sourceStream = new BinaryOsmStreamSource(stream);
+ var osmGeos = sourceStream.ToList();
+
+ sourceStream.Reset();
+ osmGeos = sourceStream.EnumerateAndIgore(false, true, false).ToList();
+
+ Assert.AreEqual(2, osmGeos.Count);
+ Assert.IsInstanceOfType(osmGeos[0], typeof(Node));
+ Assert.IsInstanceOfType(osmGeos[1], typeof(Relation));
+ }
+ }
+
+ [TestMethod]
+ public void BinaryOsmStreamSource_ShouldIgnoreRelation()
+ {
+ using (var stream = Staging.TestStreamsV0_1.GetNodeWayAndRelationTestStream())
+ {
+ var sourceStream = new BinaryOsmStreamSource(stream);
+ var osmGeos = sourceStream.ToList();
+
+ sourceStream.Reset();
+ osmGeos = sourceStream.EnumerateAndIgore(false, false, true).ToList();
+
+ Assert.AreEqual(2, osmGeos.Count);
+ Assert.IsInstanceOfType(osmGeos[0], typeof(Node));
+ Assert.IsInstanceOfType(osmGeos[1], typeof(Way));
+ }
+ }
+
+ [TestMethod]
+ public void BinaryOsmStreamSource_ShouldReadFomDeflateStream()
+ {
+ using (var stream = new MemoryStream())
+ {
+ using (var streamCompressed = new DeflateStream(stream, CompressionMode.Compress, true))
+ {
+ var targetStream = new BinaryOsmStreamTarget(streamCompressed);
+ targetStream.Initialize();
+ targetStream.AddRelation(new Relation()
+ {
+ Id = 1,
+ ChangeSetId = 1,
+ TimeStamp = DateTime.Now,
+ Tags = new TagsCollection(new Tag("name", "hu?")),
+ Members = new RelationMember[]
+ {
+ new RelationMember()
+ {
+ Id = 1,
+ Role = "node",
+ Type = OsmGeoType.Node
+ },
+ new RelationMember()
+ {
+ Id = 2,
+ Role = "way",
+ Type = OsmGeoType.Way
+ },
+ new RelationMember()
+ {
+ Id = 3,
+ Role = "relation",
+ Type = OsmGeoType.Relation
+ }
+ },
+ UserId = 1,
+ UserName = "Ben",
+ Version = 1,
+ Visible = true
+ });
+ }
+
+ stream.Seek(0, SeekOrigin.Begin);
+ using (var streamCompressed = new DeflateStream(stream, CompressionMode.Decompress))
+ {
+ var sourceStream = new BinaryOsmStreamSource(streamCompressed);
+ var osmGeos = sourceStream.ToList();
+
+ Assert.AreEqual(1, osmGeos.Count);
+ }
+ }
+ }
+
+ [TestMethod]
+ public void BinaryOsmStreamSource_ShouldReadData1()
+ {
+ using var stream = TestStreams.GetData1Stream();
+ var sourceStream = new BinaryOsmStreamSource(stream);
+ var osmGeos = sourceStream.ToList();
+
+ Assert.AreEqual(27858, osmGeos.Count);
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/OsmSharp.IO.Binary.Test/v0_1/BinaryOsmStreamTargetTests.cs b/test/OsmSharp.IO.Binary.Test/v0_1/BinaryOsmStreamTargetTests.cs
new file mode 100644
index 0000000..db0b45b
--- /dev/null
+++ b/test/OsmSharp.IO.Binary.Test/v0_1/BinaryOsmStreamTargetTests.cs
@@ -0,0 +1,170 @@
+using System;
+using System.IO;
+using System.IO.Compression;
+using System.Linq;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using OsmSharp.IO.Binary.v0_1;
+using OsmSharp.Tags;
+
+namespace OsmSharp.IO.Binary.Test.v0_1
+{
+ ///
+ /// Contains tests for the binary serializer core functions.
+ ///
+ [TestClass]
+ public class BinaryOsmStreamTargetTests
+ {
+ [TestMethod]
+ public void BinaryOsmStreamTarget_ShouldWriteNode()
+ {
+ using (var stream = new MemoryStream())
+ {
+ var targetStream = new BinaryOsmStreamTarget(stream);
+ targetStream.Initialize();
+ targetStream.AddNode(new Node()
+ {
+ Id = 1,
+ ChangeSetId = 2,
+ Latitude = 10,
+ TimeStamp = DateTime.Now,
+ Longitude = 11,
+ Tags = new TagsCollection(new Tag("name", "hu?")),
+ UserId = 12,
+ UserName = "Ben",
+ Version = 123,
+ Visible = true
+ });
+
+ stream.Seek(0, SeekOrigin.Begin);
+ var sourceStream = new BinaryOsmStreamSource(stream);
+ var nodes = sourceStream.ToList();
+
+ Assert.AreEqual(1, nodes.Count);
+ }
+ }
+
+ [TestMethod]
+ public void BinaryOsmStreamTarget_ShouldWriteWay()
+ {
+ using (var stream = new MemoryStream())
+ {
+ var targetStream = new BinaryOsmStreamTarget(stream);
+ targetStream.Initialize();
+ targetStream.AddWay(new Way()
+ {
+ Id = 1,
+ ChangeSetId = 1,
+ TimeStamp = DateTime.Now,
+ Tags = new TagsCollection(new Tag("name", "hu?")),
+ Nodes = new long[]
+ {
+ 1,
+ 2,
+ 3
+ },
+ UserId = 1,
+ UserName = "Ben",
+ Version = 1,
+ Visible = true
+ });
+
+ stream.Seek(0, SeekOrigin.Begin);
+ var sourceStream = new BinaryOsmStreamSource(stream);
+ var osmGeos = sourceStream.ToList();
+
+ Assert.AreEqual(1, osmGeos.Count);
+ }
+ }
+
+ [TestMethod]
+ public void BinaryOsmStreamTarget_ShouldWriteRelation()
+ {
+ using (var stream = new MemoryStream())
+ {
+ var targetStream = new BinaryOsmStreamTarget(stream);
+ targetStream.Initialize();
+ targetStream.AddRelation(new Relation()
+ {
+ Id = 1,
+ ChangeSetId = 1,
+ TimeStamp = DateTime.Now,
+ Tags = new TagsCollection(new Tag("name", "hu?")),
+ Members = new RelationMember[]
+ {
+ new RelationMember()
+ {
+ Id = 1,
+ Role = "node",
+ Type = OsmGeoType.Node
+ },
+ new RelationMember()
+ {
+ Id = 2,
+ Role = "way",
+ Type = OsmGeoType.Way
+ },
+ new RelationMember()
+ {
+ Id = 3,
+ Role = "relation",
+ Type = OsmGeoType.Relation
+ }
+ },
+ UserId = 1,
+ UserName = "Ben",
+ Version = 1,
+ Visible = true
+ });
+
+ stream.Seek(0, SeekOrigin.Begin);
+ var sourceStream = new BinaryOsmStreamSource(stream);
+ var osmGeos = sourceStream.ToList();
+
+ Assert.AreEqual(1, osmGeos.Count);
+ }
+ }
+
+ [TestMethod]
+ public void BinaryOsmStreamTarget_ShouldWriteToDeflateStream()
+ {
+ using (var stream = new MemoryStream())
+ using (var streamCompressed = new System.IO.Compression.DeflateStream(stream, CompressionMode.Compress))
+ {
+ var targetStream = new BinaryOsmStreamTarget(streamCompressed);
+ targetStream.Initialize();
+ targetStream.AddRelation(new Relation()
+ {
+ Id = 1,
+ ChangeSetId = 1,
+ TimeStamp = DateTime.Now,
+ Tags = new TagsCollection(new Tag("name", "hu?")),
+ Members = new RelationMember[]
+ {
+ new RelationMember()
+ {
+ Id = 1,
+ Role = "node",
+ Type = OsmGeoType.Node
+ },
+ new RelationMember()
+ {
+ Id = 2,
+ Role = "way",
+ Type = OsmGeoType.Way
+ },
+ new RelationMember()
+ {
+ Id = 3,
+ Role = "relation",
+ Type = OsmGeoType.Relation
+ }
+ },
+ UserId = 1,
+ UserName = "Ben",
+ Version = 1,
+ Visible = true
+ });
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/OsmSharp.IO.Binary.Test/v0_1/BinarySerializerTests.cs b/test/OsmSharp.IO.Binary.Test/v0_1/BinarySerializerTests.cs
new file mode 100644
index 0000000..9acebfb
--- /dev/null
+++ b/test/OsmSharp.IO.Binary.Test/v0_1/BinarySerializerTests.cs
@@ -0,0 +1,177 @@
+using System;
+using System.IO;
+using System.Linq;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using OsmSharp.Tags;
+using OsmSharp.IO.Binary.v0_1;
+
+namespace OsmSharp.IO.Binary.Test.v0_1
+{
+ [TestClass]
+ public class BinarySerializerTests
+ {
+ ///
+ /// Tests reading/writing a node.
+ ///
+ [TestMethod]
+ public void TestReadWriteNode()
+ {
+ using (var stream = new MemoryStream())
+ {
+ var node1 = new Node()
+ {
+ Id = 1,
+ ChangeSetId = 2,
+ Latitude = 10,
+ TimeStamp = DateTime.Now,
+ Longitude = 11,
+ Tags = new TagsCollection(new Tag("name", "hu?")),
+ UserId = 12,
+ UserName = "Ben",
+ Version = 123,
+ Visible = true
+ };
+
+ // tests the actual serialization code.
+ TestBinarySerializer.Append(stream, node1);
+ stream.Seek(0, SeekOrigin.Begin);
+
+ // read again and compare.
+ var osmGeo = Binary.v0_1.BinarySerializer.ReadOsmGeo(stream, new byte[1024]);
+ Assert.IsNotNull(osmGeo);
+ Assert.IsInstanceOfType(osmGeo, typeof(Node));
+ var node2 = osmGeo as Node;
+
+ Assert.AreEqual(node1.Id, node2.Id);
+ Assert.AreEqual(node1.Latitude, node2.Latitude);
+ Assert.AreEqual(node1.Longitude, node2.Longitude);
+ Assert.AreEqual(node1.ChangeSetId, node2.ChangeSetId);
+ Assert.AreEqual(node1.TimeStamp, node2.TimeStamp);
+ Assert.AreEqual(node1.UserId, node2.UserId);
+ Assert.AreEqual(node1.UserName, node2.UserName);
+ Assert.AreEqual(node1.Version, node2.Version);
+ Assert.AreEqual(node1.Visible, node2.Visible);
+ ExtraAssert.AreEqual(node1.Tags.ToArray(), node2.Tags.ToArray());
+ }
+ }
+
+ ///
+ /// Tests reading/writing a way.
+ ///
+ [TestMethod]
+ public void TestReadWriteWay()
+ {
+ using (var stream = new MemoryStream())
+ {
+ var way1 = new Way()
+ {
+ Id = 1,
+ ChangeSetId = 1,
+ TimeStamp = DateTime.Now,
+ Tags = new TagsCollection(new Tag("name", "hu?")),
+ Nodes = new long[]
+ {
+ 1,
+ 2,
+ 3
+ },
+ UserId = 1,
+ UserName = "Ben",
+ Version = 1,
+ Visible = true
+ };
+
+ // tests the actual serialization code.
+ TestBinarySerializer.Append(stream, way1);
+ stream.Seek(0, SeekOrigin.Begin);
+
+ // read again and compare.
+ var osmGeo = Binary.v0_1.BinarySerializer.ReadOsmGeo(stream, new byte[1024]);
+ Assert.IsNotNull(osmGeo);
+ Assert.IsInstanceOfType(osmGeo, typeof(Way));
+ var way2 = osmGeo as Way;
+
+ Assert.AreEqual(way1.Id, way2.Id);
+ Assert.AreEqual(way1.ChangeSetId, way2.ChangeSetId);
+ Assert.AreEqual(way1.TimeStamp, way2.TimeStamp);
+ Assert.AreEqual(way1.UserId, way2.UserId);
+ Assert.AreEqual(way1.UserName, way2.UserName);
+ Assert.AreEqual(way1.Version, way2.Version);
+ Assert.AreEqual(way1.Visible, way2.Visible);
+ Assert.AreEqual(way1.Nodes.Length, way2.Nodes.Length);
+ ExtraAssert.AreEqual(way1.Nodes, way2.Nodes);
+ ExtraAssert.AreEqual(way1.Tags.ToArray(), way2.Tags.ToArray());
+ }
+ }
+
+ ///
+ /// Tests reading/writing a relation.
+ ///
+ [TestMethod]
+ public void TestReadWriteRelation()
+ {
+ using (var stream = new MemoryStream())
+ {
+ var relation1 = new Relation()
+ {
+ Id = 1,
+ ChangeSetId = 1,
+ TimeStamp = DateTime.Now,
+ Tags = new TagsCollection(new Tag("name", "hu?")),
+ Members = new RelationMember[]
+ {
+ new RelationMember()
+ {
+ Id = 1,
+ Role = "node",
+ Type = OsmGeoType.Node
+ },
+ new RelationMember()
+ {
+ Id = 2,
+ Role = "way",
+ Type = OsmGeoType.Way
+ },
+ new RelationMember()
+ {
+ Id = 3,
+ Role = "relation",
+ Type = OsmGeoType.Relation
+ }
+ },
+ UserId = 1,
+ UserName = "Ben",
+ Version = 1,
+ Visible = true
+ };
+
+ // tests the actual serialization code.
+ TestBinarySerializer.Append(stream, relation1);
+ stream.Seek(0, SeekOrigin.Begin);
+
+ // read again and compare.
+ var osmGeo = Binary.v0_1.BinarySerializer.ReadOsmGeo(stream, new byte[1024]);
+ Assert.IsNotNull(osmGeo);
+ Assert.IsInstanceOfType(osmGeo, typeof(Relation));
+ var relation2 = osmGeo as Relation;
+
+ Assert.AreEqual(relation1.Id, relation2.Id);
+ Assert.AreEqual(relation1.ChangeSetId, relation2.ChangeSetId);
+ Assert.AreEqual(relation1.TimeStamp, relation2.TimeStamp);
+ Assert.AreEqual(relation1.UserId, relation2.UserId);
+ Assert.AreEqual(relation1.UserName, relation2.UserName);
+ Assert.AreEqual(relation1.Version, relation2.Version);
+ Assert.AreEqual(relation1.Visible, relation2.Visible);
+ Assert.AreEqual(relation1.Members.Length, relation2.Members.Length);
+ ExtraAssert.AreEqual(relation1.Members, relation2.Members, (m1, m2) =>
+ {
+ Assert.IsNotNull(m2);
+ Assert.AreEqual(m1.Id, m2.Id);
+ Assert.AreEqual(m1.Role, m2.Role);
+ Assert.AreEqual(m1.Type, m2.Type);
+ });
+ ExtraAssert.AreEqual(relation1.Tags.ToArray(), relation2.Tags.ToArray());
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/OsmSharp.IO.Binary.Test/v0_1/TestBinarySerializer.cs b/test/OsmSharp.IO.Binary.Test/v0_1/TestBinarySerializer.cs
new file mode 100644
index 0000000..5e2d75e
--- /dev/null
+++ b/test/OsmSharp.IO.Binary.Test/v0_1/TestBinarySerializer.cs
@@ -0,0 +1,248 @@
+using System;
+using System.IO;
+
+namespace OsmSharp.IO.Binary.Test.v0_1
+{
+ ///
+ /// Contains all old binary formatting writing code only.
+ ///
+ /// The old format is only supported as a reader, we include this here as a reference.
+ ///
+ public static class TestBinarySerializer
+ {
+ private static System.Text.Encoder _encoder = (new System.Text.UnicodeEncoding()).GetEncoder();
+
+ ///
+ /// Appends the header byte(s).
+ ///
+ public static int AppendHeader(this Stream stream, OsmGeo osmGeo)
+ {
+ // build header containing type and nullable flags.
+ byte header = 1; // a node.
+ if(osmGeo.Type == OsmGeoType.Way)
+ {
+ header = 2;
+ }
+ else if(osmGeo.Type == OsmGeoType.Relation)
+ {
+ header = 3;
+ }
+ if (!osmGeo.Id.HasValue) { header = (byte)(header | 4); }
+ if (!osmGeo.ChangeSetId.HasValue) { header = (byte)(header | 8); }
+ if (!osmGeo.TimeStamp.HasValue) { header = (byte)(header | 16); }
+ if (!osmGeo.UserId.HasValue) { header = (byte)(header | 32); }
+ if (!osmGeo.Version.HasValue) { header = (byte)(header | 64); }
+ if (!osmGeo.Visible.HasValue) { header = (byte)(header | 128); }
+ stream.WriteByte(header);
+
+ return 1;
+ }
+
+ ///
+ /// Writes the given node starting at the stream's current position.
+ ///
+ public static int Append(this Stream stream, Node node)
+ {
+ if (node == null) { throw new ArgumentNullException(nameof(node)); }
+
+ // appends the header.
+ var size = stream.AppendHeader(node);
+
+ // write osm geo data.
+ size += stream.AppendOsmGeo(node);
+
+ // write lat/lon with nullable flags.
+ byte header = 0;
+ if (!node.Latitude.HasValue) { header = (byte)(header | 1); }
+ if (!node.Longitude.HasValue) { header = (byte)(header | 2); }
+ size += 1;
+ stream.WriteByte(header);
+ if (node.Latitude.HasValue) { size += stream.Write(node.Latitude.Value); }
+ if (node.Longitude.HasValue) { size += stream.Write(node.Longitude.Value); }
+
+ return size;
+ }
+
+ ///
+ /// Writes the given way starting at the stream's current position.
+ ///
+ public static int Append(this Stream stream, Way way)
+ {
+ if (way == null) { throw new ArgumentNullException(nameof(way)); }
+
+ // appends the header.
+ var size = stream.AppendHeader(way);
+
+ // write data.
+ size += stream.AppendOsmGeo(way);
+
+ if (way.Nodes == null ||
+ way.Nodes.Length == 0)
+ {
+ size += stream.Write(0);
+ }
+ else
+ {
+ size += stream.Write(way.Nodes.Length);
+ for (var i = 0; i < way.Nodes.Length; i++)
+ {
+ size += stream.Write(way.Nodes[i]);
+ }
+ }
+
+ return size;
+ }
+
+ ///
+ /// Writes the given relation starting at the stream's current position.
+ ///
+ public static int Append(this Stream stream, Relation relation)
+ {
+ if (relation == null) { throw new ArgumentNullException(nameof(relation)); }
+
+ // appends the header.
+ var size = stream.AppendHeader(relation);
+
+ // write data.
+ size += stream.AppendOsmGeo(relation);
+
+ if (relation.Members == null ||
+ relation.Members.Length == 0)
+ {
+ size += stream.Write(0);
+ }
+ else
+ {
+ size += stream.Write(relation.Members.Length);
+ for (var i = 0; i < relation.Members.Length; i++)
+ {
+ size += stream.Write(relation.Members[i].Id);
+ size += stream.WriteWithSize(relation.Members[i].Role);
+ switch (relation.Members[i].Type)
+ {
+ case OsmGeoType.Node:
+ stream.WriteByte((byte)1);
+ break;
+ case OsmGeoType.Way:
+ stream.WriteByte((byte)2);
+ break;
+ case OsmGeoType.Relation:
+ stream.WriteByte((byte)3);
+ break;
+ }
+ size += 1;
+ }
+ }
+
+ return size;
+ }
+
+ private static int AppendOsmGeo(this Stream stream, OsmGeo osmGeo)
+ {
+ var size = 0;
+
+ if (osmGeo.Id.HasValue) { size += stream.Write(osmGeo.Id.Value); }
+ if (osmGeo.ChangeSetId.HasValue) { size += stream.Write(osmGeo.ChangeSetId.Value); }
+ if (osmGeo.TimeStamp.HasValue) { size += stream.Write(osmGeo.TimeStamp.Value); }
+ if (osmGeo.UserId.HasValue) { size += stream.Write(osmGeo.UserId.Value); }
+ size += stream.WriteWithSize(osmGeo.UserName);
+ if (osmGeo.Version.HasValue) { size += stream.Write((int)osmGeo.Version.Value); }
+ if (osmGeo.Visible.HasValue) { size += stream.Write(osmGeo.Visible.Value); }
+
+ if (osmGeo.Tags == null ||
+ osmGeo.Tags.Count == 0)
+ {
+ size += stream.Write(0);
+ }
+ else
+ {
+ size += stream.Write(osmGeo.Tags.Count);
+ foreach (var t in osmGeo.Tags)
+ {
+ size += stream.WriteWithSize(t.Key);
+ size += stream.WriteWithSize(t.Value);
+ }
+ }
+
+ return size;
+ }
+
+ ///
+ /// Writes the given value to the stream.
+ ///
+ public static int Write(this Stream stream, int value)
+ {
+ stream.Write(BitConverter.GetBytes(value), 0, 4);
+ return 4;
+ }
+
+ private static int Write(this Stream stream, float value)
+ {
+ stream.Write(BitConverter.GetBytes(value), 0, 4);
+ return 4;
+ }
+
+ private static int Write(this Stream stream, double value)
+ {
+ stream.Write(BitConverter.GetBytes(value), 0, 8);
+ return 8;
+ }
+
+ private static int Write(this Stream stream, long value)
+ {
+ stream.Write(BitConverter.GetBytes(value), 0, 8);
+ return 8;
+ }
+
+ private static int Write(this Stream stream, ulong value)
+ {
+ stream.Write(BitConverter.GetBytes(value), 0, 8);
+ return 8;
+ }
+
+ private static int Write(this Stream stream, DateTime value)
+ {
+ stream.Write(BitConverter.GetBytes(value.Ticks), 0, 8);
+ return 8;
+ }
+
+ private static int Write(this Stream stream, bool value)
+ {
+ if (value)
+ {
+ stream.WriteByte(1);
+ }
+ else
+ {
+ stream.WriteByte(0);
+ }
+ return 1;
+ }
+
+ private static int WriteWithSize(this Stream stream, string value)
+ {
+ if (string.IsNullOrWhiteSpace(value))
+ {
+ stream.WriteByte(0);
+ return 1;
+ }
+ else
+ { // TODO: improve this based on the protobuf way of handling this kind of variable info.
+ var bytes = System.Text.Encoding.Unicode.GetBytes(value);
+ var position = 0;
+ while(bytes.Length - position >= 255)
+ { // write in blocks of 255.
+ stream.WriteByte(255);
+ stream.Write(bytes, position, 255);
+ position += 256; // data + size
+ }
+ stream.WriteByte((byte)(bytes.Length - position));
+ if (bytes.Length - position > 0)
+ {
+ stream.Write(bytes, position, bytes.Length - position);
+ }
+ return bytes.Length + 1;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/OsmSharp.IO.Binary.Test/v0_1/test-data/TestStreams.cs b/test/OsmSharp.IO.Binary.Test/v0_1/test-data/TestStreams.cs
new file mode 100644
index 0000000..d237d9a
--- /dev/null
+++ b/test/OsmSharp.IO.Binary.Test/v0_1/test-data/TestStreams.cs
@@ -0,0 +1,18 @@
+using System.IO;
+
+namespace OsmSharp.IO.Binary.Test.v0_1
+{
+ public static class TestStreams
+ {
+ public static Stream GetData1Stream()
+ {
+ return TestStreams.LoadAsStream("OsmSharp.IO.Binary.Test.v0_1.test_data.data1.osm.bin");
+ }
+
+ public static Stream LoadAsStream(string path)
+ {
+ return System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream(
+ path);
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/OsmSharp.IO.Binary.Test/v0_1/test-data/data1.osm.bin b/test/OsmSharp.IO.Binary.Test/v0_1/test-data/data1.osm.bin
new file mode 100644
index 0000000..4b42862
Binary files /dev/null and b/test/OsmSharp.IO.Binary.Test/v0_1/test-data/data1.osm.bin differ