diff --git a/GeoJSON.Tests/GeoJSON.Tests.csproj b/GeoJSON.Tests/GeoJSON.Tests.csproj
index b2ba915..a0af335 100644
--- a/GeoJSON.Tests/GeoJSON.Tests.csproj
+++ b/GeoJSON.Tests/GeoJSON.Tests.csproj
@@ -2,6 +2,11 @@
netcoreapp3.1
+ true
+
+
+
+ DEBUG;TRACE
diff --git a/GeoJSON.Tests/WkbUnitTests.cs b/GeoJSON.Tests/WkbUnitTests.cs
new file mode 100644
index 0000000..c8cafbe
--- /dev/null
+++ b/GeoJSON.Tests/WkbUnitTests.cs
@@ -0,0 +1,627 @@
+using BAMCIS.GeoJSON;
+using BAMCIS.GeoJSON.Wkb;
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using Xunit;
+
+namespace GeoJSON.Tests
+{
+ public class WkbUnitTests
+ {
+ #region WkbConverter Tests
+
+ [Fact]
+ public void WkbConverterTest_ToBinary_BigEndian()
+ {
+ // ARRANGE
+ byte[] expectedBytes = HexStringToByteArray("000000000140000000000000004010000000000000");
+ Point point = new Point(new Position(2.0, 4.0));
+
+ // ACT
+ byte[] bytes = WkbConverter.ToBinary(point, Endianness.BIG);
+
+ // ASSERT
+ Assert.Equal(expectedBytes, bytes);
+ }
+
+ [Fact]
+ public void WkbConverterTest_FromBinary_BigEndian()
+ {
+ // ARRANGE
+ byte[] bytes = HexStringToByteArray("000000000140000000000000004010000000000000");
+
+ // ACT
+ Point point = WkbConverter.FromBinary(bytes);
+
+ // ASSERT
+ Assert.Equal(2.0, point.GetLongitude());
+ Assert.Equal(4.0, point.GetLatitude());
+ }
+
+ #endregion
+
+ #region Point Tests
+
+ [Fact]
+ public void PointTest_Conversion()
+ {
+ // ARRANGE
+ Point point = new Point(new Position(10.0, 10.0));
+
+ // ACT
+ byte[] bytes = point.ToWkb();
+ Geometry geo = Point.FromWkb(bytes);
+
+ // ASSERT
+ point = Assert.IsType(geo);
+ }
+
+ [Fact]
+ public void PointTest_Conversion2()
+ {
+ // ARRANGE
+ Point point = new Point(new Position(10.0, 10.0));
+
+ // ACT
+ byte[] bytes = point.ToWkb();
+ point = Geometry.FromWkb(bytes);
+
+ // ASSERT
+ point = Assert.IsType(point);
+ Assert.Equal(10.0, point.GetLongitude());
+ Assert.Equal(10.0, point.GetLatitude());
+ }
+
+ [Fact]
+ public void PointTest_FromBinary_BigEndian()
+ {
+ // ARRANGE
+
+ // POINT(2.0 4.0) BIG ENDIAN
+ byte[] bytes = HexStringToByteArray("000000000140000000000000004010000000000000");
+
+ // ACT
+ Geometry geo = WkbConverter.FromBinary(bytes);
+
+ // ASSERT
+ Point point = Assert.IsType(geo);
+ Assert.Equal(2.0, point.Coordinates.Longitude);
+ Assert.Equal(4.0, point.Coordinates.Latitude);
+ }
+
+ [Fact]
+ public void PointTest_ToBinary_BigEndian()
+ {
+ // ARRANGE
+
+ // POINT(2.0 4.0) BIG ENDIAN
+ byte[] bytes = HexStringToByteArray("000000000140000000000000004010000000000000");
+
+ // ACT
+ Geometry geo = WkbConverter.FromBinary(bytes);
+ byte[] newBytes = WkbConverter.ToBinary(geo, Endianness.BIG);
+
+ // ASSERT
+ Assert.Equal(bytes, newBytes);
+ }
+
+ [Fact]
+ public void PointTest_FromBinary_LittleEndian()
+ {
+ // ARRANGE
+
+ // POINT(1.2345 2.3456) LITTLE ENDIAN
+ byte[] bytes = HexStringToByteArray("01010000008D976E1283C0F33F16FBCBEEC9C30240");
+
+ // ACT
+ Geometry geo = WkbConverter.FromBinary(bytes);
+
+ // ASSERT
+ Point point = Assert.IsType(geo);
+ Assert.Equal(1.2345, point.Coordinates.Longitude);
+ Assert.Equal(2.3456, point.Coordinates.Latitude);
+ }
+
+ [Fact]
+ public void PointTest_ToBinary_LittleEndian()
+ {
+ // ARRANGE
+
+ // POINT(1.2345 2.3456) LITTLE ENDIAN
+ byte[] bytes = HexStringToByteArray("01010000008D976E1283C0F33F16FBCBEEC9C30240");
+
+ // ACT
+ Geometry geo = WkbConverter.FromBinary(bytes);
+ byte[] newBytes = WkbConverter.ToBinary(geo, Endianness.LITTLE);
+
+ // ASSERT
+ Assert.Equal(bytes, newBytes);
+ }
+
+ #endregion
+
+ #region LineString Tests
+
+ [Fact]
+ public void LineStringTest_FromBinary_BigEndian()
+ {
+ // ARRANGE
+
+ // LINESTRING(30 10, 10 30, 40 40)
+ byte[] bytes = HexStringToByteArray("000000000200000003403E00000000000040240000000000004024000000000000403E00000000000040440000000000004044000000000000");
+
+ // ACT
+ Geometry geo = WkbConverter.FromBinary(bytes);
+
+ // ASSERT
+ LineString lineString = Assert.IsType(geo);
+ Assert.Equal(3, lineString.Coordinates.Count());
+ Assert.Equal(new Position(30, 10), lineString.Coordinates.ElementAt(0));
+ Assert.Equal(new Position(10, 30), lineString.Coordinates.ElementAt(1));
+ Assert.Equal(new Position(40, 40), lineString.Coordinates.ElementAt(2));
+ }
+
+ [Fact]
+ public void LineStringTest_FromBinary_LittleEndian()
+ {
+ // ARRANGE
+
+ // LINESTRING(30 10, 10 30, 40 40)
+ byte[] bytes = HexStringToByteArray("0102000000030000000000000000003e40000000000000244000000000000024400000000000003e4000000000000044400000000000004440");
+
+ // ACT
+ Geometry geo = WkbConverter.FromBinary(bytes);
+
+ // ASSERT
+ LineString lineString = Assert.IsType(geo);
+ Assert.Equal(3, lineString.Coordinates.Count());
+ Assert.Equal(new Position(30, 10), lineString.Coordinates.ElementAt(0));
+ Assert.Equal(new Position(10, 30), lineString.Coordinates.ElementAt(1));
+ Assert.Equal(new Position(40, 40), lineString.Coordinates.ElementAt(2));
+ }
+
+ [Fact]
+ public void LineStringTestWithDoubles_FromBinary_BigEndian()
+ {
+ // ARRANGE
+
+ // LINESTRING(30.1234 10.6, 10.77 30.85, 40.1 40.2, 21 07, 19 77)
+ byte[] bytes = HexStringToByteArray("000000000200000005403E1F972474538F402533333333333340258A3D70A3D70A403ED9999999999A40440CCCCCCCCCCD404419999999999A4035000000000000401C00000000000040330000000000004053400000000000");
+
+ // ACT
+ Geometry geo = WkbConverter.FromBinary(bytes);
+
+ // ASSERT
+ LineString lineString = Assert.IsType(geo);
+ Assert.Equal(5, lineString.Coordinates.Count());
+ Assert.Equal(new Position(30.1234, 10.6), lineString.Coordinates.ElementAt(0));
+ Assert.Equal(new Position(10.77, 30.85), lineString.Coordinates.ElementAt(1));
+ Assert.Equal(new Position(19, 77), lineString.Coordinates.ElementAt(4));
+ }
+
+ [Fact]
+ public void LineStringTest_ToBinary_BigEndian()
+ {
+ // ARRANGE
+
+ // LINESTRING(30 10, 10 30, 40 40)
+ byte[] bytes = HexStringToByteArray("000000000200000003403E00000000000040240000000000004024000000000000403E00000000000040440000000000004044000000000000");
+
+ // ACT
+ Geometry geo = WkbConverter.FromBinary(bytes);
+ byte[] newBytes = WkbConverter.ToBinary(geo, Endianness.BIG);
+
+ // ASSERT
+ Assert.Equal(bytes, newBytes);
+ }
+
+ [Fact]
+ public void LineStringTest_ToBinary_LittleEndian()
+ {
+ // ARRANGE
+
+ // LINESTRING(30 10, 10 30, 40 40)
+ byte[] bytes = HexStringToByteArray("0102000000030000000000000000003e40000000000000244000000000000024400000000000003e4000000000000044400000000000004440");
+
+ // ACT
+ Geometry geo = WkbConverter.FromBinary(bytes);
+ byte[] newBytes = WkbConverter.ToBinary(geo, Endianness.LITTLE);
+
+ // ASSERT
+ Assert.Equal(bytes, newBytes);
+ }
+
+ [Fact]
+ public void LineStringTestWithDoubles_ToBinary_BigEndian()
+ {
+ // ARRANGE
+
+ // LINESTRING(30.1234 10.6, 10.77 30.85, 40.1 40.2, 21 07, 19 77)
+ byte[] bytes = HexStringToByteArray("000000000200000005403E1F972474538F402533333333333340258A3D70A3D70A403ED9999999999A40440CCCCCCCCCCD404419999999999A4035000000000000401C00000000000040330000000000004053400000000000");
+
+ // ACT
+ Geometry geo = WkbConverter.FromBinary(bytes);
+ byte[] newBytes = WkbConverter.ToBinary(geo, Endianness.BIG);
+
+ // ASSERT
+ Assert.Equal(bytes, newBytes);
+ }
+
+ #endregion
+
+ #region MultiLineString Tests
+
+ [Fact]
+ public void MultiLineStringTest_FromBinary_BigEndian()
+ {
+ // ARRANGE
+
+ // MULTILINESTRING ((10 10, 20 20, 10 40), (40 40, 30 30, 40 20, 30 10))
+ byte[] bytes = HexStringToByteArray("00000000050000000200000000020000000340240000000000004024000000000000403400000000000040340000000000004024000000000000404400000000000000000000020000000440440000000000004044000000000000403E000000000000403E00000000000040440000000000004034000000000000403E0000000000004024000000000000");
+
+ // ACT
+ Geometry geo = WkbConverter.FromBinary(bytes);
+
+ // ASSERT
+ MultiLineString lineString = Assert.IsType(geo);
+ Assert.Equal(2, lineString.Coordinates.Count());
+ LineString ls1 = Assert.IsType(lineString.Coordinates.ElementAt(1));
+ Assert.Equal(40, ls1.Coordinates.ElementAt(2).Longitude);
+ Assert.Equal(20, ls1.Coordinates.ElementAt(2).Latitude);
+ }
+
+ [Fact]
+ public void MultiLineStringTest_FromBinary_LittleEndian()
+ {
+ // ARRANGE
+
+ // MULTILINESTRING((10 10,20 20,10 40),(40 40,30 30,40 20,30 10))
+ byte[] bytes = HexStringToByteArray("010500000002000000010200000003000000000000000000244000000000000024400000000000003440000000000000344000000000000024400000000000004440010200000004000000000000000000444000000000000044400000000000003e400000000000003e40000000000000444000000000000034400000000000003e400000000000002440");
+
+ // ACT
+ Geometry geo = WkbConverter.FromBinary(bytes);
+
+ // ASSERT
+ MultiLineString lineString = Assert.IsType(geo);
+ Assert.Equal(2, lineString.Coordinates.Count());
+ LineString ls1 = Assert.IsType(lineString.Coordinates.ElementAt(1));
+ Assert.Equal(40, ls1.Coordinates.ElementAt(2).Longitude);
+ Assert.Equal(20, ls1.Coordinates.ElementAt(2).Latitude);
+ }
+
+ [Fact]
+ public void MultiLineStringTest_ToBinary_BigEndian()
+ {
+ // ARRANGE
+
+ // MULTILINESTRING ((10 10, 20 20, 10 40), (40 40, 30 30, 40 20, 30 10))
+ byte[] bytes = HexStringToByteArray("00000000050000000200000000020000000340240000000000004024000000000000403400000000000040340000000000004024000000000000404400000000000000000000020000000440440000000000004044000000000000403E000000000000403E00000000000040440000000000004034000000000000403E0000000000004024000000000000");
+
+ // ACT
+ Geometry geo = WkbConverter.FromBinary(bytes);
+ byte[] newBytes = WkbConverter.ToBinary(geo, Endianness.BIG);
+
+ // ASSERT
+ Assert.Equal(bytes, newBytes);
+ }
+
+ [Fact]
+ public void MultiLineStringTest_ToBinary_LittleEndian()
+ {
+ // ARRANGE
+
+ // MULTILINESTRING((10 10,20 20,10 40),(40 40,30 30,40 20,30 10))
+ byte[] bytes = HexStringToByteArray("010500000002000000010200000003000000000000000000244000000000000024400000000000003440000000000000344000000000000024400000000000004440010200000004000000000000000000444000000000000044400000000000003e400000000000003e40000000000000444000000000000034400000000000003e400000000000002440");
+
+ // ACT
+ Geometry geo = WkbConverter.FromBinary(bytes);
+ byte[] newBytes = WkbConverter.ToBinary(geo, Endianness.LITTLE);
+
+ // ASSERT
+ Assert.Equal(bytes, newBytes);
+ }
+
+ #endregion
+
+ #region MultiPoint Tests
+
+ [Fact]
+ public void MultiPointTest_FromBinary_LittleEndian()
+ {
+ // ARRANGE
+
+ // MULTIPOINT(10 40,40 30,20 20,30 10)
+ byte[] bytes = HexStringToByteArray("010400000004000000010100000000000000000024400000000000004440010100000000000000000044400000000000003e4001010000000000000000003440000000000000344001010000000000000000003e400000000000002440");
+
+ // ACT
+ Geometry geo = WkbConverter.FromBinary(bytes);
+
+ // ASSERT
+ MultiPoint mp = Assert.IsType(geo);
+ }
+
+ [Fact]
+ public void MultiPointTest_FromBinary_BigEndian()
+ {
+ // ARRANGE
+
+ // MULTIPOINT ((21.06 19.77), (03.02 19.54), (40 20), (30 10))
+ byte[] bytes = HexStringToByteArray("000000000400000004000000000140350F5C28F5C28F4033C51EB851EB850000000001400828F5C28F5C2940338A3D70A3D70A0000000001404400000000000040340000000000000000000001403E0000000000004024000000000000");
+
+
+ // ACT
+ Geometry geo = WkbConverter.FromBinary(bytes);
+
+ // ASSERT
+ MultiPoint mp = Assert.IsType(geo);
+ Assert.Equal(21.06, mp.Coordinates.ElementAt(0).Longitude);
+ Assert.Equal(19.77, mp.Coordinates.ElementAt(0).Latitude);
+ }
+
+ [Fact]
+ public void MultiPointTest_ToBinary_BigEndian()
+ {
+ // ARRANGE
+
+ // MULTIPOINT ((21.06 19.77), (03.02 19.54), (40 20), (30 10))
+ byte[] bytes = HexStringToByteArray("000000000400000004000000000140350F5C28F5C28F4033C51EB851EB850000000001400828F5C28F5C2940338A3D70A3D70A0000000001404400000000000040340000000000000000000001403E0000000000004024000000000000");
+
+ // ACT
+ Geometry geo = WkbConverter.FromBinary(bytes);
+ byte[] newBytes = WkbConverter.ToBinary(geo, Endianness.BIG);
+
+ // ASSERT
+ Assert.Equal(bytes, newBytes);
+ }
+
+ [Fact]
+ public void MultiPointTest_ToBinary_LittleEndian()
+ {
+ // ARRANGE
+
+ // MULTIPOINT(10 40,40 30,20 20,30 10)
+ byte[] bytes = HexStringToByteArray("010400000004000000010100000000000000000024400000000000004440010100000000000000000044400000000000003e4001010000000000000000003440000000000000344001010000000000000000003e400000000000002440");
+
+ // ACT
+ Geometry geo = WkbConverter.FromBinary(bytes);
+ byte[] newBytes = WkbConverter.ToBinary(geo, Endianness.LITTLE);
+
+ // ASSERT
+ Assert.Equal(bytes, newBytes);
+ }
+
+ #endregion
+
+ #region MultiPolygon Tests
+
+ [Fact]
+ public void MultiPolygonTest_FromBinary_BigEndian()
+ {
+ // ARRANGE
+
+ // MULTIPOLYGON(((30 20, 45 40, 10 40, 30 20)), ((15 5, 40 10, 10 20, 5 10, 15 5)))
+ byte[] bytes = HexStringToByteArray("00000000060000000200000000030000000100000004403E00000000000040340000000000004046800000000000404400000000000040240000000000004044000000000000403E000000000000403400000000000000000000030000000100000005402E0000000000004014000000000000404400000000000040240000000000004024000000000000403400000000000040140000000000004024000000000000402E0000000000004014000000000000");
+
+ // ACT
+ Geometry geo = WkbConverter.FromBinary(bytes);
+
+ // ASSERT
+ MultiPolygon mp = Assert.IsType(geo);
+ }
+
+ [Fact]
+ public void MultiPolygonTest_FromBinary_LittleEndian()
+ {
+ // ARRANGE
+
+ // MULTIPOLYGON(((30 20, 45 40, 10 40, 30 20)), ((15 5, 40 10, 10 20, 5 10, 15 5)))
+ byte[] bytes = HexStringToByteArray("010600000002000000010300000001000000040000000000000000003e40000000000000344000000000008046400000000000004440000000000000244000000000000044400000000000003e400000000000003440010300000001000000050000000000000000002e4000000000000014400000000000004440000000000000244000000000000024400000000000003440000000000000144000000000000024400000000000002e400000000000001440");
+
+ // ACT
+ Geometry geo = WkbConverter.FromBinary(bytes);
+
+ // ASSERT
+ MultiPolygon mp = Assert.IsType(geo);
+ }
+
+ [Fact]
+ public void MultiPolygonTest_ToBinary_LittleEndian()
+ {
+ // ARRANGE
+
+ // MULTIPOLYGON(((30 20, 45 40, 10 40, 30 20)), ((15 5, 40 10, 10 20, 5 10, 15 5)))
+ byte[] bytes = HexStringToByteArray("010600000002000000010300000001000000040000000000000000003e40000000000000344000000000008046400000000000004440000000000000244000000000000044400000000000003e400000000000003440010300000001000000050000000000000000002e4000000000000014400000000000004440000000000000244000000000000024400000000000003440000000000000144000000000000024400000000000002e400000000000001440");
+
+ // ACT
+ Geometry geo = WkbConverter.FromBinary(bytes);
+ byte[] newBytes = WkbConverter.ToBinary(geo, Endianness.LITTLE);
+
+ // ASSERT
+ Assert.Equal(bytes, newBytes);
+ }
+
+ [Fact]
+ public void MultiPolygonTest_ToBinary_BigEndian()
+ {
+ // ARRANGE
+
+ // MULTIPOLYGON(((30 20, 45 40, 10 40, 30 20)), ((15 5, 40 10, 10 20, 5 10, 15 5)))
+ byte[] bytes = HexStringToByteArray("00000000060000000200000000030000000100000004403E00000000000040340000000000004046800000000000404400000000000040240000000000004044000000000000403E000000000000403400000000000000000000030000000100000005402E0000000000004014000000000000404400000000000040240000000000004024000000000000403400000000000040140000000000004024000000000000402E0000000000004014000000000000");
+
+ // ACT
+ Geometry geo = WkbConverter.FromBinary(bytes);
+ byte[] newBytes = WkbConverter.ToBinary(geo, Endianness.BIG);
+
+ // ASSERT
+ Assert.Equal(bytes, newBytes);
+ }
+
+ #endregion
+
+ #region Polygon Tests
+
+ [Fact]
+ public void PolygonTest_FromBinary_BigEndian()
+ {
+ // ARRANGE
+
+ // POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))
+ byte[] bytes = HexStringToByteArray("00000000030000000100000005403E0000000000004024000000000000404400000000000040440000000000004034000000000000404400000000000040240000000000004034000000000000403E0000000000004024000000000000");
+
+ // ACT
+ Geometry geo = WkbConverter.FromBinary(bytes);
+
+ // ASSERT
+ Polygon polygon = Assert.IsType(geo);
+ }
+
+ [Fact]
+ public void PolygonTest_FromBinary_LittleEndian()
+ {
+ // ARRANGE
+
+ // POLYGON((30 10,40 40,20 40,10 20,30 10))
+ byte[] bytes = HexStringToByteArray("010300000001000000050000000000000000003e4000000000000024400000000000004440000000000000444000000000000034400000000000004440000000000000244000000000000034400000000000003e400000000000002440");
+
+ // ACT
+ Geometry geo = WkbConverter.FromBinary(bytes);
+
+ // ASSERT
+ Polygon polygon = Assert.IsType(geo);
+ }
+
+ [Fact]
+ public void PolygonTest_ToBinary_LittleEndian()
+ {
+ // ARRANGE
+
+ // POLYGON((30 10,40 40,20 40,10 20,30 10))
+ byte[] bytes = HexStringToByteArray("010300000001000000050000000000000000003e4000000000000024400000000000004440000000000000444000000000000034400000000000004440000000000000244000000000000034400000000000003e400000000000002440");
+
+ // ACT
+ Geometry geo = WkbConverter.FromBinary(bytes);
+ byte[] newBytes = WkbConverter.ToBinary(geo, Endianness.LITTLE);
+
+ // ASSERT
+ Assert.Equal(bytes, newBytes);
+ }
+
+ [Fact]
+ public void PolygonTest_ToBinary_BigEndian()
+ {
+ // ARRANGE
+
+ // POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))
+ byte[] bytes = HexStringToByteArray("00000000030000000100000005403E0000000000004024000000000000404400000000000040440000000000004034000000000000404400000000000040240000000000004034000000000000403E0000000000004024000000000000");
+
+ // ACT
+ Geometry geo = WkbConverter.FromBinary(bytes);
+ byte[] newBytes = WkbConverter.ToBinary(geo, Endianness.BIG);
+
+ // ASSERT
+ Assert.Equal(bytes, newBytes);
+ }
+
+ #endregion
+
+ #region GeometryCollection Tests
+
+ [Fact]
+ public void GeometryCollectionTest_FromBinary_BigEndian()
+ {
+ // ARRANGE
+
+ // GEOMETRYCOLLECTION(POINT (40 10),LINESTRING(10 10, 20 20, 10 40),POLYGON((40 40, 20 45, 45 30, 40 40)))
+ byte[] bytes = HexStringToByteArray("0000000007000000030000000001404400000000000040240000000000000000000002000000034024000000000000402400000000000040340000000000004034000000000000402400000000000040440000000000000000000003000000010000000440440000000000004044000000000000403400000000000040468000000000004046800000000000403E00000000000040440000000000004044000000000000");
+
+ // ACT
+ Geometry geo = WkbConverter.FromBinary(bytes);
+
+ // ASSERT
+ GeometryCollection geoCollection = Assert.IsType(geo);
+ Point point = Assert.IsType(geoCollection.Geometries.ElementAt(0));
+ LineString lineString = Assert.IsType(geoCollection.Geometries.ElementAt(1));
+ Polygon polygon = Assert.IsType(geoCollection.Geometries.ElementAt(2));
+ Assert.Equal(40, point.Coordinates.Longitude);
+ Assert.Equal(10, point.Coordinates.Latitude);
+ }
+
+ [Fact]
+ public void GeometryCollectionTest_FromBinary_LittleEndian()
+ {
+ // ARRANGE
+
+ // GEOMETRYCOLLECTION(POINT(4 6),LINESTRING(4 6,7 10))
+ byte[] bytes = HexStringToByteArray("010700000002000000010100000000000000000010400000000000001840010200000002000000000000000000104000000000000018400000000000001c400000000000002440");
+
+ // ACT
+ Geometry geo = WkbConverter.FromBinary(bytes);
+
+ // ASSERT
+ GeometryCollection geoCollection = Assert.IsType(geo);
+ Point point = Assert.IsType(geoCollection.Geometries.ElementAt(0));
+ LineString lineString = Assert.IsType(geoCollection.Geometries.ElementAt(1));
+ Assert.Equal(4, point.Coordinates.Longitude);
+ Assert.Equal(6, point.Coordinates.Latitude);
+ }
+
+ [Fact]
+ public void GeometryCollectionTest_ToBinary_LittleEndian()
+ {
+ // ARRANGE
+
+ // GEOMETRYCOLLECTION(POINT(4 6),LINESTRING(4 6,7 10))
+ byte[] bytes = HexStringToByteArray("010700000002000000010100000000000000000010400000000000001840010200000002000000000000000000104000000000000018400000000000001c400000000000002440");
+
+ // ACT
+ Geometry geo = WkbConverter.FromBinary(bytes);
+ byte[] newBytes = WkbConverter.ToBinary(geo, Endianness.LITTLE);
+
+ // ASSERT
+ Assert.Equal(bytes, newBytes);
+ }
+
+ [Fact]
+ public void GeometryCollectionTest_ToBinary_BigEndian()
+ {
+ // ARRANGE
+
+ // GEOMETRYCOLLECTION(POINT (40 10),LINESTRING(10 10, 20 20, 10 40),POLYGON((40 40, 20 45, 45 30, 40 40)))
+ byte[] bytes = HexStringToByteArray("0000000007000000030000000001404400000000000040240000000000000000000002000000034024000000000000402400000000000040340000000000004034000000000000402400000000000040440000000000000000000003000000010000000440440000000000004044000000000000403400000000000040468000000000004046800000000000403E00000000000040440000000000004044000000000000");
+
+ // ACT
+ Geometry geo = WkbConverter.FromBinary(bytes);
+ byte[] newBytes = WkbConverter.ToBinary(geo, Endianness.BIG);
+
+ // ASSERT
+ Assert.Equal(bytes, newBytes);
+ }
+
+ #endregion
+
+ #region Private Methods
+
+ private static byte[] HexStringToByteArray(string hexString)
+ {
+ if (hexString.Length % 2 != 0)
+ {
+ throw new ArgumentException(String.Format(CultureInfo.InvariantCulture, "The binary key cannot have an odd number of digits: {0}", hexString));
+ }
+
+ byte[] data = new byte[hexString.Length / 2];
+
+ for (int index = 0; index < data.Length; index++)
+ {
+ string byteValue = hexString.Substring(index * 2, 2);
+ data[index] = byte.Parse(byteValue, NumberStyles.HexNumber, CultureInfo.InvariantCulture);
+ }
+
+ return data;
+ }
+
+ #endregion
+ }
+}
diff --git a/GeoJSON/Feature.cs b/GeoJSON/Feature.cs
index 67f9fbe..ca7fa78 100644
--- a/GeoJSON/Feature.cs
+++ b/GeoJSON/Feature.cs
@@ -1,6 +1,5 @@
using BAMCIS.GeoJSON.Serde;
using Newtonsoft.Json;
-using System;
using System.Collections.Generic;
using System.Linq;
diff --git a/GeoJSON/GeoJSON.csproj b/GeoJSON/GeoJSON.csproj
index 066b965..9184852 100644
--- a/GeoJSON/GeoJSON.csproj
+++ b/GeoJSON/GeoJSON.csproj
@@ -4,7 +4,7 @@
netstandard1.6;netstandard2.0;net45
1.6
BAMCIS.GeoJSON
- 2.2.0
+ 2.3.0
Michael Haken
bamcis.io
A .NET Core library for serializing and deserializing GeoJSON formatted JSON data. Complies with RFC 7946.
@@ -16,7 +16,7 @@
https://github.com/bamcis-io/GeoJSON
Git
GeoJSON RFC7946
- Added Id property for Feature.
+ Added Well-Known Binary serialization and deserialization support for `Geometry` objects.
true
GeoJSON.snk
false
diff --git a/GeoJSON/GeoJson.cs b/GeoJSON/GeoJson.cs
index edad540..155034c 100644
--- a/GeoJSON/GeoJson.cs
+++ b/GeoJSON/GeoJson.cs
@@ -1,4 +1,5 @@
using BAMCIS.GeoJSON.Serde;
+using BAMCIS.GeoJSON.Wkb;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
diff --git a/GeoJSON/Geometry.cs b/GeoJSON/Geometry.cs
index c2b113f..003a131 100644
--- a/GeoJSON/Geometry.cs
+++ b/GeoJSON/Geometry.cs
@@ -1,4 +1,5 @@
using BAMCIS.GeoJSON.Serde;
+using BAMCIS.GeoJSON.Wkb;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
@@ -62,6 +63,36 @@ protected Geometry(GeoJsonType type, bool is3D, IEnumerable boundingBox
return JsonConvert.DeserializeObject(json);
}
+ ///
+ /// Deserialize WKB byte array to Geometry.
+ ///
+ ///
+ ///
+ public static Geometry FromWkb(byte[] bytes)
+ {
+ return WkbConverter.FromBinary(bytes);
+ }
+
+ ///
+ /// Deserialize WKB byte array to Geometry.
+ ///
+ ///
+ ///
+ public static T FromWkb(byte[] bytes) where T : Geometry
+ {
+ return WkbConverter.FromBinary(bytes);
+ }
+
+ ///
+ /// Serialize this geometry object to WKB
+ ///
+ /// The default is LITTLE
+ ///
+ public byte[] ToWkb(Endianness endianness = Endianness.LITTLE)
+ {
+ return WkbConverter.ToBinary(this, endianness);
+ }
+
///
/// Gets the appropriate class type corresponding to the enum
/// representing the type
diff --git a/GeoJSON/Point.cs b/GeoJSON/Point.cs
index d66b701..4ce724e 100644
--- a/GeoJSON/Point.cs
+++ b/GeoJSON/Point.cs
@@ -12,7 +12,6 @@ namespace BAMCIS.GeoJSON
[JsonConverter(typeof(InheritanceBlockerConverter))]
public class Point : Geometry
{
-
#region Public Properties
///
diff --git a/GeoJSON/Wkb/EndianAwareBinaryReader.cs b/GeoJSON/Wkb/EndianAwareBinaryReader.cs
new file mode 100644
index 0000000..aed1c70
--- /dev/null
+++ b/GeoJSON/Wkb/EndianAwareBinaryReader.cs
@@ -0,0 +1,140 @@
+using System;
+using System.IO;
+using System.Text;
+
+namespace BAMCIS.GeoJSON.Wkb
+{
+ ///
+ /// An extension to the binary reader class that allows you to
+ /// specify the endianess of the binary data you are reading
+ ///
+ public class EndianAwareBinaryReader : BinaryReader
+ {
+ #region Private Fields
+
+ private Endianness _endianness = Endianness.LITTLE;
+
+ #endregion
+
+ #region Constructors
+
+ public EndianAwareBinaryReader(Stream input) : base(input)
+ {
+ }
+
+ public EndianAwareBinaryReader(Stream input, Encoding encoding) : base(input, encoding)
+ {
+ }
+
+ public EndianAwareBinaryReader(Stream input, Encoding encoding, bool leaveOpen) : base(input, encoding, leaveOpen)
+ {
+ }
+
+ public EndianAwareBinaryReader(Stream input, Endianness endianness) : base(input)
+ {
+ _endianness = endianness;
+ }
+
+ public EndianAwareBinaryReader(Stream input, Encoding encoding, Endianness endianness) : base(input, encoding)
+ {
+ _endianness = endianness;
+ }
+
+ public EndianAwareBinaryReader(Stream input, Encoding encoding, bool leaveOpen, Endianness endianness) : base(input, encoding, leaveOpen)
+ {
+ _endianness = endianness;
+ }
+
+ #endregion
+
+ #region Public Override Methods
+
+ public override short ReadInt16() => ReadInt16(_endianness);
+
+ public override int ReadInt32() => ReadInt32(_endianness);
+
+ public override long ReadInt64() => ReadInt64(_endianness);
+
+ public override ushort ReadUInt16() => ReadUInt16(_endianness);
+
+ public override uint ReadUInt32() => ReadUInt32(_endianness);
+
+ public override ulong ReadUInt64() => ReadUInt64(_endianness);
+
+ public override double ReadDouble() => ReadDouble(_endianness);
+
+ public override float ReadSingle() => ReadSingle(_endianness);
+
+ public override bool ReadBoolean() => ReadBoolean(_endianness);
+
+ public override char ReadChar() => ReadChar(_endianness);
+
+ #endregion
+
+ #region Public Methods
+
+ public void SetEndianness(Endianness endianness)
+ {
+ this._endianness = endianness;
+ }
+
+ public short ReadInt16(Endianness endianness) => BitConverter.ToInt16(ReadForEndianness(sizeof(short), endianness), 0);
+
+ public int ReadInt32(Endianness endianness) => BitConverter.ToInt32(ReadForEndianness(sizeof(int), endianness), 0);
+
+ public long ReadInt64(Endianness endianness) => BitConverter.ToInt64(ReadForEndianness(sizeof(long), endianness), 0);
+
+ public ushort ReadUInt16(Endianness endianness) => BitConverter.ToUInt16(ReadForEndianness(sizeof(ushort), endianness), 0);
+
+ public uint ReadUInt32(Endianness endianness) => BitConverter.ToUInt32(ReadForEndianness(sizeof(uint), endianness), 0);
+
+ public ulong ReadUInt64(Endianness endianness) => BitConverter.ToUInt64(ReadForEndianness(sizeof(ulong), endianness), 0);
+
+ public float ReadSingle(Endianness endianness) => BitConverter.ToSingle(ReadForEndianness(sizeof(float), endianness), 0);
+
+ public double ReadDouble(Endianness endianness)
+ {
+ byte[] temp = ReadForEndianness(sizeof(double), endianness);
+
+ try
+ {
+
+ return BitConverter.ToDouble(temp, 0);
+ }
+ catch (Exception e)
+ {
+ return 0;
+ }
+ }
+
+ public char ReadChar(Endianness endianness) => BitConverter.ToChar(ReadForEndianness(sizeof(char), endianness), 0);
+
+ public bool ReadBoolean(Endianness endianness) => BitConverter.ToBoolean(ReadForEndianness(sizeof(bool), endianness), 0);
+
+ #endregion
+
+ #region Private Methods
+
+ private byte[] ReadForEndianness(int bytesToRead, Endianness endianness)
+ {
+ try
+ {
+ byte[] bytesRead = this.ReadBytes(bytesToRead);
+
+ if ((endianness == Endianness.LITTLE && !BitConverter.IsLittleEndian)
+ || (endianness == Endianness.BIG && BitConverter.IsLittleEndian))
+ {
+ Array.Reverse(bytesRead);
+ }
+
+ return bytesRead;
+ }
+ catch (Exception e)
+ {
+ return null;
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/GeoJSON/Wkb/EndianAwareBinaryWriter.cs b/GeoJSON/Wkb/EndianAwareBinaryWriter.cs
new file mode 100644
index 0000000..7c60b74
--- /dev/null
+++ b/GeoJSON/Wkb/EndianAwareBinaryWriter.cs
@@ -0,0 +1,132 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+
+namespace BAMCIS.GeoJSON.Wkb
+{
+ public class EndianAwareBinaryWriter : BinaryWriter
+ {
+ #region Private Fields
+
+ private readonly Endianness _endianness = Endianness.LITTLE;
+
+ #endregion
+
+ #region Constructors
+
+ public EndianAwareBinaryWriter(Stream input) : base(input)
+ {
+ }
+
+ public EndianAwareBinaryWriter(Stream input, Encoding encoding) : base(input, encoding)
+ {
+ }
+
+ public EndianAwareBinaryWriter(Stream input, Encoding encoding, bool leaveOpen) : base(input, encoding, leaveOpen)
+ {
+ }
+
+ public EndianAwareBinaryWriter(Stream input, Endianness endianness) : base(input)
+ {
+ _endianness = endianness;
+ }
+
+ public EndianAwareBinaryWriter(Stream input, Encoding encoding, Endianness endianness) : base(input, encoding)
+ {
+ _endianness = endianness;
+ }
+
+ public EndianAwareBinaryWriter(Stream input, Encoding encoding, bool leaveOpen, Endianness endianness) : base(input, encoding, leaveOpen)
+ {
+ _endianness = endianness;
+ }
+
+ #endregion
+
+ #region Public Override Methods
+
+ public override void Write(byte value) => Write(value, this._endianness);
+
+ public override void Write(byte[] buffer) => Write(buffer, this._endianness);
+
+ public override void Write(char ch) => Write(ch, this._endianness);
+
+ public override void Write(double value) => Write(value, this._endianness);
+
+ public override void Write(float value) => Write(value, this._endianness);
+
+ public override void Write(int value) => Write(value, this._endianness);
+
+ public override void Write(long value) => Write(value, this._endianness);
+
+ public override void Write(short value) => Write(value, this._endianness);
+
+ public override void Write(uint value) => Write(value, this._endianness);
+
+ public override void Write(ushort value) => Write(value, this._endianness);
+
+ public override void Write(ulong value) => Write(value, this._endianness);
+
+ public override void Write(sbyte value) => Write(value, this._endianness);
+
+ public override void Write(bool value) => Write(value, this._endianness);
+
+ public override void Write(char[] chars) => Write(chars, this._endianness);
+ #endregion
+
+ #region Public Methods
+
+ public void Write(byte value, Endianness endianness) => this.WriteForEndianness(new byte[1] { value }, endianness);
+
+ public void Write(byte[] value, Endianness endianness) => this.WriteForEndianness(value, endianness);
+
+ public void Write(short value, Endianness endianness) => this.WriteForEndianness(BitConverter.GetBytes(value), endianness);
+
+ public void Write(int value, Endianness endianness) => this.WriteForEndianness(BitConverter.GetBytes(value), endianness);
+
+ public void Write(long value, Endianness endianness) => this.WriteForEndianness(BitConverter.GetBytes(value), endianness);
+
+ public void Write(float value, Endianness endianness) => this.WriteForEndianness(BitConverter.GetBytes(value), endianness);
+
+ public void Write(double value, Endianness endianness) => this.WriteForEndianness(BitConverter.GetBytes(value), endianness);
+
+ public void Write(char value, Endianness endianness) => this.WriteForEndianness(BitConverter.GetBytes(value), endianness);
+
+ public void Write(char[] chars, Endianness endianness) => this.WriteForEndianness(chars.Select(x => (byte)x).ToArray(), endianness);
+
+ public void Write(UInt16 value, Endianness endianness) => this.WriteForEndianness(BitConverter.GetBytes(value), endianness);
+
+ public void Write(UInt32 value, Endianness endianness) => this.WriteForEndianness(BitConverter.GetBytes(value), endianness);
+
+ public void Write(UInt64 value, Endianness endianness) => this.WriteForEndianness(BitConverter.GetBytes(value), endianness);
+
+ public void Write(bool value, Endianness endianness) => this.WriteForEndianness(BitConverter.GetBytes(value), endianness);
+
+ public void Write(sbyte value, Endianness endianness) => this.WriteForEndianness(BitConverter.GetBytes(value), endianness);
+
+ public void WriteEndianness() => WriteEndianness(this._endianness);
+
+ public void WriteEndianness(Endianness endianness) => this.Write((byte)endianness);
+
+ #endregion
+
+ #region Private Methods
+
+ private EndianAwareBinaryWriter WriteForEndianness(byte[] bytesToWrite, Endianness endianness)
+ {
+ if ((endianness == Endianness.LITTLE && !BitConverter.IsLittleEndian)
+ || (endianness == Endianness.BIG && BitConverter.IsLittleEndian))
+ {
+ Array.Reverse(bytesToWrite);
+ }
+
+ this.BaseStream.Write(bytesToWrite, 0, bytesToWrite.Length);
+
+ return this;
+ }
+
+ #endregion
+ }
+}
diff --git a/GeoJSON/Wkb/Endianness.cs b/GeoJSON/Wkb/Endianness.cs
new file mode 100644
index 0000000..2ea3903
--- /dev/null
+++ b/GeoJSON/Wkb/Endianness.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace BAMCIS.GeoJSON.Wkb
+{
+ public enum Endianness : byte
+ {
+ BIG = 0x00,
+ LITTLE = 0x01
+ }
+}
diff --git a/GeoJSON/Wkb/WkbConverter.cs b/GeoJSON/Wkb/WkbConverter.cs
new file mode 100644
index 0000000..10aab44
--- /dev/null
+++ b/GeoJSON/Wkb/WkbConverter.cs
@@ -0,0 +1,372 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+
+namespace BAMCIS.GeoJSON.Wkb
+{
+ public static class WkbConverter
+ {
+ #region Public Methods
+
+ ///
+ /// Returns the geometry of the specified type from WKB.
+ ///
+ /// The geometry type, i.e. Point, LineString, etc
+ ///
+ ///
+ public static T FromBinary(byte[] bytes) where T : Geometry
+ {
+ return (T)FromBinary(bytes);
+ }
+
+ ///
+ /// Returns the generic Geometry object from the WKB, but can be
+ /// casted to the specific underlying geometry object like Point, LineString,
+ /// Polygon, etc.
+ ///
+ ///
+ ///
+ public static Geometry FromBinary(byte[] bytes)
+ {
+ using (MemoryStream stream = new MemoryStream(bytes))
+ {
+ using (EndianAwareBinaryReader reader = new EndianAwareBinaryReader(stream))
+ {
+ return FromBinary(reader);
+ }
+ }
+ }
+
+ ///
+ /// Converts a geometry object to WKB based on what it's real underlying
+ /// type is, like Point or LineString.
+ ///
+ /// The geometry to serialize
+ /// The endianness to use during serialization, defaults to LITTLE endian.
+ ///
+ public static byte[] ToBinary(Geometry geo, Endianness endianness = Endianness.LITTLE)
+ {
+ using (MemoryStream stream = new MemoryStream())
+ {
+ using (EndianAwareBinaryWriter writer = new EndianAwareBinaryWriter(stream, endianness))
+ {
+ ToBinary(writer, geo);
+
+ return stream.ToArray();
+ }
+ }
+ }
+
+ #endregion
+
+ #region Private Methods
+
+ #region From Binary Methods
+
+ private static Geometry FromBinary(EndianAwareBinaryReader reader)
+ {
+ Endianness endianness = (Endianness)reader.ReadByte();
+ reader.SetEndianness(endianness);
+
+ WkbType type = (WkbType)reader.ReadUInt32();
+
+ switch (type)
+ {
+ case WkbType.Geometry:
+ {
+ return FromBinary(reader);
+ }
+ case WkbType.Point:
+ {
+ return PointFrom(reader);
+ }
+ case WkbType.LineString:
+ {
+ return LineStringFrom(reader);
+ }
+ case WkbType.Polygon:
+ {
+ return PolygonFrom(reader);
+ }
+ case WkbType.MultiPoint:
+ {
+ return MultiPointFrom(reader);
+ }
+ case WkbType.MultiLineString:
+ {
+ return MultiLineStringFrom(reader);
+ }
+ case WkbType.MultiPolygon:
+ {
+ return MultiPolygonFrom(reader);
+ }
+ case WkbType.GeometryCollection:
+ {
+ return GeometryCollectionFrom(reader);
+ }
+ default:
+ {
+ throw new NotSupportedException($"Unsupported WKB type {type.ToString()}.");
+ }
+ }
+ }
+
+ private static Point PointFrom(EndianAwareBinaryReader reader)
+ {
+ return new Point(new Position(reader.ReadDouble(), reader.ReadDouble()));
+ }
+
+ private static LineString LineStringFrom(EndianAwareBinaryReader reader)
+ {
+ UInt32 amount = reader.ReadUInt32();
+ List coordinates = new List();
+
+ for (int i = 0; i < amount; i++)
+ {
+ coordinates.Add(new Position(reader.ReadDouble(), reader.ReadDouble()));
+ }
+
+ return new LineString(coordinates);
+ }
+
+ private static Polygon PolygonFrom(EndianAwareBinaryReader reader)
+ {
+ int ringQuantity = reader.ReadInt32();
+ List rings = new List(ringQuantity);
+
+ for (int i = 0; i < ringQuantity; i++)
+ {
+ int numberOfPositions = reader.ReadInt32();
+ List coordinates = new List(numberOfPositions);
+ for (int j = 0; j < numberOfPositions; j++)
+ {
+ coordinates.Add(new Position(reader.ReadDouble(), reader.ReadDouble()));
+ }
+
+ rings.Add(new LinearRing(coordinates));
+ }
+
+ return new Polygon(rings);
+ }
+
+ private static MultiPoint MultiPointFrom(EndianAwareBinaryReader reader)
+ {
+ List coordinates = new List();
+ int numberOfGroups = reader.ReadInt32();
+
+ for (int i = 0; i < numberOfGroups; i++)
+ {
+ Endianness endianness = (Endianness)reader.ReadByte();
+
+ UInt32 numberOfPositions = reader.ReadUInt32((Endianness)endianness);
+
+ for (int j = 0; j < numberOfPositions; j++)
+ {
+ coordinates.Add(new Position(reader.ReadDouble(), reader.ReadDouble()));
+ }
+ }
+
+ return new MultiPoint(coordinates);
+ }
+
+ private static MultiLineString MultiLineStringFrom(EndianAwareBinaryReader reader)
+ {
+ List lineStrings = new List();
+ int quantityOfLineStrings = reader.ReadInt32();
+
+ for (int i = 0; i < quantityOfLineStrings; i++)
+ {
+ /*
+ Endianness endianness = (Endianness)reader.ReadByte();
+ WkbType type = (WkbType)reader.ReadUInt32(endianness);
+ lineStrings.Add(LineStringFrom(reader));
+ */
+
+ lineStrings.Add((LineString)FromBinary(reader));
+ }
+
+ return new MultiLineString(lineStrings);
+ }
+
+ private static MultiPolygon MultiPolygonFrom(EndianAwareBinaryReader reader)
+ {
+ List polygons = new List();
+ int quantityOfPolygons = reader.ReadInt32();
+
+ for (int i = 0; i < quantityOfPolygons; i++)
+ {
+ /*
+ Endianness endianness = (Endianness)reader.ReadByte();
+ WkbType type = (WkbType)reader.ReadUInt32(endianness);
+ polygons.Add(PolygonFrom(reader));
+ */
+
+ polygons.Add((Polygon)FromBinary(reader));
+ }
+
+ return new MultiPolygon(polygons);
+ }
+
+ private static GeometryCollection GeometryCollectionFrom(EndianAwareBinaryReader reader)
+ {
+ List geometries = new List();
+ int quantity = reader.ReadInt32();
+
+ for (int i = 0; i < quantity; i++)
+ {
+ geometries.Add(FromBinary(reader));
+ }
+
+ return new GeometryCollection(geometries);
+ }
+
+ #endregion
+
+ #region To Binary Methods
+
+ private static void ToBinary(EndianAwareBinaryWriter writer, Point point)
+ {
+ writer.WriteEndianness();
+ writer.Write((UInt32)WkbType.Point);
+ WritePosition(writer, point.Coordinates);
+ }
+
+ private static void ToBinary(EndianAwareBinaryWriter writer, LineString lineString)
+ {
+ writer.WriteEndianness();
+ writer.Write((UInt32)WkbType.LineString);
+ writer.Write(lineString.Coordinates.Count());
+
+ foreach (Position pos in lineString.Coordinates)
+ {
+ WritePosition(writer, pos);
+ }
+ }
+
+ private static void ToBinary(EndianAwareBinaryWriter writer, Polygon polygon)
+ {
+ writer.WriteEndianness();
+ writer.Write((UInt32)WkbType.Polygon);
+ writer.Write((UInt32)polygon.Coordinates.Count());
+
+ foreach (LinearRing linearRing in polygon.Coordinates)
+ {
+ writer.Write((UInt32)linearRing.Coordinates.Count());
+
+ foreach (Position pos in linearRing.Coordinates)
+ {
+ WritePosition(writer, pos);
+ }
+ }
+ }
+
+ private static void ToBinary(EndianAwareBinaryWriter writer, MultiPoint multiPoint)
+ {
+ writer.WriteEndianness();
+ writer.Write((UInt32)WkbType.MultiPoint);
+ writer.Write((UInt32)multiPoint.Coordinates.Count());
+
+ foreach (Position pos in multiPoint.Coordinates)
+ {
+ Point point = new Point(pos);
+ ToBinary(writer, point);
+ }
+ }
+
+ private static void ToBinary(EndianAwareBinaryWriter writer, MultiLineString multiLineString)
+ {
+ writer.WriteEndianness();
+ writer.Write((UInt32)WkbType.MultiLineString);
+ writer.Write((UInt32)multiLineString.Coordinates.Count());
+
+ foreach (LineString lineString in multiLineString.Coordinates)
+ {
+ ToBinary(writer, lineString);
+ }
+ }
+
+ private static void ToBinary(EndianAwareBinaryWriter writer, MultiPolygon multiPolygon)
+ {
+ writer.WriteEndianness();
+ writer.Write((UInt32)WkbType.MultiPolygon);
+ writer.Write((UInt32)multiPolygon.Coordinates.Count());
+
+ foreach (Polygon polygon in multiPolygon.Coordinates)
+ {
+ ToBinary(writer, polygon);
+ }
+ }
+
+ private static void ToBinary(EndianAwareBinaryWriter writer, GeometryCollection geometryCollection)
+ {
+ writer.WriteEndianness();
+ writer.Write((UInt32)WkbType.GeometryCollection);
+ writer.Write((UInt32)geometryCollection.Geometries.Count());
+
+ foreach (Geometry geometry in geometryCollection.Geometries)
+ {
+ ToBinary(writer, geometry);
+ }
+ }
+
+ private static void ToBinary(EndianAwareBinaryWriter writer, Geometry geometry)
+ {
+ switch (geometry.Type)
+ {
+ case GeoJsonType.Point:
+ {
+ ToBinary(writer, (Point)geometry);
+ break;
+ }
+ case GeoJsonType.LineString:
+ {
+ ToBinary(writer, (LineString)geometry);
+ break;
+ }
+ case GeoJsonType.Polygon:
+ {
+ ToBinary(writer, (Polygon)geometry);
+ break;
+ }
+ case GeoJsonType.MultiPoint:
+ {
+ ToBinary(writer, (MultiPoint)geometry);
+ break;
+ }
+ case GeoJsonType.MultiLineString:
+ {
+ ToBinary(writer, (MultiLineString)geometry);
+ break;
+ }
+ case GeoJsonType.MultiPolygon:
+ {
+ ToBinary(writer, (MultiPolygon)geometry);
+ break;
+ }
+ case GeoJsonType.GeometryCollection:
+ {
+ ToBinary(writer, (GeometryCollection)geometry);
+ break;
+ }
+ default:
+ {
+ throw new NotSupportedException($"The GeoJson type {geometry.Type.ToString()} is not supported for conversion to WKB.");
+ }
+ }
+ }
+
+ private static EndianAwareBinaryWriter WritePosition(EndianAwareBinaryWriter writer, Position position)
+ {
+ writer.Write(position.Longitude);
+ writer.Write(position.Latitude);
+
+ return writer;
+ }
+
+ #endregion
+
+ #endregion
+ }
+}
diff --git a/GeoJSON/Wkb/WkbType.cs b/GeoJSON/Wkb/WkbType.cs
new file mode 100644
index 0000000..318c571
--- /dev/null
+++ b/GeoJSON/Wkb/WkbType.cs
@@ -0,0 +1,128 @@
+using System;
+
+namespace BAMCIS.GeoJSON.Wkb
+{
+ ///
+ /// The different geometry types available in WKB
+ ///
+ public enum WkbType : UInt32
+ {
+ // 2D
+ Geometry = 0000,
+ Point = 0001,
+ LineString = 0002,
+ Polygon = 0003,
+ MultiPoint = 0004,
+ MultiLineString = 0005,
+ MultiPolygon = 0006,
+ GeometryCollection = 0007,
+ CircularString = 0008,
+ CompoundCurve = 0009,
+ CurvePolygon = 0010,
+ MultiCurve = 0011,
+ MultiSurface = 0012,
+ Curve = 0013,
+ Surface = 0014,
+ PolyhedralSurface = 0015,
+ TIN = 0016,
+ Triangle = 0017,
+ Circle = 0018,
+ GeodesicString = 0019,
+ EllipticalCurve = 0020,
+ NurbsCurve = 0021,
+ Clothoid = 0022,
+ SpiralCurve = 0023,
+ CompoundSurface = 0024,
+ BrepSolid = 0025,
+ AffinePlacement = 0102,
+
+ // Z
+ Z_Geometry = 1000,
+ Z_Point = 1001,
+ Z_LineString = 1002,
+ Z_Polygon = 1003,
+ Z_MultiPoint = 1004,
+ Z_MultiLineString = 1005,
+ Z_MultiPolygon = 1006,
+ Z_GeometryCollection = 1007,
+ Z_CircularString = 1008,
+ Z_CompoundCurve = 1009,
+ Z_CurvePolygon = 1010,
+ Z_MultiCurve = 1011,
+ Z_MultiSurface = 1012,
+ Z_Curve = 1013,
+ Z_Surface = 1014,
+ Z_PolyhedralSurface = 1015,
+ Z_TIN = 1016,
+ Z_Triangle = 1017,
+ Z_Circle = 1018,
+ Z_GeodesicString = 1019,
+ Z_EllipticalCurve = 1020,
+ Z_NurbsCurve = 1021,
+ Z_Clothoid = 1022,
+ Z_SpiralCurve = 1023,
+ Z_CompoundSurface = 1024,
+ Z_BrepSolid = 1025,
+ Z_AffinePlacement = 1102,
+
+
+ // M
+ M_Geometry = 2000,
+ M_Point = 2001,
+ M_LineString = 2002,
+ M_Polygon = 2003,
+ M_MultiPoint = 2004,
+ M_MultiLineString = 2005,
+ M_MultiPolygon = 2006,
+ M_GeometryCollection = 2007,
+ M_CircularString = 2008,
+ M_CompoundCurve = 2009,
+ M_CurvePolygon = 2010,
+ M_MultiCurve = 2011,
+ M_MultiSurface = 2012,
+ M_Curve = 2013,
+ M_Surface = 2014,
+ M_PolyhedralSurface = 2015,
+ M_TIN = 2016,
+ M_Triangle = 2017,
+ M_Circle = 2018,
+ M_GeodesicString = 2019,
+ M_EllipticalCurve = 2020,
+ M_NurbsCurve = 2021,
+ M_Clothoid = 2022,
+ M_SpiralCurve = 2023,
+ M_CompoundSurface = 2024,
+ M_BrepSolid = 2025,
+ M_AffinePlacement = 2102,
+
+
+ // ZM
+ ZM_Geometry = 3000,
+ ZM_Point = 3001,
+ ZM_LineString = 3002,
+ ZM_Polygon = 3003,
+ ZM_MultiPoint = 3004,
+ ZM_MultiLineString = 3005,
+ ZM_MultiPolygon = 3006,
+ ZM_GeometryCollection = 3007,
+ ZM_CircularString = 3008,
+ ZM_CompoundCurve = 3009,
+ ZM_CurvePolygon = 3010,
+ ZM_MultiCurve = 3011,
+ ZM_MultiSurface = 3012,
+ ZM_Curve = 3013,
+ ZM_Surface = 3014,
+ ZM_PolyhedralSurface = 3015,
+ ZM_TIN = 3016,
+ ZM_Triangle = 3017,
+ ZM_Circle = 3018,
+ ZM_GeodesicString = 3019,
+ ZM_EllipticalCurve = 3020,
+ ZM_NurbsCurve = 3021,
+ ZM_Clothoid = 3022,
+ ZM_SpiralCurve = 3023,
+ ZM_CompoundSurface = 3024,
+ ZM_BrepSolid = 3025,
+ ZM_AffinePlacement = 3102
+ }
+}
diff --git a/README.md b/README.md
index b9f3bc7..4428e24 100644
--- a/README.md
+++ b/README.md
@@ -7,6 +7,7 @@ An implementation of GeoJSON written in .NET Core 2.0. The library complies with
* [Example 1](#example-1)
* [Example 2](#example-2)
* [Example 3](#example-3)
+ * [Example 4](#example-4)
* [Usage Notes](#usage-notes)
* [Global Configuration](#global-configuration)
- [Revision History](#revision-history)
@@ -78,6 +79,52 @@ MultiPoint mp = new MultiPoint(new Position[] {pos1, pos2});
string json = JsonConvert.Serialize(mp);
```
+### Example 4
+The library also supports conversion of `Geometry` objects to and from Well-Known Binary (WKB). For example:
+
+```json
+{
+ "type": "Point",
+ "coordinates": [ 2.0, 4.0 ]
+}
+```
+
+```csharp
+Point point = new Point(102.0, 0.5);
+byte[] wkb = point.ToWkb();
+point = Geometry.FromWkb(wkb);
+```
+
+The binary produced is `0x000000000140000000000000004010000000000000`. You can also convert this way.
+
+```csharp
+Point point = new Point(102.0, 0.5);
+byte[] wkb = point.ToWkb();
+Geometry geo = Point.FromWkb(wkb)
+point = (Point)geo;
+```
+
+You can also specify the endianness of the binary encoding (the default is LITTLE).
+
+```csharp
+Point point = new Point(102.0, 0.5);
+byte[] wkb = point.ToWkb(Endianness.BIG);
+Geometry geo = Point.FromWkb(wkb)
+point = (Point)geo;
+```
+
+Finally, you can use the `WkbConverter` class directly.
+
+```csharp
+Point point = new Point(new Position(2.0, 4.0));
+byte[] bytes = WkbConverter.ToBinary(point, Endianness.BIG);
+```
+
+```csharp
+byte[] bytes = HexStringToByteArray("000000000140000000000000004010000000000000");
+Point point = WkbConverter.FromBinary(bytes);
+```
+
### Usage Notes
Each of the 9 GeoJSON types: **Feature**, **FeatureCollection**, **GeometryCollection**, **LineString**, **MultiLineString**, **MultiPoint**, **MultiPolygon**, **Point**, and **Polygon** all have convenience methods ToJson() and FromJson() to make serialization and deserialization easy.
@@ -118,6 +165,9 @@ Feature geo = JsonConvert.DeserializeObject(content);
## Revision History
+### 2.3.0
+Added Well-Known Binary serialization and deserialization support for `Geometry` objects.
+
### 2.2.0
Added an Id property to in `Feature`. Also added a global config object that can be used to ignore validation of coordinate values.