diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..f14019e
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,111 @@
+[*.cs]
+
+# CA2208: Instanciar exceções de argumentos corretamente
+dotnet_diagnostic.CA2208.severity = none
+
+# IDE0059: Atribuição desnecessária de um valor
+dotnet_diagnostic.IDE0059.severity = none
+
+[*.cs]
+#### Estilos de nomenclatura ####
+
+# Regras de nomenclatura
+
+dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
+dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
+dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
+
+dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
+dotnet_naming_rule.types_should_be_pascal_case.symbols = types
+dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
+
+dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
+dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
+dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
+
+# Especificações de símbolo
+
+dotnet_naming_symbols.interface.applicable_kinds = interface
+dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.interface.required_modifiers =
+
+dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
+dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.types.required_modifiers =
+
+dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
+dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.non_field_members.required_modifiers =
+
+# Estilos de nomenclatura
+
+dotnet_naming_style.begins_with_i.required_prefix = I
+dotnet_naming_style.begins_with_i.required_suffix =
+dotnet_naming_style.begins_with_i.word_separator =
+dotnet_naming_style.begins_with_i.capitalization = pascal_case
+
+dotnet_naming_style.pascal_case.required_prefix =
+dotnet_naming_style.pascal_case.required_suffix =
+dotnet_naming_style.pascal_case.word_separator =
+dotnet_naming_style.pascal_case.capitalization = pascal_case
+
+dotnet_naming_style.pascal_case.required_prefix =
+dotnet_naming_style.pascal_case.required_suffix =
+dotnet_naming_style.pascal_case.word_separator =
+dotnet_naming_style.pascal_case.capitalization = pascal_case
+csharp_style_expression_bodied_methods = false:silent
+csharp_style_expression_bodied_constructors = false:silent
+csharp_style_expression_bodied_operators = false:silent
+csharp_style_expression_bodied_properties = true:silent
+csharp_style_expression_bodied_indexers = true:silent
+csharp_style_expression_bodied_accessors = true:silent
+csharp_style_expression_bodied_lambdas = true:silent
+csharp_style_expression_bodied_local_functions = false:silent
+
+[*.vb]
+#### Estilos de nomenclatura ####
+
+# Regras de nomenclatura
+
+dotnet_naming_rule.interface_should_be_começa_com_i.severity = suggestion
+dotnet_naming_rule.interface_should_be_começa_com_i.symbols = interface
+dotnet_naming_rule.interface_should_be_começa_com_i.style = começa_com_i
+
+dotnet_naming_rule.tipos_should_be_pascal_case.severity = suggestion
+dotnet_naming_rule.tipos_should_be_pascal_case.symbols = tipos
+dotnet_naming_rule.tipos_should_be_pascal_case.style = pascal_case
+
+dotnet_naming_rule.membros_sem_campo_should_be_pascal_case.severity = suggestion
+dotnet_naming_rule.membros_sem_campo_should_be_pascal_case.symbols = membros_sem_campo
+dotnet_naming_rule.membros_sem_campo_should_be_pascal_case.style = pascal_case
+
+# Especificações de símbolo
+
+dotnet_naming_symbols.interface.applicable_kinds = interface
+dotnet_naming_symbols.interface.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected
+dotnet_naming_symbols.interface.required_modifiers =
+
+dotnet_naming_symbols.tipos.applicable_kinds = class, struct, interface, enum
+dotnet_naming_symbols.tipos.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected
+dotnet_naming_symbols.tipos.required_modifiers =
+
+dotnet_naming_symbols.membros_sem_campo.applicable_kinds = property, event, method
+dotnet_naming_symbols.membros_sem_campo.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected
+dotnet_naming_symbols.membros_sem_campo.required_modifiers =
+
+# Estilos de nomenclatura
+
+dotnet_naming_style.começa_com_i.required_prefix = I
+dotnet_naming_style.começa_com_i.required_suffix =
+dotnet_naming_style.começa_com_i.word_separator =
+dotnet_naming_style.começa_com_i.capitalization = pascal_case
+
+dotnet_naming_style.pascal_case.required_prefix =
+dotnet_naming_style.pascal_case.required_suffix =
+dotnet_naming_style.pascal_case.word_separator =
+dotnet_naming_style.pascal_case.capitalization = pascal_case
+
+dotnet_naming_style.pascal_case.required_prefix =
+dotnet_naming_style.pascal_case.required_suffix =
+dotnet_naming_style.pascal_case.word_separator =
+dotnet_naming_style.pascal_case.capitalization = pascal_case
diff --git a/Extensions/ArrayExtensions/DoubleFloatArray_AlgebraExtensions.cs b/Extensions/ArrayExtensions/DoubleFloatArray_AlgebraExtensions.cs
new file mode 100644
index 0000000..a72cd35
--- /dev/null
+++ b/Extensions/ArrayExtensions/DoubleFloatArray_AlgebraExtensions.cs
@@ -0,0 +1,334 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Numerics;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Extensions.ArrayExtensions
+{
+
+
+ public static class DoubleFloatArray_AlgebraExtensions
+ {
+ public static double Sum(this double[] array)
+ {
+ double result = 0;
+
+ for (int i = 0; i < array.Length; i++)
+ {
+
+ result += array[i];
+ }
+
+ return result;
+
+ }
+
+ public static double[] Sum(this double[] array, double[] other)
+ {
+ double[] result = new double[array.Length];
+
+ for (int i = 0; i < array.Length; i++)
+ {
+ result[i] = array[i] + other[i];
+ }
+
+ return result;
+
+ }
+
+ public static double[] Subtract(this double[] array, double[] other)
+ {
+ double[] result = new double[array.Length];
+
+ for (int i = 0; i < array.Length; i++)
+ {
+ result[i] = array[i] - other[i];
+ }
+
+ return result;
+
+ }
+
+ public static double Mean(this double[] array)
+ {
+ return array.Sum() / array.Count();
+ }
+
+ ///
+ /// Returns the module of the array.
+ ///
+ /// The module of an array is mathematically equivalent to its own dot product.
+ ///
+
+ ///
+ /// The Modulus (magnitude) of an array is equivalent to the squared root of the summation of its own elementwise multiplication.
+ ///
+ /// Assume a 3D array V1: {-1,2,5}.
+ ///
+ ///
+ /// The module of this Array will be: 1 + 4 + 25 = √30.
+ ///
+ ///
+ /// Equivalently, the magnitude of an array is equivalent to the squared root of its own Dot Product, and thus, it could be written as: √(V1 . V1)
+ ///
+ ///
+ ///
+ /// the Module of the array
+ public static double Magnitude(this double[] array)
+ {
+ var magnitude = array.DotProduct(array);
+
+ return Math.Sqrt(magnitude);
+
+ }
+
+ ///
+ /// Alias for Magnitude:
+ ///
+ ///
+ /// The Modulus (magnitude) of an array is equivalent to the squared root of the summation of its own elementwise multiplication.
+ ///
+ /// Assume a 3D array V1: {-1,2,5}.
+ ///
+ ///
+ /// The module of this Array will be: 1 + 4 + 25 = √30.
+ ///
+ ///
+ /// Equivalently, the magnitude of an array is equivalent to the squared root of its own Dot Product, and thus, it could be written as: √(V1 . V1)
+ ///
+ ///
+ ///
+ /// the magnitude of the array
+ public static double Module(this double[] array)
+ {
+ return array.Magnitude();
+
+ }
+
+ ///
+ /// ElementWise multiplication between two arrays.
+ ///
+ /// Assume two 3D vectors:
+ /// V1) {1,2,3}
+ /// V2) {4,5,6};
+ ///
+ /// the returned multiplication will be:
+ /// V3) {4,10,18}
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static double[] Multiply(this double[] array, double[] other)
+ {
+ var arrayResult = new double[array.Length];
+ for (int i = 0; i < array.Length; i++)
+ {
+ arrayResult[i] = array[i] * other[i];
+ }
+
+ return arrayResult;
+
+ }
+
+ ///
+ /// ElementWise multiplication between a constant and an array.
+ ///
+ /// Assume two 3D vectors:
+ /// V1) {1,2,3}
+ /// V2) {4,5,6};
+ ///
+ /// the returned multiplication will be:
+ /// V3) {4,10,18}
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static double[] Multiply(this double[] array, double constant)
+ {
+ var arrayResult = new double[array.Length];
+ for (int i = 0; i < array.Length; i++)
+ {
+ arrayResult[i] = array[i] * constant;
+ }
+
+ return arrayResult;
+
+ }
+
+ ///
+ /// ElementWise division between two arrays.
+ ///
+ /// Assume two 3D vectors:
+ /// V1) {1,2,3}
+ /// V2) {4,5,6};
+ ///
+ /// the returned multiplication will be:
+ /// V3) {4,10,18}
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static double[] Divide(this double[] array, double[] other)
+ {
+ var arrayResult = new double[array.Length];
+ for (int i = 0; i < array.Length; i++)
+ {
+ arrayResult[i] = array[i] / other[i];
+ }
+
+ return arrayResult;
+
+ }
+
+
+ ///
+ /// ElementWise division between a constant and an array.
+ ///
+ /// Assume two 3D vectors:
+ /// V1) {1,2,3}
+ /// V2) {4,5,6};
+ ///
+ /// the returned multiplication will be:
+ /// V3) {4,10,18}
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static double[] Divide(this double[] array, double constant)
+ {
+ var arrayResult = new double[array.Length];
+ for (int i = 0; i < array.Length; i++)
+ {
+ arrayResult[i] = array[i] / constant;
+ }
+
+ return arrayResult;
+
+ }
+
+ ///
+ /// Dot Product of two arrays (scalar multiplication).
+ ///
+ ///
+ ///
+ ///
+ public static double DotProduct(this double[] array, double[] other)
+ {
+ var arrayResult = array.Multiply(other).Sum();
+
+ return arrayResult;
+
+ }
+
+
+ public static double Determinant2D(double[] ab, double[] cd)
+ {
+ if (ab.Length != 2 || cd.Length != 2)
+ {
+ throw new InvalidOperationException("The provided arrays must of length 2");
+ }
+
+ return ( ab[0] * cd[1] ) - ( cd[0] * ab[1] );
+ }
+
+ public static double[] Determinant3D(double[] a, double[] b)
+ {
+ if (a.Length != 3 || b.Length != 3)
+ {
+ throw new InvalidOperationException("The provided arrays must of length 2");
+ }
+
+ double a2b3 = a[1] * b[2];
+ double a3b2 = a[2] * b[1];
+ double a3b1 = a[2] * b[0];
+ double a1b3 = a[0] * b[2];
+ double a1b2 = a[0] * b[1];
+ double a2b1 = a[1] * b[0];
+
+ return new double[] { a2b3 - a3b2,
+ a3b1 - a1b3,
+ a1b2 - a2b1 };
+ }
+
+ ///
+ /// Cross-Product two 3D arrays (vector product).
+ ///
+ ///
+ ///
+ ///
+ public static double[] CrossProduct3D(this double[] array, double[] other)
+ {
+ double[] crossProduct = new double[3] { 1, 1, 1 };
+ double[] a = new double[3] { 1, 1, 1 };
+ double[] b = new double[3] { 1, 1, 1 };
+
+ for (int i = 0; i < array.Count(); i++)
+ {
+ a[i] = array[i];
+ b[i] = other[i];
+ }
+
+
+ if (array.Length != 2)
+ {
+ throw new InvalidOperationException("The provided arrays must of length 2");
+ }
+
+ else
+ {
+ if (a.Length != 3 || b.Length != 3)
+ {
+ throw new InvalidOperationException("The provided arrays must of length 2");
+ }
+
+ double a2b3 = a[1] * b[2];
+ double a3b2 = a[2] * b[1];
+ double a3b1 = a[2] * b[0];
+ double a1b3 = a[0] * b[2];
+ double a1b2 = a[0] * b[1];
+ double a2b1 = a[1] * b[0];
+
+
+ return new double[] {a2b3 - a3b2, a1b3 - a3b1, a1b2 - a2b1 };
+
+ }
+ }
+
+ ///
+ /// Cross-Product two 2D arrays (vector product).
+ ///
+ ///
+ ///
+ /// Double
+ public static double CrossProduct2D(this double[] array, double[] other)
+ {
+ double result = array[0]*other[1] - array[1]*other[0];
+
+ return result;
+ }
+
+ public static double[] GetUnitVector(this double[] array)
+ {
+ return array.Divide(array.Magnitude());
+ }
+
+ ///
+ /// Returns the angle between two arrays
+ ///
+ ///
+ ///
+ ///
+ public static double Angle(this double[] array, double[] other)
+ {
+ var theta = array.DotProduct(other) / ( array.Magnitude() * other.Magnitude() );
+
+ return Math.Acos(theta);
+ }
+ }
+}
diff --git a/Extensions/Extensions.csproj b/Extensions/Extensions.csproj
new file mode 100644
index 0000000..7746344
--- /dev/null
+++ b/Extensions/Extensions.csproj
@@ -0,0 +1,7 @@
+
+
+ net7.0
+ enable
+ enable
+
+
\ No newline at end of file
diff --git a/Extensions/ListExtensions/ListExtensions.cs b/Extensions/ListExtensions/ListExtensions.cs
new file mode 100644
index 0000000..06f3c13
--- /dev/null
+++ b/Extensions/ListExtensions/ListExtensions.cs
@@ -0,0 +1,41 @@
+
+namespace Extensions.ListExtensions
+{
+ public static class ListExtensions
+ {
+ public static T PopAt(this List list, int index)
+ {
+ var r = list[index];
+ list.RemoveAt(index);
+ return r;
+ }
+
+ public static T Pop(this List list)
+ {
+ var r = list[0];
+ list.RemoveAt(0);
+ return r;
+ }
+
+ public static T PopFirst(this List list, Predicate predicate)
+ {
+ var index = list.FindIndex(predicate);
+ var r = list[index];
+ list.RemoveAt(index);
+ return r;
+ }
+
+ public static T? PopFirstOrDefault(this List list, Predicate predicate) where T : class
+ {
+ var index = list.FindIndex(predicate);
+ if (index > -1)
+ {
+ var r = list[index];
+ list.RemoveAt(index);
+ return r;
+ }
+ return null;
+ }
+
+ }
+}
diff --git a/GeoJSON.Tests/GeoJSON.Tests.csproj b/GeoJSON.Tests/GeoJSON.Tests.csproj
index a0af335..e66facd 100644
--- a/GeoJSON.Tests/GeoJSON.Tests.csproj
+++ b/GeoJSON.Tests/GeoJSON.Tests.csproj
@@ -1,83 +1,18 @@
-
- netcoreapp3.1
+ net7.0
true
-
DEBUG;TRACE
-
-
-
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
-
-
+
\ No newline at end of file
diff --git a/GeoJSON.Tests/GeoJsonUnitTests.cs b/GeoJSON.Tests/GeoJsonUnitTests.cs
deleted file mode 100644
index 8c6a047..0000000
--- a/GeoJSON.Tests/GeoJsonUnitTests.cs
+++ /dev/null
@@ -1,371 +0,0 @@
-using BAMCIS.GeoJSON;
-using Newtonsoft.Json;
-using System;
-using System.IO;
-using Xunit;
-
-namespace GeoJSON.Tests
-{
- public class GeoJsonUnitTests
- {
- [Fact]
- public void MultiLineStringTest()
- {
- // ARRANGE
- string content = File.ReadAllText("multilinestring.json").Replace("\r", "").Replace("\n", "").Replace("\t", "").Replace(" ", "");
-
- // ACT
- MultiLineString geo = MultiLineString.FromJson(content);
-
- string content2 = geo.ToJson();
-
- MultiLineString geo2 = MultiLineString.FromJson(content2);
-
- // ASSERT
- Assert.True(geo.Equals(geo2));
- }
-
- [Fact]
- public void LineStringTest()
- {
- // ARRANGE
- string content = File.ReadAllText("linestring.json").Replace("\r", "").Replace("\n", "").Replace("\t", "").Replace(" ", "");
-
- // ACT
- LineString geo = JsonConvert.DeserializeObject(content);
- string content2 = JsonConvert.SerializeObject(geo);
- LineString geo2 = JsonConvert.DeserializeObject(content2);
-
- // ASSERT
- Assert.True(geo.Equals(geo2));
- }
-
- [Fact]
- public void MultiPolygonTest()
- {
- // ARRANGE
- string content = File.ReadAllText("multipolygon.json").Replace("\r", "").Replace("\n", "").Replace("\t", "").Replace(" ", "");
-
- // ACT
- MultiPolygon geo = JsonConvert.DeserializeObject(content);
- string content2 = JsonConvert.SerializeObject(geo);
-
- // ASSERT
- Assert.Equal(content, content2, true, true, true);
- }
-
- [Fact]
- public void FeatureTest()
- {
- // ARRANGE
- string content = File.ReadAllText("feature.json").Replace("\r", "").Replace("\n", "").Replace("\t", "").Replace(" ", "");
-
- // ACT
- Feature geo = JsonConvert.DeserializeObject(content);
- string content2 = JsonConvert.SerializeObject(geo);
- Feature geo2 = JsonConvert.DeserializeObject(content2);
-
- // ASSERT
- Assert.True(geo.Equals(geo2));
- }
-
- [Fact]
- public void FeatureIdNotEqualTest()
- {
- // ARRANGE
- string content1 = File.ReadAllText("feature_id_number.json").Replace("\r", "").Replace("\n", "").Replace("\t", "").Replace(" ", "");
- string content2 = File.ReadAllText("feature_id_num_as_string.json").Replace("\r", "").Replace("\n", "").Replace("\t", "").Replace(" ", "");
-
- // ACT
- Feature geoWithNumId = JsonConvert.DeserializeObject(content1);
- Feature geoWithStringId = JsonConvert.DeserializeObject(content2);
-
- // ASSERT
- Assert.Equal(geoWithNumId.Id.Value, geoWithStringId.Id.Value);
- Assert.NotEqual(geoWithNumId.Id, geoWithStringId.Id);
- }
-
- [Fact]
- public void FeatureIdEqualTest()
- {
- // ARRANGE
- string content = File.ReadAllText("feature_id_number.json").Replace("\r", "").Replace("\n", "").Replace("\t", "").Replace(" ", "");
-
- // ACT
- Feature geo1 = JsonConvert.DeserializeObject(content);
- Feature geo2 = JsonConvert.DeserializeObject(content);
-
- // ASSERT
- Assert.Equal(geo1.Id.Value, geo2.Id.Value);
- Assert.Equal(geo1.Id, geo2.Id);
- }
-
- [Fact]
- public void FeatureTestStringId()
- {
- // ARRANGE
- string content = File.ReadAllText("feature_id_string.json").Replace("\r", "").Replace("\n", "").Replace("\t", "").Replace(" ", "");
-
- // ACT
- Feature geo = JsonConvert.DeserializeObject(content);
- string content2 = JsonConvert.SerializeObject(geo);
- Feature geo2 = JsonConvert.DeserializeObject(content2);
-
- // ASSERT
- Assert.True(geo.Equals(geo2));
- }
-
- [Fact]
- public void FeatureTestNumberId()
- {
- // ARRANGE
- string content = File.ReadAllText("feature_id_number.json").Replace("\r", "").Replace("\n", "").Replace("\t", "").Replace(" ", "");
-
- // ACT
- Feature geo = JsonConvert.DeserializeObject(content);
- string content2 = JsonConvert.SerializeObject(geo);
- Feature geo2 = JsonConvert.DeserializeObject(content2);
-
- // ASSERT
- Assert.True(geo.Equals(geo2));
- }
-
- [Fact]
- public void FeatureOutOfRangeTest()
- {
- // ARRANGE
- GeoJsonConfig.EnforcePositionValidation();
- string content = File.ReadAllText("feature_out_of_range.json").Replace("\r", "").Replace("\n", "").Replace("\t", "").Replace(" ", "");
-
- // ACT & ASSERT
- Assert.Throws(() => JsonConvert.DeserializeObject(content));
- }
-
- [Fact]
- public void FeatureOutOfRangeTestIgnoreValidation()
- {
- // ARRANGE
- GeoJsonConfig.IgnorePositionValidation();
- string content = File.ReadAllText("feature_out_of_range.json").Replace("\r", "").Replace("\n", "").Replace("\t", "").Replace(" ", "");
-
- // ACT
- Feature geo = JsonConvert.DeserializeObject(content);
- string content2 = JsonConvert.SerializeObject(geo);
- Feature geo2 = JsonConvert.DeserializeObject(content2);
-
- // ASSERT
- Assert.True(geo.Equals(geo2));
- }
-
- [Fact]
- public void FeatureTestNullGeometry()
- {
- // ARRANGE
- string content = File.ReadAllText("feature_null_geometry.json").Replace("\r", "").Replace("\n", "").Replace("\t", "").Replace(" ", "");
-
- // ACT
- Feature geo = JsonConvert.DeserializeObject(content);
- string content2 = JsonConvert.SerializeObject(geo);
- Feature geo2 = JsonConvert.DeserializeObject(content2);
-
- // ASSERT
- Assert.True(geo.Equals(geo2));
- }
-
- [Fact]
- public void FeatureCollectionTest()
- {
- // ARRANGE
- string content = File.ReadAllText("featurecollection.json").Replace("\r", "").Replace("\n", "").Replace("\t", "").Replace(" ", "");
-
- // ACT
- FeatureCollection geo = JsonConvert.DeserializeObject(content);
- string content2 = JsonConvert.SerializeObject(geo);
- FeatureCollection geo2 = JsonConvert.DeserializeObject(content2);
-
- // ASSERT
- Assert.True(geo.Equals(geo2));
- }
-
- [Fact]
- public void PolygonTest()
- {
- // ARRANGE
- string content = File.ReadAllText("polygon.json").Replace("\r", "").Replace("\n", "").Replace("\t", "").Replace(" ", "");
-
- // ACT
- Polygon geo = JsonConvert.DeserializeObject(content);
- string content2 = JsonConvert.SerializeObject(geo);
- Polygon geo2 = JsonConvert.DeserializeObject(content2);
-
- // ASSERT
- Assert.True(geo.Equals(geo2));
- }
-
- [Fact]
- public void PolygonWithHoleTest()
- {
- // ARRANGE
- string content = File.ReadAllText("polygonwithhole.json").Replace("\r", "").Replace("\n", "").Replace("\t", "").Replace(" ", "");
-
- // ACT
- Polygon geo = JsonConvert.DeserializeObject(content);
- string content2 = JsonConvert.SerializeObject(geo);
- Polygon geo2 = JsonConvert.DeserializeObject(content2);
-
- // ASSERT
- Assert.True(geo.Equals(geo2));
- }
-
- [Fact]
- public void PolygonRemoveInnerRingsTestWithHole()
- {
- // ARRANGE
- string content = File.ReadAllText("polygonwithhole.json").Replace("\r", "").Replace("\n", "").Replace("\t", "").Replace(" ", "");
-
- // ACT
- Polygon geo = JsonConvert.DeserializeObject(content);
- bool result = geo.RemoveInteriorRings();
-
-
- // ASSERT
- Assert.True(result);
- Assert.Single(geo.Coordinates);
- }
-
- [Fact]
- public void PolygonRemoveInnerRingsTestWithoutHole()
- {
- // ARRANGE
- string content = File.ReadAllText("polygon.json").Replace("\r", "").Replace("\n", "").Replace("\t", "").Replace(" ", "");
-
- // ACT
- Polygon geo = JsonConvert.DeserializeObject(content);
- bool result = geo.RemoveInteriorRings();
-
-
- // ASSERT
- Assert.False(result);
- Assert.Single(geo.Coordinates);
- }
-
- [Fact]
- public void PointTest()
- {
- // ARRANGE
- string content = File.ReadAllText("point.json").Replace("\r", "").Replace("\n", "").Replace("\t", "").Replace(" ", "");
-
- // ACT
- Point geo = JsonConvert.DeserializeObject(content);
- string content2 = JsonConvert.SerializeObject(geo);
- Point geo2 = JsonConvert.DeserializeObject(content2);
-
- // ASSERT
- Assert.True(geo.Equals(geo2));
- }
-
- [Fact]
- public void MultiPointTest()
- {
- // ARRANGE
- string content = File.ReadAllText("multipoint.json").Replace("\r", "").Replace("\n", "").Replace("\t", "").Replace(" ", "");
-
- // ACT
- MultiPoint geo = JsonConvert.DeserializeObject(content);
- string content2 = JsonConvert.SerializeObject(geo);
- MultiPoint geo2 = JsonConvert.DeserializeObject(content2);
-
- // ASSERT
- Assert.True(geo.Equals(geo2));
- }
-
- [Fact]
- public void GeometryCollectionTest()
- {
- // ARRANGE
- string content = File.ReadAllText("geometrycollection.json").Replace("\r", "").Replace("\n", "").Replace("\t", "").Replace(" ", "");
-
- // ACT
- GeometryCollection geo = JsonConvert.DeserializeObject(content);
- string content2 = JsonConvert.SerializeObject(geo);
- GeometryCollection geo2 = JsonConvert.DeserializeObject(content2);
-
- // ASSERT
- Assert.True(geo.Equals(geo2));
- }
-
- [Fact]
- public void PositionTest()
- {
- // ARRANGE
- string content = File.ReadAllText("position.json").Replace("\r", "").Replace("\n", "").Replace("\t", "").Replace(" ", "");
-
- // ACT
- Position geo = JsonConvert.DeserializeObject(content);
- string content2 = JsonConvert.SerializeObject(geo);
-
- // ASSERT
- Assert.Equal(content, content2, true, true, true);
- }
-
- [Fact]
- public void GeoJsonFeatureTest()
- {
- // ARRANGE
- string content = File.ReadAllText("feature.json").Replace("\r", "").Replace("\n", "").Replace("\t", "").Replace(" ", "");
-
- // ACT
- GeoJson geo = GeoJson.FromJson(content);
- string content2 = geo.ToJson();
- GeoJson geo2 = GeoJson.FromJson(content2);
-
- // ASSERT
- Assert.True(geo.Equals(geo2));
- }
-
- [Fact]
- public void GeoJsonFeatureTestWithBbox()
- {
- // ARRANGE
- string content = File.ReadAllText("featurebbox.json").Replace("\r", "").Replace("\n", "").Replace("\t", "").Replace(" ", "");
-
- // ACT
- GeoJson geo = GeoJson.FromJson(content);
- string content2 = geo.ToJson();
- GeoJson geo2 = GeoJson.FromJson(content2);
-
- // ASSERT
- Assert.True(geo.Equals(geo2));
- }
-
- [Fact]
- public void GeoJson3DLineStringTestWithBbox()
- {
- // ARRANGE
- string content = File.ReadAllText("3dlinestringbbox.json").Replace("\r", "").Replace("\n", "").Replace("\t", "").Replace(" ", "");
-
- // ACT
- GeoJson geo = GeoJson.FromJson(content);
- string content2 = geo.ToJson();
- GeoJson geo2 = GeoJson.FromJson(content2);
-
- // ASSERT
- Assert.True(geo.Equals(geo2));
- }
-
- [Fact]
- public void GeoJsonFeatureCollectionTestWithBbox()
- {
- // ARRANGE
- string content = File.ReadAllText("featurecollectionbbox.json").Replace("\r", "").Replace("\n", "").Replace("\t", "").Replace(" ", "");
-
- // ACT
- GeoJson geo = GeoJson.FromJson(content);
- string content2 = geo.ToJson();
- GeoJson geo2 = GeoJson.FromJson(content2);
-
- // ASSERT
- Assert.True(geo.Equals(geo2));
- }
- }
-}
diff --git a/GeoJSON.Tests/3dlinestringbbox.json b/GeoJSON.Tests/ReferenceFiles/3dlinestringbbox.json
similarity index 100%
rename from GeoJSON.Tests/3dlinestringbbox.json
rename to GeoJSON.Tests/ReferenceFiles/3dlinestringbbox.json
diff --git a/GeoJSON.Tests/feature.json b/GeoJSON.Tests/ReferenceFiles/feature.json
similarity index 100%
rename from GeoJSON.Tests/feature.json
rename to GeoJSON.Tests/ReferenceFiles/feature.json
diff --git a/GeoJSON.Tests/feature_id_num_as_string.json b/GeoJSON.Tests/ReferenceFiles/feature_id_num_as_string.json
similarity index 100%
rename from GeoJSON.Tests/feature_id_num_as_string.json
rename to GeoJSON.Tests/ReferenceFiles/feature_id_num_as_string.json
diff --git a/GeoJSON.Tests/feature_id_number.json b/GeoJSON.Tests/ReferenceFiles/feature_id_number.json
similarity index 100%
rename from GeoJSON.Tests/feature_id_number.json
rename to GeoJSON.Tests/ReferenceFiles/feature_id_number.json
diff --git a/GeoJSON.Tests/feature_id_string.json b/GeoJSON.Tests/ReferenceFiles/feature_id_string.json
similarity index 100%
rename from GeoJSON.Tests/feature_id_string.json
rename to GeoJSON.Tests/ReferenceFiles/feature_id_string.json
diff --git a/GeoJSON.Tests/feature_null_geometry.json b/GeoJSON.Tests/ReferenceFiles/feature_null_geometry.json
similarity index 100%
rename from GeoJSON.Tests/feature_null_geometry.json
rename to GeoJSON.Tests/ReferenceFiles/feature_null_geometry.json
diff --git a/GeoJSON.Tests/feature_out_of_range.json b/GeoJSON.Tests/ReferenceFiles/feature_out_of_range.json
similarity index 100%
rename from GeoJSON.Tests/feature_out_of_range.json
rename to GeoJSON.Tests/ReferenceFiles/feature_out_of_range.json
diff --git a/GeoJSON.Tests/featurebbox.json b/GeoJSON.Tests/ReferenceFiles/featurebbox.json
similarity index 100%
rename from GeoJSON.Tests/featurebbox.json
rename to GeoJSON.Tests/ReferenceFiles/featurebbox.json
diff --git a/GeoJSON.Tests/featurecollection.json b/GeoJSON.Tests/ReferenceFiles/featurecollection.json
similarity index 100%
rename from GeoJSON.Tests/featurecollection.json
rename to GeoJSON.Tests/ReferenceFiles/featurecollection.json
diff --git a/GeoJSON.Tests/featurecollectionbbox.json b/GeoJSON.Tests/ReferenceFiles/featurecollectionbbox.json
similarity index 100%
rename from GeoJSON.Tests/featurecollectionbbox.json
rename to GeoJSON.Tests/ReferenceFiles/featurecollectionbbox.json
diff --git a/GeoJSON.Tests/ReferenceFiles/geometrycollection.json b/GeoJSON.Tests/ReferenceFiles/geometrycollection.json
new file mode 100644
index 0000000..679d76d
--- /dev/null
+++ b/GeoJSON.Tests/ReferenceFiles/geometrycollection.json
@@ -0,0 +1 @@
+{"geometries":[{"type":"LineString","coordinates":[[0.0,0.0],[0.0,1.0]]},{"type":"LineString","coordinates":[[1.0,0.0],[1.0,1.0]]}],"type":"GeometryCollection"}
\ No newline at end of file
diff --git a/GeoJSON.Tests/linestring.json b/GeoJSON.Tests/ReferenceFiles/linestring.json
similarity index 100%
rename from GeoJSON.Tests/linestring.json
rename to GeoJSON.Tests/ReferenceFiles/linestring.json
diff --git a/GeoJSON.Tests/ReferenceFiles/multilinestring.json b/GeoJSON.Tests/ReferenceFiles/multilinestring.json
new file mode 100644
index 0000000..f426c83
--- /dev/null
+++ b/GeoJSON.Tests/ReferenceFiles/multilinestring.json
@@ -0,0 +1 @@
+{"type":"MultiLineString","coordinates":[[[0.0,0.0],[0.0,1.0]],[[1.0,0.0],[1.0,1.0]]]}
\ No newline at end of file
diff --git a/GeoJSON.Tests/multipoint.json b/GeoJSON.Tests/ReferenceFiles/multipoint.json
similarity index 87%
rename from GeoJSON.Tests/multipoint.json
rename to GeoJSON.Tests/ReferenceFiles/multipoint.json
index c2d4130..84d4852 100644
--- a/GeoJSON.Tests/multipoint.json
+++ b/GeoJSON.Tests/ReferenceFiles/multipoint.json
@@ -1,6 +1,6 @@
{
"type": "MultiPoint",
- "coordinates": [
+ "Points": [
[ 102.0, 1.5 ],
[ 103.0, 2.5 ],
[ 101.0, 1.0 ],
diff --git a/GeoJSON.Tests/multipolygon.json b/GeoJSON.Tests/ReferenceFiles/multipolygon.json
similarity index 100%
rename from GeoJSON.Tests/multipolygon.json
rename to GeoJSON.Tests/ReferenceFiles/multipolygon.json
diff --git a/GeoJSON.Tests/point.json b/GeoJSON.Tests/ReferenceFiles/point.json
similarity index 100%
rename from GeoJSON.Tests/point.json
rename to GeoJSON.Tests/ReferenceFiles/point.json
diff --git a/GeoJSON.Tests/ReferenceFiles/polygon.json b/GeoJSON.Tests/ReferenceFiles/polygon.json
new file mode 100644
index 0000000..5de004f
--- /dev/null
+++ b/GeoJSON.Tests/ReferenceFiles/polygon.json
@@ -0,0 +1 @@
+{"type":"Polygon","coordinates":[[[40.0,40.0],[20.0,45.0],[45.0,30.0],[40.0,40.0]]]}
\ No newline at end of file
diff --git a/GeoJSON.Tests/polygonwithhole.json b/GeoJSON.Tests/ReferenceFiles/polygonwithhole.json
similarity index 100%
rename from GeoJSON.Tests/polygonwithhole.json
rename to GeoJSON.Tests/ReferenceFiles/polygonwithhole.json
diff --git a/GeoJSON.Tests/position.json b/GeoJSON.Tests/ReferenceFiles/position.json
similarity index 100%
rename from GeoJSON.Tests/position.json
rename to GeoJSON.Tests/ReferenceFiles/position.json
diff --git a/GeoJSON.Tests/Testers/GeoJsonBaseUnitTester.cs b/GeoJSON.Tests/Testers/GeoJsonBaseUnitTester.cs
new file mode 100644
index 0000000..ca41ef0
--- /dev/null
+++ b/GeoJSON.Tests/Testers/GeoJsonBaseUnitTester.cs
@@ -0,0 +1,42 @@
+using System.Diagnostics;
+using System.IO;
+using System.Reflection;
+
+namespace GeoJSON.Tests.Tests
+{
+ public class GeoJsonBaseUnitTester: WkbBaseUnitTester
+ {
+
+ ///
+ /// General method for writing geometryContent into a file.
+ ///
+ ///
+ ///
+ protected static void WriteJsonFile(string filename, string geometryContent )
+ {
+ var filepath = Path.Join(GetCurrentWorkingDirectory(), filename);
+ Debug.WriteLine($"Saving content into {filepath}");
+
+ File.WriteAllText(filepath, geometryContent);
+ }
+
+ protected static string ReadJsonFile(string filename)
+ {
+ var filepath = Path.Join(GetCurrentWorkingDirectory(), filename);
+ Debug.WriteLine($"Reading content from {filepath}");
+
+ return File.ReadAllText(filepath);
+ }
+
+ protected static string GetCurrentWorkingDirectory()
+ {
+ var cwddirname = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
+
+ var ToDirname = cwddirname.Split("bin")[0];
+
+ return ToDirname;
+
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/GeoJSON.Tests/Testers/GeoJsonFeatureCollectionUnitTester.cs b/GeoJSON.Tests/Testers/GeoJsonFeatureCollectionUnitTester.cs
new file mode 100644
index 0000000..2bd34a6
--- /dev/null
+++ b/GeoJSON.Tests/Testers/GeoJsonFeatureCollectionUnitTester.cs
@@ -0,0 +1,73 @@
+using BAMCIS.GeoJSON;
+using GeoJSON.Tests.Tests;
+using Newtonsoft.Json;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using Xunit;
+
+namespace GeoJSON.Tests.Testers
+{
+ public class GeoJsonFeatureCollectionUnitTester : GeoJsonBaseUnitTester
+ {
+
+ [Fact]
+ public void FeatureCollectionTest()
+ {
+ // ARRANGE
+ FeatureCollection featCollection = FetchDefaultFeaturecollection();
+ string content = JsonConvert.SerializeObject(featCollection);
+ Debug.WriteLine(content);
+
+ // ACT
+ FeatureCollection geo = JsonConvert.DeserializeObject(content);
+ string contentDeserialized = JsonConvert.SerializeObject(geo);
+ Debug.WriteLine(contentDeserialized);
+
+ Assert.True(contentDeserialized.Equals(content));
+
+
+ FeatureCollection geoAfterDesrialization = JsonConvert.DeserializeObject(contentDeserialized);
+
+ // ASSERT
+ Assert.True(geo.Equals(geoAfterDesrialization));
+ }
+
+ private static FeatureCollection FetchDefaultFeaturecollection()
+ {
+ var linearRing = new LinearRing(new List{
+ new LineSegment(new Point(100,0),
+ new Point(101,0)
+ ),
+
+ new LineSegment(new Point(101,0),
+ new Point(101,1)
+ ),
+
+ new LineSegment(new Point(101,1),
+ new Point(100,1)
+ ),
+
+ new LineSegment(new Point(100,1),
+ new Point(100,0)
+ )
+ });
+
+ var polygon = new Polygon(linearRing);
+
+
+ var LineString = new LineString(new List { new Point(102.0, 0.0), new Point(103.0, 1.0) });
+
+ var point = new Point(102, 0.5);
+
+
+ var featCol = new FeatureCollection(new List { new Feature(point, new Dictionary{ {"PropertyToTest", "Test" } } ),
+ new Feature(LineString, new Dictionary{ {"PropertyToTest", "Test" } }),
+ new Feature(polygon, new Dictionary{ {"PropertyToTest", "Test" } })
+ });
+
+ return featCol;
+
+ }
+ }
+}
diff --git a/GeoJSON.Tests/Testers/GeoJsonFeatureUnitTester.cs b/GeoJSON.Tests/Testers/GeoJsonFeatureUnitTester.cs
new file mode 100644
index 0000000..96d54fa
--- /dev/null
+++ b/GeoJSON.Tests/Testers/GeoJsonFeatureUnitTester.cs
@@ -0,0 +1,134 @@
+using BAMCIS.GeoJSON;
+using GeoJSON.Tests.Tests;
+using Newtonsoft.Json;
+using System;
+using System.Diagnostics;
+using System.IO;
+using Xunit;
+
+namespace GeoJSON.Tests.Testers
+{
+ public class GeoJsonFeatureUnitTester : GeoJsonBaseUnitTester
+ {
+
+ [Fact]
+ public void FeatureTest()
+ {
+ // ARRANGE
+ string content = File.ReadAllText("ReferenceFiles/feature.json").Replace("\r", "").Replace("\n", "").Replace("\t", "").Replace(" ", "");
+
+ // ACT
+ Feature geo = JsonConvert.DeserializeObject(content);
+ string content2 = JsonConvert.SerializeObject(geo);
+ Feature geo2 = JsonConvert.DeserializeObject(content2);
+
+ // ASSERT
+ Assert.True(geo.Equals(geo2));
+ }
+
+ [Fact]
+ public void FeatureIdNotEqualTest()
+ {
+ // ARRANGE
+ string content1 = File.ReadAllText("ReferenceFiles/feature_id_number.json").Replace("\r", "").Replace("\n", "").Replace("\t", "").Replace(" ", "");
+ string content2 = File.ReadAllText("ReferenceFiles/feature_id_num_as_string.json").Replace("\r", "").Replace("\n", "").Replace("\t", "").Replace(" ", "");
+
+ // ACT
+ Feature geoWithNumId = JsonConvert.DeserializeObject(content1);
+ Feature geoWithStringId = JsonConvert.DeserializeObject(content2);
+
+ // ASSERT
+ Assert.Equal(geoWithNumId.Id.Value, geoWithStringId.Id.Value);
+ Assert.NotEqual(geoWithNumId.Id, geoWithStringId.Id);
+ }
+
+ [Fact]
+ public void FeatureIdEqualTest()
+ {
+ Debug.WriteLine(GetCurrentWorkingDirectory());
+ // ARRANGE
+ string content = File.ReadAllText("ReferenceFiles/feature_id_number.json").Replace("\r", "").Replace("\n", "").Replace("\t", "").Replace(" ", "");
+
+ // ACT
+ Feature geo1 = JsonConvert.DeserializeObject(content);
+ Feature geo2 = JsonConvert.DeserializeObject(content);
+
+ // ASSERT
+ Assert.Equal(geo1.Id.Value, geo2.Id.Value);
+ Assert.Equal(geo1.Id, geo2.Id);
+ }
+
+ [Fact]
+ public void FeatureTestStringId()
+ {
+ // ARRANGE
+ string content = File.ReadAllText("ReferenceFiles/feature_id_string.json").Replace("\r", "").Replace("\n", "").Replace("\t", "").Replace(" ", "");
+
+ // ACT
+ Feature geo = JsonConvert.DeserializeObject(content);
+ string content2 = JsonConvert.SerializeObject(geo);
+ Feature geo2 = JsonConvert.DeserializeObject(content2);
+
+ // ASSERT
+ Assert.True(geo.Equals(geo2));
+ }
+
+ [Fact]
+ public void FeatureTestNumberId()
+ {
+ // ARRANGE
+ string content = File.ReadAllText("ReferenceFiles/feature_id_number.json").Replace("\r", "").Replace("\n", "").Replace("\t", "").Replace(" ", "");
+
+ // ACT
+ Feature geo = JsonConvert.DeserializeObject(content);
+ string content2 = JsonConvert.SerializeObject(geo);
+ Feature geo2 = JsonConvert.DeserializeObject(content2);
+
+ // ASSERT
+ Assert.True(geo.Equals(geo2));
+ }
+
+ [Fact]
+ public void FeatureOutOfRangeTest()
+ {
+ // ARRANGE
+ GeoJsonConfig.EnforcePositionValidation();
+ string content = File.ReadAllText("ReferenceFiles/feature_out_of_range.json").Replace("\r", "").Replace("\n", "").Replace("\t", "").Replace(" ", "");
+
+ // ACT & ASSERT
+ Assert.Throws(() => JsonConvert.DeserializeObject(content));
+ }
+
+ [Fact]
+ public void FeatureOutOfRangeTestIgnoreValidation()
+ {
+ // ARRANGE
+ GeoJsonConfig.IgnorePositionValidation();
+ string content = File.ReadAllText("ReferenceFiles/feature_out_of_range.json").Replace("\r", "").Replace("\n", "").Replace("\t", "").Replace(" ", "");
+
+ // ACT
+ Feature geo = JsonConvert.DeserializeObject(content);
+ string content2 = JsonConvert.SerializeObject(geo);
+ Feature geo2 = JsonConvert.DeserializeObject(content2);
+
+ // ASSERT
+ Assert.True(geo.Equals(geo2));
+ }
+
+ [Fact]
+ public void FeatureTestNullGeometry()
+ {
+ // ARRANGE
+ string content = File.ReadAllText("ReferenceFiles/feature_null_geometry.json").Replace("\r", "").Replace("\n", "").Replace("\t", "").Replace(" ", "");
+
+ // ACT
+ Feature geo = JsonConvert.DeserializeObject(content);
+ string content2 = JsonConvert.SerializeObject(geo);
+ Feature geo2 = JsonConvert.DeserializeObject(content2);
+
+ // ASSERT
+ Assert.True(geo.Equals(geo2));
+ }
+
+ }
+}
diff --git a/GeoJSON.Tests/Testers/GeoJsonLineStringUnitTester.cs b/GeoJSON.Tests/Testers/GeoJsonLineStringUnitTester.cs
new file mode 100644
index 0000000..1a5f331
--- /dev/null
+++ b/GeoJSON.Tests/Testers/GeoJsonLineStringUnitTester.cs
@@ -0,0 +1,66 @@
+using BAMCIS.GeoJSON;
+using GeoJSON.Tests.Tests;
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using Xunit;
+
+namespace GeoJSON.Tests.Testers
+{
+ public class GeoJsonLineStringUnitTester : GeoJsonBaseUnitTester
+ {
+
+ private LineString FetchDefaultLineString()
+ {
+ var p1 = new Point(1, 1);
+
+ var p2 = new Point(2, 5);
+
+ var p3 = new Point(5, 5);
+
+ var p4 = new Point(8, 5);
+
+ var lineSegment = new LineSegment(p1, p2);
+
+ var lineSegment2 = new LineSegment(p2, p3);
+
+ var lineSegment3 = new LineSegment(p3, p4);
+
+ var lineString = new LineString(new List { lineSegment, lineSegment2, lineSegment3 });
+
+ return lineString;
+ }
+
+ [Fact]
+ public void LineStringTouchesPoint()
+ {
+ // ARRANGE
+ var lineString = FetchDefaultLineString();
+
+ var p1 = new Point(2, 5);
+ var p2 = new Point(6, 5);
+ // ACT
+
+
+ // ASSERT
+ Assert.False(lineString.Equals(p1));
+ Assert.True(lineString.Touches(p1));
+ Assert.False(lineString.Intersects(p1));
+ Assert.True(lineString.Touches(p2));
+ Assert.False(lineString.Intersects(p1));
+ }
+
+ [Fact]
+ public void LineStringEqualityEvaluation()
+ {
+ // ARRANGE
+ var lineString = FetchDefaultLineString();
+ var lineString2 = FetchDefaultLineString();
+
+ // ASSERT
+ Assert.True(lineString.Equals(lineString2));
+ }
+
+ }
+}
diff --git a/GeoJSON.Tests/Testers/GeoJsonMultiLineUnitTester.cs b/GeoJSON.Tests/Testers/GeoJsonMultiLineUnitTester.cs
new file mode 100644
index 0000000..9e1ad22
--- /dev/null
+++ b/GeoJSON.Tests/Testers/GeoJsonMultiLineUnitTester.cs
@@ -0,0 +1,68 @@
+using BAMCIS.GeoJSON;
+using Newtonsoft.Json;
+using System.Collections.Generic;
+using System.IO;
+using System.Reflection;
+using Xunit;
+
+namespace GeoJSON.Tests.Tests
+{
+ public class GeoJsonMultiLineUnitTester : GeoJsonBaseUnitTester
+ {
+ protected MultiLineString FetchDefaultGeometry()
+ {
+ var line1 = new LineString(new List{ new Point(0,0), new Point(0,1) });
+
+ var line2 = new LineString(new List { new Point(1, 0), new Point(1, 1) });
+
+
+ return new MultiLineString(new List{ line1, line2 });
+ }
+
+ [Fact]
+ public void MultiLineStringTest()
+ {
+ // ARRANGE
+ var multiLineString = FetchDefaultGeometry();
+ var content = multiLineString.ToJson();
+
+ WriteJsonFile("ReferenceFiles/multilinestring.json", content);
+
+ content = ReadJsonFile("ReferenceFiles/multilinestring.json");
+
+ // ACT
+ MultiLineString geo = MultiLineString.FromJson(content);
+
+ string content2 = geo.ToJson();
+
+ MultiLineString geo2 = MultiLineString.FromJson(content2);
+
+ // ASSERT
+ Assert.True(geo.Equals(geo2));
+ }
+
+
+ [Fact]
+ public void MultiLineStringWritingTest()
+ {
+ // ACT
+
+ LineString lineString1 = GeometriesDefaultGenerator.GenerateDefaultLineStringForUnitTests();
+
+ LineString lineString2 = GeometriesDefaultGenerator.GenerateDefaultLineStringForUnitTests(3, 5, 2);
+
+
+ var multiLineString = new MultiLineString(new List { lineString1, lineString2 });
+
+
+ string content2 = JsonConvert.SerializeObject(multiLineString, Formatting.Indented);
+
+ string ToDirname = GetCurrentWorkingDirectory();
+
+ File.WriteAllText(Path.Combine(ToDirname, "multilinestring.json"), content2);
+
+ // File.Delete(ToDirname, "multilinestring.json"));
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/GeoJSON.Tests/Testers/GeoJsonMultiPolygonUnitTester.cs b/GeoJSON.Tests/Testers/GeoJsonMultiPolygonUnitTester.cs
new file mode 100644
index 0000000..9f67450
--- /dev/null
+++ b/GeoJSON.Tests/Testers/GeoJsonMultiPolygonUnitTester.cs
@@ -0,0 +1,26 @@
+using BAMCIS.GeoJSON;
+using Newtonsoft.Json;
+using System.IO;
+using Xunit;
+
+namespace GeoJSON.Tests.Testers
+{
+ public class GeoJsonMultiPolygonUnitTester
+ {
+
+ [Fact]
+ public void MultiPolygonTest()
+ {
+ // ARRANGE
+ string content = File.ReadAllText("ReferenceFiles/multipolygon.json").Replace("\r", "").Replace("\n", "").Replace("\t", "").Replace(" ", "");
+
+ // ACT
+ MultiPolygon geo = JsonConvert.DeserializeObject(content);
+ string content2 = JsonConvert.SerializeObject(geo);
+
+ // ASSERT
+ Assert.Equal(content, content2, true, true, true);
+ }
+
+ }
+}
diff --git a/GeoJSON.Tests/Testers/GeoJsonPolygonUnitTester.cs b/GeoJSON.Tests/Testers/GeoJsonPolygonUnitTester.cs
new file mode 100644
index 0000000..254d6f3
--- /dev/null
+++ b/GeoJSON.Tests/Testers/GeoJsonPolygonUnitTester.cs
@@ -0,0 +1,240 @@
+using BAMCIS.GeoJSON;
+using GeoJSON.Tests.Tests;
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using Xunit;
+
+namespace GeoJSON.Tests.Testers
+{
+ public class GeoJsonPolygonUnitTester : GeoJsonBaseUnitTester
+ {
+ #region Helper Methods
+ private static Polygon FetchDefaultPolygon()
+ {
+ var coordinates = new List { new Coordinate(40, 40) ,
+ new Coordinate(20,45) ,
+ new Coordinate(45, 30) ,
+ new Coordinate(40,40)
+ };
+
+ var linearRing = new LinearRing(coordinates);
+
+ var polygon = new Polygon(linearRing);
+
+ return polygon;
+ }
+
+ ///
+ /// This method generates a regular rectangle (therefore, a polygon)
+ /// ranging from the coordinate 1,1 (lower left corner) up to the coordinate 4,4 (Upper right corner)
+ ///
+ ///
+ private static Polygon FetchDefaultRectangle()
+ {
+ var coordinates = new List { new Coordinate(1, 1) ,
+ new Coordinate(1, 4) ,
+ new Coordinate(4, 4) ,
+ new Coordinate(4, 1) ,
+ new Coordinate(1, 1)
+ };
+
+ var linearRing = new LinearRing(coordinates);
+
+ var polygon = new Polygon(linearRing);
+
+ return polygon;
+ }
+
+
+ private static Polygon FetchIrregularPolygon()
+ {
+ var coordinates = new List { new Coordinate(1, 1) ,
+ new Coordinate(1, 4) ,
+ new Coordinate(4, 4) ,
+ new Coordinate(6, 4) ,
+ new Coordinate(5, -2) ,
+ new Coordinate(3.5, 2.5) ,
+ new Coordinate(3.25, 2.4) ,
+ new Coordinate(2, -2),
+ new Coordinate(1, 1)
+ };
+
+ var linearRing = new LinearRing(coordinates);
+
+ var polygon = new Polygon(linearRing);
+
+ return polygon;
+ }
+
+ #endregion Helper Methods
+
+ #region Test Methods
+
+ [Fact]
+ public void SerializePolygon()
+ {
+ // ARRANGE
+
+ var polygon = FetchDefaultPolygon();
+ string polygonContent = JsonConvert.SerializeObject(polygon);
+ WriteJsonFile("ReferenceFiles/Polygon.json", polygonContent);
+
+ string content = ReadJsonFile("ReferenceFiles/Polygon.json");
+
+ // ACT
+ var PolygonReconstituted = JsonConvert.DeserializeObject(content);
+
+
+ // ASSERT
+ Assert.True(polygon.Equals(PolygonReconstituted));
+ }
+
+ [Fact]
+ public void PolygonContainsPoint()
+ {
+
+ // ARRANGE
+
+ var regularPolygon = FetchDefaultRectangle();
+
+ var irregularPolygon = FetchIrregularPolygon();
+
+ var point = new Point(2, 2);
+
+ var point2 = new Point(5, 2);
+
+ var point3 = new Point(3, -2.5); // this point must be outside of the irregularpolygon, and also outside of its bounding box
+
+ var point4 = new Point(3.5, 0.5); // this point must be outside of the irregularpolygon, though inside of its bounding box
+
+ var point5 = new Point(3.5, 2.5); // this points must touch the irregularPolygon, not be inside of the polygon, nor touch its bounding box
+
+ // ASSERT
+
+ #region Regular Polygon Validations
+
+ Assert.True(regularPolygon.Contains(point));
+ Assert.False(regularPolygon.Touches(point));
+
+ Assert.False(regularPolygon.Contains(point2));
+ Assert.False(regularPolygon.Touches(point2));
+
+ #endregion Regular Polygon Validations
+
+ #region Irregular Polygon Validations
+ Assert.True(!irregularPolygon.Contains(point3) &&
+ !irregularPolygon.BoundingBox.Contains(point3) &&
+ !irregularPolygon.Intersects(point3) &&
+ !irregularPolygon.BoundingBox.Touches(point3));
+
+ Assert.True(!irregularPolygon.Contains(point4));
+
+ Assert.True(irregularPolygon.BoundingBox.Contains(point4));
+ Assert.False(irregularPolygon.Intersects(point4));
+ Assert.False(irregularPolygon.BoundingBox.Touches(point4));
+
+
+
+ Assert.False(irregularPolygon.Contains(point5));
+
+ Assert.True(irregularPolygon.BoundingBox.Contains(point5));
+
+ Assert.True(irregularPolygon.Touches(point5));
+
+ Assert.False(irregularPolygon.BoundingBox.Touches(point5));
+
+ #endregion Irregular Polygon Validations
+
+ }
+
+ [Fact]
+ public void PolygonIntersectsPoint()
+ {
+
+ // ARRANGE
+
+ var polygon = FetchDefaultRectangle();
+
+ var point = new Point(1, 1);
+
+
+ // ASSERT
+ Assert.False(polygon.Intersects(point));
+
+ }
+
+
+
+ [Fact]
+ public void PolygonContainsLineSegment()
+ {
+
+ // ARRANGE
+
+ var polygon = FetchDefaultRectangle();
+
+ var point = new Point(2, 2);
+
+ var point2 = new Point(5, 2);
+
+ // ASSERT
+ Assert.True(polygon.Contains(point));
+ Assert.False(polygon.Touches(point));
+
+ Assert.False(polygon.Contains(point2));
+ Assert.False(polygon.Touches(point2));
+
+ }
+
+ [Fact]
+ public void IntersectsLineSegment()
+ {
+
+ // ARRANGE
+
+ var polygon = FetchDefaultRectangle();
+
+ var point = new Point(1, 1);
+
+ var lineSegment = new LineSegment(point, new Point(5, 5));
+
+
+ // ASSERT
+ Assert.True(polygon.Intersects(lineSegment));
+ Assert.False(polygon.Contains(lineSegment));
+
+ }
+
+
+ [Fact]
+ public void IntersectsLineString()
+ {
+
+ // ARRANGE
+
+ var polygon = FetchDefaultRectangle();
+
+ var p1 = new Point(1, 1);
+
+ var p2 = new Point(2, 5);
+
+ var p3 = new Point(5, 5);
+
+ var lineSegment = new LineSegment(p1, p2);
+
+ var lineSegment2 = new LineSegment(p2, p3);
+
+ var lineString = new LineString(new List {lineSegment,lineSegment2 });
+
+
+ // ASSERT
+ Assert.True(polygon.Intersects(lineString));
+ Assert.False(polygon.Contains(lineString));
+ }
+
+ #endregion Test Methods
+
+ }
+}
diff --git a/GeoJSON.Tests/Testers/GeoJsonUnitTests.cs b/GeoJSON.Tests/Testers/GeoJsonUnitTests.cs
new file mode 100644
index 0000000..9c04ef4
--- /dev/null
+++ b/GeoJSON.Tests/Testers/GeoJsonUnitTests.cs
@@ -0,0 +1,206 @@
+using BAMCIS.GeoJSON;
+using Newtonsoft.Json;
+using System;
+using System.IO;
+using Xunit;
+
+namespace GeoJSON.Tests.Tests
+{
+ public class GeoJsonUnitTests : GeoJsonMultiLineUnitTester
+ {
+
+ [Fact]
+ public void PolygonTest()
+ {
+ // ARRANGE
+ string content = File.ReadAllText("ReferenceFiles/polygon.json").Replace("\r", "").Replace("\n", "").Replace("\t", "").Replace(" ", "");
+
+ // ACT
+ Polygon geo = JsonConvert.DeserializeObject(content);
+ string content2 = JsonConvert.SerializeObject(geo);
+ Polygon geo2 = JsonConvert.DeserializeObject(content2);
+
+ // ASSERT
+ Assert.True(geo.Equals(geo2));
+ }
+
+ [Fact]
+ public void PolygonWithHoleTest()
+ {
+ // ARRANGE
+ string content = File.ReadAllText("ReferenceFiles/polygonwithhole.json").Replace("\r", "").Replace("\n", "").Replace("\t", "").Replace(" ", "");
+
+ // ACT
+ Polygon geo = JsonConvert.DeserializeObject(content);
+ string content2 = JsonConvert.SerializeObject(geo);
+ Polygon geo2 = JsonConvert.DeserializeObject(content2);
+
+ // ASSERT
+ Assert.True(geo.Equals(geo2));
+ }
+
+ [Fact]
+ public void PolygonRemoveInnerRingsTestWithHole()
+ {
+ // ARRANGE
+ string content = File.ReadAllText("ReferenceFiles/polygonwithhole.json").Replace("\r", "").Replace("\n", "").Replace("\t", "").Replace(" ", "");
+
+ // ACT
+
+ Polygon geo = JsonConvert.DeserializeObject(content);
+ geo.RemoveInteriorRings();
+
+ // ASSERT
+ Assert.Single(geo.LinearRings);
+
+
+ Assert.True(true);
+
+
+
+ }
+
+ [Fact]
+ public void PolygonRemoveInnerRingsTestWithoutHole()
+ {
+ // ARRANGE
+ string content = File.ReadAllText("ReferenceFiles/polygon.json").Replace("\r", "").Replace("\n", "").Replace("\t", "").Replace(" ", "");
+
+ // ACT
+ Polygon geo = JsonConvert.DeserializeObject(content);
+ geo.RemoveInteriorRings();
+
+
+ // ASSERT
+ Assert.Single(geo.LinearRings);
+ }
+
+ [Fact]
+ public void PointTest()
+ {
+ // ARRANGE
+ string content = File.ReadAllText("ReferenceFiles/point.json").Replace("\r", "").Replace("\n", "").Replace("\t", "").Replace(" ", "");
+
+ // ACT
+ Point geo = JsonConvert.DeserializeObject(content);
+ string content2 = JsonConvert.SerializeObject(geo);
+ Point geo2 = JsonConvert.DeserializeObject(content2);
+
+ // ASSERT
+ Assert.True(geo.Equals(geo2));
+ }
+
+ [Fact]
+ public void MultiPointTest()
+ {
+ // ARRANGE
+ string content = File.ReadAllText("ReferenceFiles/multipoint.json").Replace("\r", "").Replace("\n", "").Replace("\t", "").Replace(" ", "");
+ var geo = MultiPoint.FromJson(content);
+ // ACT
+ // MultiPoint geo = JsonConvert.DeserializeObject(content);
+ string content2 = JsonConvert.SerializeObject(geo);
+ MultiPoint geo2 = JsonConvert.DeserializeObject(content2);
+
+ // ASSERT
+ Assert.True(geo.Equals(geo2));
+ }
+
+ [Fact]
+ public void GeometryCollectionTest()
+ {
+ // ARRANGE
+
+ var multiLineString = FetchDefaultGeometry();
+
+ var geometryCollection = new GeometryCollection(multiLineString.ToList());
+
+ var content = geometryCollection.ToJson();
+
+ WriteJsonFile("ReferenceFiles/geometrycollection.json", content);
+
+ content = ReadJsonFile("ReferenceFiles/geometrycollection.json");
+
+ // ACT
+ GeometryCollection geo = JsonConvert.DeserializeObject(content);
+ string content2 = JsonConvert.SerializeObject(geo);
+ GeometryCollection geo2 = JsonConvert.DeserializeObject(content2);
+
+ // ASSERT
+ Assert.True(geo.Equals(geo2));
+ }
+
+ [Fact]
+ public void PositionTest()
+ {
+ // ARRANGE
+ string content = File.ReadAllText("ReferenceFiles/position.json").Replace("\r", "").Replace("\n", "").Replace("\t", "").Replace(" ", "");
+
+ // ACT
+ Coordinate geo = JsonConvert.DeserializeObject(content);
+ string content2 = JsonConvert.SerializeObject(geo);
+
+ // ASSERT
+ Assert.Equal(content, content2, true, true, true);
+ }
+
+ [Fact]
+ public void GeoJsonFeatureTest()
+ {
+ // ARRANGE
+ string content = File.ReadAllText("ReferenceFiles/feature.json").Replace("\r", "").Replace("\n", "").Replace("\t", "").Replace(" ", "");
+
+ // ACT
+ GeoJson geo = GeoJson.FromJson(content);
+ string content2 = geo.ToJson();
+ GeoJson geo2 = GeoJson.FromJson(content2);
+
+ // ASSERT
+ Assert.True(geo.Equals(geo2));
+ }
+
+ [Fact]
+ public void GeoJsonFeatureTestWithBbox()
+ {
+ // ARRANGE
+ string content = File.ReadAllText("ReferenceFiles/featurebbox.json").Replace("\r", "").Replace("\n", "").Replace("\t", "").Replace(" ", "");
+
+ // ACT
+ GeoJson geo = GeoJson.FromJson(content);
+ string content2 = geo.ToJson();
+ GeoJson geo2 = GeoJson.FromJson(content2);
+
+ // ASSERT
+ Assert.True(geo.Equals(geo2));
+ }
+
+ [Fact]
+ public void GeoJson3DLineStringTestWithBbox()
+ {
+ // ARRANGE
+ string content = File.ReadAllText("ReferenceFiles/3dlinestringbbox.json").Replace("\r", "").Replace("\n", "").Replace("\t", "").Replace(" ", "");
+
+ // ACT
+ GeoJson geo = GeoJson.FromJson(content);
+ string content2 = geo.ToJson();
+ GeoJson geo2 = GeoJson.FromJson(content2);
+
+ // ASSERT
+ Assert.True(geo.Equals(geo2));
+ }
+
+ [Fact]
+ public void GeoJsonFeatureCollectionTestWithBbox()
+ {
+ // ARRANGE
+ string content = File.ReadAllText("ReferenceFiles/featurecollectionbbox.json").Replace("\r", "").Replace("\n", "").Replace("\t", "").Replace(" ", "");
+
+ // ACT
+ GeoJson geo = GeoJson.FromJson(content);
+ string content2 = geo.ToJson();
+ GeoJson geo2 = GeoJson.FromJson(content2);
+
+ // ASSERT
+ Assert.True(geo.Equals(geo2));
+ }
+ }
+}
diff --git a/GeoJSON.Tests/Testers/GeometriesDefaultGenerator.cs b/GeoJSON.Tests/Testers/GeometriesDefaultGenerator.cs
new file mode 100644
index 0000000..0c161b4
--- /dev/null
+++ b/GeoJSON.Tests/Testers/GeometriesDefaultGenerator.cs
@@ -0,0 +1,47 @@
+using BAMCIS.GeoJSON;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace GeoJSON.Tests.Tests
+{
+ internal static class GeometriesDefaultGenerator
+ {
+
+ internal static LineString GenerateDefaultLineStringForUnitTests(int maxLineSegmentsPerLineString = 4, int x0 = 0, int y0 = 0)
+ {
+ var lineSegments = new List();
+
+ for (int lineSegmentCounter = 0; lineSegmentCounter < maxLineSegmentsPerLineString; lineSegmentCounter++)
+ {
+ for (int x = x0; x < x0 + 2; x++)
+ {
+ int y = 0;
+ var p1 = new Point(new Coordinate(x, y));
+
+ for (y = y0; y < y0 + 2; y++)
+ {
+ var p2 = new Point(new Coordinate(x, y));
+
+ if (p1 == p2)
+ {
+
+ }
+ else
+ {
+ var lineSegment = new LineSegment(p1, p2);
+ lineSegments.Add(lineSegment);
+ }
+ }
+ }
+ }
+
+ var geo = new LineString(lineSegments);
+ return geo;
+ }
+
+
+ }
+}
diff --git a/GeoJSON.Tests/Testers/WkbGeometryCollectionUnitTester.cs b/GeoJSON.Tests/Testers/WkbGeometryCollectionUnitTester.cs
new file mode 100644
index 0000000..b7f2a95
--- /dev/null
+++ b/GeoJSON.Tests/Testers/WkbGeometryCollectionUnitTester.cs
@@ -0,0 +1,108 @@
+using BAMCIS.GeoJSON;
+using BAMCIS.GeoJSON.Wkb;
+using System.Collections.Generic;
+using System.Linq;
+using Xunit;
+
+namespace GeoJSON.Tests.Testers
+{
+ public class WkbGeometryCollectionUnitTester : WkbBaseUnitTester
+ {
+ #region geometry generator
+ ///
+ /// GEOMETRYCOLLECTION(POINT (40 10),LINESTRING(10 10, 20 20, 10 40),POLYGON((40 40, 20 45, 45 30, 40 40)))
+ ///
+ ///
+ ///
+ ///
+ ///
+ private static void FetchDefaultGeometryCollectionTest(out Point point, out LinearRing linearRing, out Polygon polygon, out GeometryCollection geomColl)
+ {
+ point = new Point(40, 10);
+ var lineString = new LineString(new List { new Point(10, 10), new Point(20, 20), new Point(10, 40) });
+
+ var coordinates = new List { new Coordinate(40, 40) ,
+ new Coordinate(20,45) ,
+ new Coordinate(45, 30) ,
+ new Coordinate(40,40)
+ };
+
+ linearRing = new LinearRing(coordinates);
+ polygon = new Polygon(linearRing);
+ geomColl = new GeometryCollection(new List { point, lineString, polygon });
+ }
+
+
+ #endregion geometry generator
+
+
+ #region Tests
+
+ [Fact]
+ public void GeometryCollectionTest_FromBinary_BigEndian()
+ {
+ // ARRANGE
+
+
+ FetchDefaultGeometryCollectionTest(out Point point,
+ out LinearRing linearRing,
+ out Polygon polygon,
+ out GeometryCollection geomColl);
+
+ // ACT
+ byte[] bytes = WkbConverter.ToBinary(geomColl);
+
+ Geometry geo = WkbConverter.FromBinary(bytes);
+
+ GeometryCollection geoCollection = Assert.IsType(geo);
+ Point pointReconstituted = Assert.IsType(geoCollection.Geometries.ElementAt(0));
+ LineString lineStringReconstituted = Assert.IsType(geoCollection.Geometries.ElementAt(1));
+ Polygon polygonReconstituted = Assert.IsType(geoCollection.Geometries.ElementAt(2));
+
+
+ // ASSERT
+
+ Assert.True(lineStringReconstituted.Equals(linearRing), "Reconstituted LinearRing is not equivalent to the original one");
+ Assert.True(polygonReconstituted.Equals(polygon), "Reconstituted Polygon is not equivalent to the original one");
+ Assert.True(pointReconstituted.Equals(point), "Reconstituted Point is not equivalent to the original one");
+ Assert.Equal(40, point.Coordinates.Longitude);
+ Assert.Equal(10, point.Coordinates.Latitude);
+ }
+
+
+
+ [Fact]
+ public void GeometryCollectionTest_ToBinary_BigEndian()
+ {
+ // ARRANGE
+
+ WkbGeometryCollectionUnitTester.FetchDefaultGeometryCollectionTest(out Point point,
+ out LinearRing linearRing,
+ out Polygon polygon,
+ out GeometryCollection geomColl);
+
+ // ACT
+ byte[] bytes = WkbConverter.ToBinary(geomColl, Endianness.BIG);
+
+
+ Geometry geo = WkbConverter.FromBinary(bytes);
+
+
+ GeometryCollection geoCollection = Assert.IsType(geo);
+ Point pointReconstituted = Assert.IsType(geoCollection.Geometries.ElementAt(0));
+ LineString lineStringReconstituted = Assert.IsType(geoCollection.Geometries.ElementAt(1));
+ Polygon polygonReconstituted = Assert.IsType(geoCollection.Geometries.ElementAt(2));
+
+
+ // ASSERT
+
+ Assert.True(lineStringReconstituted.Equals(linearRing), "Reconstituted LinearRing is not equivalent to the original one");
+ Assert.True(polygonReconstituted.Equals(polygon), "Reconstituted Polygon is not equivalent to the original one");
+ Assert.True(pointReconstituted.Equals(point), "Reconstituted Point is not equivalent to the original one");
+ Assert.Equal(40, point.Coordinates.Longitude);
+ Assert.Equal(10, point.Coordinates.Latitude);
+ }
+
+ #endregion Tests
+ }
+}
\ No newline at end of file
diff --git a/GeoJSON.Tests/WkbBaseUnitTester.cs b/GeoJSON.Tests/WkbBaseUnitTester.cs
new file mode 100644
index 0000000..721871a
--- /dev/null
+++ b/GeoJSON.Tests/WkbBaseUnitTester.cs
@@ -0,0 +1,37 @@
+using System.Globalization;
+using System;
+
+namespace GeoJSON.Tests
+{
+ public class WkbBaseUnitTester
+ {
+
+ #region Convertion Methods
+
+ protected 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;
+ }
+
+ protected static string ByteArrayTohexString(byte[] bytes)
+ {
+ return BitConverter.ToString(bytes).Replace("-", "");
+ }
+
+ #endregion Convertion Methods
+
+ }
+}
\ No newline at end of file
diff --git a/GeoJSON.Tests/WkbPointUnitTester.cs b/GeoJSON.Tests/WkbPointUnitTester.cs
new file mode 100644
index 0000000..9ff688d
--- /dev/null
+++ b/GeoJSON.Tests/WkbPointUnitTester.cs
@@ -0,0 +1,133 @@
+using BAMCIS.GeoJSON;
+using BAMCIS.GeoJSON.Wkb;
+using Xunit;
+
+namespace GeoJSON.Tests
+{
+ public class WkbPointUnitTester : WkbBaseUnitTester
+ {
+ [Fact]
+ public void WkbConverterTest_ToBinary_BigEndian()
+ {
+ // ARRANGE
+ byte[] expectedBytes = HexStringToByteArray("000000000140000000000000004010000000000000");
+ var point = new Point(new Coordinate(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());
+ }
+
+ [Fact]
+ public void PointTest_Conversion()
+ {
+ // ARRANGE
+ var point = new Point(new Coordinate(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
+ var point = new Point(new Coordinate(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_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_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_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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/GeoJSON.Tests/WkbUnitTests.cs b/GeoJSON.Tests/WkbUnitTests.cs
index c8cafbe..cad1f4f 100644
--- a/GeoJSON.Tests/WkbUnitTests.cs
+++ b/GeoJSON.Tests/WkbUnitTests.cs
@@ -9,141 +9,9 @@
namespace GeoJSON.Tests
{
- public class WkbUnitTests
+ public class WkbUnitTests : WkbBaseUnitTester
{
- #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()
@@ -158,10 +26,10 @@ public void LineStringTest_FromBinary_BigEndian()
// 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));
+ Assert.True(lineString.LineSegments.Count() == 2, "Length of the LineSegment does not match");
+ Assert.Equal(new Coordinate(30, 10), lineString.LineSegments.ElementAt(0).ElementAt(0).Coordinates);
+ Assert.Equal(new Coordinate(10, 30), lineString.LineSegments.ElementAt(1).ElementAt(0).Coordinates);
+ Assert.Equal(new Coordinate(40, 40), lineString.LineSegments.ElementAt(1).ElementAt(1).Coordinates);
}
[Fact]
@@ -177,10 +45,10 @@ public void LineStringTest_FromBinary_LittleEndian()
// 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));
+ Assert.True(lineString.LineSegments.Count() == 2, "Length of the LineSegment does not match");
+ Assert.Equal(new Coordinate(30, 10), lineString.LineSegments.ElementAt(0).ElementAt(0).Coordinates);
+ Assert.Equal(new Coordinate(10, 30), lineString.LineSegments.ElementAt(1).ElementAt(0).Coordinates);
+ Assert.Equal(new Coordinate(40, 40), lineString.LineSegments.ElementAt(1).ElementAt(1).Coordinates);
}
[Fact]
@@ -196,10 +64,10 @@ public void LineStringTestWithDoubles_FromBinary_BigEndian()
// 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));
+ Assert.Equal(4, lineString.LineSegments.Count());
+ Assert.Equal(new Coordinate(30.1234, 10.6), lineString.LineSegments.ElementAt(0).ElementAt(0).Coordinates);
+ Assert.Equal(new Coordinate(10.77, 30.85), lineString.LineSegments.ElementAt(1).ElementAt(0).Coordinates);
+ Assert.Equal(new Coordinate(19, 77), lineString.LineSegments.ElementAt(3).ElementAt(1).Coordinates);
}
[Fact]
@@ -207,15 +75,14 @@ public void LineStringTest_ToBinary_BigEndian()
{
// ARRANGE
- // LINESTRING(30 10, 10 30, 40 40)
- byte[] bytes = HexStringToByteArray("000000000200000003403E00000000000040240000000000004024000000000000403E00000000000040440000000000004044000000000000");
+ var geo = new LineString(new List{ new Point(new Coordinate(30, 10)), new Point(new Coordinate(10, 30)), new Point(new Coordinate(40, 40)) } );
// ACT
- Geometry geo = WkbConverter.FromBinary(bytes);
- byte[] newBytes = WkbConverter.ToBinary(geo, Endianness.BIG);
+ byte[] bytes = WkbConverter.ToBinary(geo, Endianness.BIG);
+ Geometry geoReconstituted = WkbConverter.FromBinary(bytes);
// ASSERT
- Assert.Equal(bytes, newBytes);
+ Assert.True(geo.Equals(geoReconstituted), "Geometries are not equivalent.");
}
[Fact]
@@ -223,15 +90,14 @@ public void LineStringTest_ToBinary_LittleEndian()
{
// ARRANGE
- // LINESTRING(30 10, 10 30, 40 40)
- byte[] bytes = HexStringToByteArray("0102000000030000000000000000003e40000000000000244000000000000024400000000000003e4000000000000044400000000000004440");
+ var geo = new LineString(new List { new Point(new Coordinate(30, 10)), new Point(new Coordinate(10, 30)), new Point(new Coordinate(40, 40)) });
// ACT
- Geometry geo = WkbConverter.FromBinary(bytes);
- byte[] newBytes = WkbConverter.ToBinary(geo, Endianness.LITTLE);
+ byte[] bytes = WkbConverter.ToBinary(geo, Endianness.LITTLE);
+ Geometry geoReconstituted = WkbConverter.FromBinary(bytes);
// ASSERT
- Assert.Equal(bytes, newBytes);
+ Assert.True(geo.Equals(geoReconstituted), "Geometries are not equivalent.");
}
[Fact]
@@ -240,17 +106,16 @@ 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");
+ var geo = new LineString(new List { new Point(new Coordinate(30, 10)), new Point(new Coordinate(10, 30)), new Point(new Coordinate(40, 40)) });
// ACT
- Geometry geo = WkbConverter.FromBinary(bytes);
- byte[] newBytes = WkbConverter.ToBinary(geo, Endianness.BIG);
+ byte[] bytes = WkbConverter.ToBinary(geo, Endianness.BIG);
+ Geometry geoReconstituted = WkbConverter.FromBinary(bytes);
// ASSERT
- Assert.Equal(bytes, newBytes);
+ Assert.True(geo.Equals(geoReconstituted), "Geometries are not equivalent.");
}
- #endregion
#region MultiLineString Tests
@@ -267,10 +132,10 @@ public void MultiLineStringTest_FromBinary_BigEndian()
// 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);
+ Assert.Equal(2, lineString.LineStrings.Count());
+ LineString ls1 = Assert.IsType(lineString.LineStrings.ElementAt(1));
+ Assert.Equal(40, ls1.LineSegments.ElementAt(2).ElementAt(0).Coordinates.Longitude);
+ Assert.Equal(20, ls1.LineSegments.ElementAt(2).ElementAt(0).Coordinates.Latitude);
}
[Fact]
@@ -286,10 +151,10 @@ public void MultiLineStringTest_FromBinary_LittleEndian()
// 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);
+ Assert.Equal(2, lineString.LineStrings.Count());
+ LineString ls1 = Assert.IsType(lineString.LineStrings.ElementAt(1));
+ Assert.Equal(40, ls1.LineSegments.ElementAt(2).ElementAt(0).Coordinates.Longitude);
+ Assert.Equal(20, ls1.LineSegments.ElementAt(2).ElementAt(0).Coordinates.Latitude);
}
[Fact]
@@ -297,11 +162,12 @@ 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");
+ // MULTILINESTRING((10 10,20 20,10 40),(40 40,30 30,40 20,30 10))
+ byte[] bytes = HexStringToByteArray("0000000005000000020000000002000000044024000000000000402400000000000040340000000000004034000000000000403400000000000040340000000000004024000000000000404400000000000000000000020000000640440000000000004044000000000000403E000000000000403E000000000000403E000000000000403E0000000000004044000000000000403400000000000040440000000000004034000000000000403E0000000000004024000000000000");
// ACT
Geometry geo = WkbConverter.FromBinary(bytes);
+
byte[] newBytes = WkbConverter.ToBinary(geo, Endianness.BIG);
// ASSERT
@@ -357,8 +223,8 @@ public void MultiPointTest_FromBinary_BigEndian()
// ASSERT
MultiPoint mp = Assert.IsType(geo);
- Assert.Equal(21.06, mp.Coordinates.ElementAt(0).Longitude);
- Assert.Equal(19.77, mp.Coordinates.ElementAt(0).Latitude);
+ Assert.Equal(21.06, mp.Points.ElementAt(0).GetLongitude());
+ Assert.Equal(19.77, mp.Points.ElementAt(0).GetLatitude());
}
[Fact]
@@ -527,101 +393,6 @@ public void PolygonTest_ToBinary_BigEndian()
#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.Tests/geometrycollection.json b/GeoJSON.Tests/geometrycollection.json
deleted file mode 100644
index 7870c83..0000000
--- a/GeoJSON.Tests/geometrycollection.json
+++ /dev/null
@@ -1,48 +0,0 @@
-{
- "type": "GeometryCollection",
- "geometries": [
- {
- "type": "LineString",
- "coordinates": [
- [ 102.0, 0.0 ],
- [ 103.0, 1.0 ],
- [ 104.0, 0.0 ],
- [ 105.0, 1.0 ]
- ]
- },
- {
- "type": "Polygon",
- "coordinates": [
- [
- [ -10.0, -10.0 ],
- [ 10.0, -10.0 ],
- [ 10.0, 10.0 ],
- [ -10.0, -10.0 ]
- ]
- ]
- },
- {
- "type": "MultiPolygon",
- "coordinates": [
- [
- [
- [ 180.0, 40.0 ],
- [ 180.0, 50.0 ],
- [ 170.0, 50.0 ],
- [ 170.0, 40.0 ],
- [ 180.0, 40.0 ]
- ]
- ],
- [
- [
- [ -170.0, 40.0 ],
- [ -170.0, 50.0 ],
- [ -180.0, 50.0 ],
- [ -180.0, 40.0 ],
- [ -170.0, 40.0 ]
- ]
- ]
- ]
- }
- ]
-}
\ No newline at end of file
diff --git a/GeoJSON.Tests/multilinestring.json b/GeoJSON.Tests/multilinestring.json
index c998174..9dbd4e2 100644
--- a/GeoJSON.Tests/multilinestring.json
+++ b/GeoJSON.Tests/multilinestring.json
@@ -1,13 +1,169 @@
-{
+{
"type": "MultiLineString",
"coordinates": [
[
- [ 170.0, 45.0 ],
- [ 180.0, 45.0 ]
+ [
+ 0.0,
+ 0.0
+ ],
+ [
+ 0.0,
+ 1.0
+ ],
+ [
+ 1.0,
+ 0.0
+ ],
+ [
+ 1.0,
+ 1.0
+ ],
+ [
+ 0.0,
+ 0.0
+ ],
+ [
+ 0.0,
+ 1.0
+ ],
+ [
+ 1.0,
+ 0.0
+ ],
+ [
+ 1.0,
+ 1.0
+ ],
+ [
+ 0.0,
+ 0.0
+ ],
+ [
+ 0.0,
+ 1.0
+ ],
+ [
+ 1.0,
+ 0.0
+ ],
+ [
+ 1.0,
+ 1.0
+ ],
+ [
+ 0.0,
+ 0.0
+ ],
+ [
+ 0.0,
+ 1.0
+ ],
+ [
+ 1.0,
+ 0.0
+ ],
+ [
+ 1.0,
+ 1.0
+ ]
],
[
- [ -180.0, 45.0 ],
- [ -170.0, 45.0 ]
+ [
+ 5.0,
+ 0.0
+ ],
+ [
+ 5.0,
+ 2.0
+ ],
+ [
+ 5.0,
+ 0.0
+ ],
+ [
+ 5.0,
+ 3.0
+ ],
+ [
+ 6.0,
+ 0.0
+ ],
+ [
+ 6.0,
+ 2.0
+ ],
+ [
+ 6.0,
+ 0.0
+ ],
+ [
+ 6.0,
+ 3.0
+ ],
+ [
+ 5.0,
+ 0.0
+ ],
+ [
+ 5.0,
+ 2.0
+ ],
+ [
+ 5.0,
+ 0.0
+ ],
+ [
+ 5.0,
+ 3.0
+ ],
+ [
+ 6.0,
+ 0.0
+ ],
+ [
+ 6.0,
+ 2.0
+ ],
+ [
+ 6.0,
+ 0.0
+ ],
+ [
+ 6.0,
+ 3.0
+ ],
+ [
+ 5.0,
+ 0.0
+ ],
+ [
+ 5.0,
+ 2.0
+ ],
+ [
+ 5.0,
+ 0.0
+ ],
+ [
+ 5.0,
+ 3.0
+ ],
+ [
+ 6.0,
+ 0.0
+ ],
+ [
+ 6.0,
+ 2.0
+ ],
+ [
+ 6.0,
+ 0.0
+ ],
+ [
+ 6.0,
+ 3.0
+ ]
]
]
}
\ No newline at end of file
diff --git a/GeoJSON.Tests/polygon.json b/GeoJSON.Tests/polygon.json
deleted file mode 100644
index 308ba3e..0000000
--- a/GeoJSON.Tests/polygon.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "type": "Polygon",
- "coordinates": [
- [
- [ -10.0, -10.0 ],
- [ 10.0, -10.0 ],
- [ 10.0, 10.0 ],
- [ -10.0, -10.0 ]
- ]
- ]
-}
\ No newline at end of file
diff --git a/GeoJSON.sln b/GeoJSON.sln
index 3ae0fd1..00cb0ad 100644
--- a/GeoJSON.sln
+++ b/GeoJSON.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 16
-VisualStudioVersion = 16.0.30011.22
+# Visual Studio Version 17
+VisualStudioVersion = 17.7.34003.232
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GeoJSON", "GeoJSON\GeoJSON.csproj", "{13B19F69-F3B6-497E-A9E0-D6DFCC533EC6}"
EndProject
@@ -9,9 +9,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GeoJSON.Tests", "GeoJSON.Te
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{D5ED7550-3703-4250-B3F5-B3DFB135A6A5}"
ProjectSection(SolutionItems) = preProject
- README.md = README.md
+ .editorconfig = .editorconfig
EndProjectSection
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Extensions", "Extensions\Extensions.csproj", "{AA7705B8-F518-41C2-8E72-5E22962C7096}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -26,6 +28,10 @@ Global
{457C0EFB-99D9-46DB-8949-B5B90A855847}.Debug|Any CPU.Build.0 = Debug|Any CPU
{457C0EFB-99D9-46DB-8949-B5B90A855847}.Release|Any CPU.ActiveCfg = Release|Any CPU
{457C0EFB-99D9-46DB-8949-B5B90A855847}.Release|Any CPU.Build.0 = Release|Any CPU
+ {AA7705B8-F518-41C2-8E72-5E22962C7096}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {AA7705B8-F518-41C2-8E72-5E22962C7096}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {AA7705B8-F518-41C2-8E72-5E22962C7096}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {AA7705B8-F518-41C2-8E72-5E22962C7096}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/GeoJSON/Feature.cs b/GeoJSON/Feature.cs
index ca7fa78..981a49a 100644
--- a/GeoJSON/Feature.cs
+++ b/GeoJSON/Feature.cs
@@ -33,6 +33,7 @@ public class Feature : GeoJson
[JsonProperty(PropertyName = "id", NullValueHandling = NullValueHandling.Ignore)]
public FeatureId Id { get; }
+
#endregion
#region Constructors
@@ -43,11 +44,12 @@ public class Feature : GeoJson
/// The geometry to create the feature from
/// The feature properties
[JsonConstructor]
- public Feature(Geometry geometry, IDictionary properties = null, IEnumerable boundingBox = null, FeatureId id = null) : base(GeoJsonType.Feature, geometry == null ? false : geometry.IsThreeDimensional(), boundingBox)
+ public Feature(Geometry geometry, IDictionary properties = null, FeatureId id = null) : base(GeoJsonType.Feature, geometry != null && geometry.IsThreeDimensional())
{
this.Geometry = geometry; // Geometry can be null
this.Properties = properties ?? new Dictionary();
this.Id = id; // id can be null
+
}
#endregion
@@ -87,13 +89,13 @@ public override bool Equals(object obj)
bool bBoxEqual = true;
- if (this.BoundingBox != null && other.BoundingBox != null)
+ if (this?.Geometry?.BoundingBox != null && other?.Geometry?.BoundingBox != null)
{
- bBoxEqual = this.BoundingBox.SequenceEqual(other.BoundingBox);
+ bBoxEqual = this.Geometry.BoundingBox.SequenceEqual(other.Geometry.BoundingBox);
}
else
{
- bBoxEqual = (this.BoundingBox == null && other.BoundingBox == null);
+ bBoxEqual = (this?.Geometry?.BoundingBox == null && other?.Geometry?.BoundingBox == null);
}
bool propertiesEqual = true;
@@ -109,14 +111,14 @@ public override bool Equals(object obj)
return this.Type == other.Type &&
- this.Geometry == other.Geometry &&
+ this?.Geometry == other?.Geometry &&
bBoxEqual &&
propertiesEqual;
}
public override int GetHashCode()
{
- return Hashing.Hash(this.Type, this.Geometry, this.BoundingBox, this.Properties);
+ return Hashing.Hash(this.Type, this.Geometry, this.Geometry.BoundingBox, this.Properties);
}
public static bool operator ==(Feature left, Feature right)
diff --git a/GeoJSON/FeatureCollection.cs b/GeoJSON/FeatureCollection.cs
index cd4671f..338ceee 100644
--- a/GeoJSON/FeatureCollection.cs
+++ b/GeoJSON/FeatureCollection.cs
@@ -1,6 +1,7 @@
using BAMCIS.GeoJSON.Serde;
using Newtonsoft.Json;
using System;
+using System.Collections;
using System.Collections.Generic;
using System.Linq;
@@ -9,8 +10,8 @@ namespace BAMCIS.GeoJSON
///
/// Represents a collection of feature objects
///
- [JsonConverter(typeof(InheritanceBlockerConverter))]
- public class FeatureCollection : GeoJson
+ [JsonConverter(typeof(FeatureCollectionConverter))]
+ public class FeatureCollection : GeoJson, IEnumerable
{
#region Public Properties
@@ -20,6 +21,11 @@ public class FeatureCollection : GeoJson
[JsonProperty(PropertyName = "features")]
public IEnumerable Features { get; }
+
+ [JsonProperty(PropertyName = "BoundingBox")]
+ [JsonIgnore]
+ public Rectangle BoundingBox { get; private set; }
+
#endregion
#region Constructors
@@ -29,20 +35,77 @@ public class FeatureCollection : GeoJson
///
/// The features that are part of the feature collection
[JsonConstructor]
- public FeatureCollection(IEnumerable features, IEnumerable boundingBox = null) : base(GeoJsonType.FeatureCollection, features.Any(x => x.IsThreeDimensional()), boundingBox)
+ public FeatureCollection(IEnumerable features) : base(GeoJsonType.FeatureCollection, features.Any(x => x.IsThreeDimensional()))
{
- this.Features = features ?? throw new ArgumentNullException("features");
+ this.Features = features ?? throw new ArgumentNullException(nameof(features));
+
+ this.BoundingBox = FetchBoundingBox();
+
+ }
+
+ public Rectangle FetchBoundingBox()
+ {
+ double MaxLatitude = double.MinValue;
+ double MaxLongitude = double.MinValue;
+ double MinLatitude = double.MaxValue;
+ double MinLongitude = double.MaxValue;
+
+ foreach (Feature feature in this.Features)
+ {
+
+ if (feature.Geometry?.BoundingBox != null)
+ {
+ if (MaxLatitude < (feature?.Geometry?.BoundingBox?.MaxLatitude ?? 0.0))
+ {
+ MaxLatitude = (double) ( feature?.Geometry?.BoundingBox.MaxLatitude );
+ }
+
+ if (MaxLongitude < (feature?.Geometry?.BoundingBox?.MaxLongitude ?? 0.0 ))
+ {
+ MaxLongitude = (double)feature.Geometry?.BoundingBox.MaxLongitude;
+ }
+
+ if (MinLatitude > (feature?.Geometry?.BoundingBox?.MinLatitude ?? 0.0 ))
+ {
+ MinLatitude = (double) feature?.Geometry.BoundingBox.MinLatitude;
+ }
+
+ if (MinLongitude > (feature?.Geometry?.BoundingBox?.MinLongitude ?? 0.0 ))
+ {
+ MinLongitude = (double) feature?.Geometry?.BoundingBox.MinLongitude;
+ }
+ }
+ else
+ {
+ MaxLatitude = 0;
+ MaxLongitude = 0;
+ MinLatitude = 0;
+ MinLongitude = 0;
+ }
+ }
+
+ var LL = new Point(new Coordinate(MinLongitude, MinLatitude));
+ var LR = new Point(new Coordinate(MaxLongitude, MinLatitude));
+ var UL = new Point(new Coordinate(MinLongitude, MaxLatitude));
+ var UR = new Point(new Coordinate(MaxLongitude, MaxLatitude));
+
+ return new Rectangle(LL, LR, UL, UR);
}
#endregion
#region Public Methods
+ #region Conversion Methods
public new static FeatureCollection FromJson(string json)
{
return JsonConvert.DeserializeObject(json);
}
+ #endregion Conversion Methods
+
+ #region Equality Operations
+
public override bool Equals(object obj)
{
if (ReferenceEquals(this, obj))
@@ -109,6 +172,24 @@ public override int GetHashCode()
return !(left == right);
}
- #endregion
+ #endregion Equality Operations
+
+ #endregion Public Methods
+
+ #region Enumerable
+ public IEnumerator GetEnumerator()
+ {
+ foreach (Feature feat in this.Features)
+ {
+ yield return feat;
+ }
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ #endregion Enumerable
}
}
diff --git a/GeoJSON/GeoJSON.csproj b/GeoJSON/GeoJSON.csproj
index 4600b0c..4a43a39 100644
--- a/GeoJSON/GeoJSON.csproj
+++ b/GeoJSON/GeoJSON.csproj
@@ -1,7 +1,6 @@
-
- netstandard1.6;netstandard2.0;net45
+ net7.0
1.6
BAMCIS.GeoJSON
2.3.1
@@ -11,7 +10,8 @@
bamcis.io
true
true
-
+
+
https://github.com/bamcis-io/GeoJSON
https://github.com/bamcis-io/GeoJSON
Git
@@ -20,21 +20,18 @@
true
GeoJSON.snk
false
-
+
+
MIT
-
true
true
snupkg
-
-
-
+
+
-
-
+
-
-
+
\ No newline at end of file
diff --git a/GeoJSON/GeoJson.cs b/GeoJSON/GeoJson.cs
index 155034c..452b2cc 100644
--- a/GeoJSON/GeoJson.cs
+++ b/GeoJSON/GeoJson.cs
@@ -7,6 +7,8 @@
namespace BAMCIS.GeoJSON
{
+
+
///
/// A base abstract class for the implementation of GeoJson
///
@@ -17,7 +19,7 @@ public abstract class GeoJson
private static readonly Dictionary typeToDerivedType;
private static readonly Dictionary derivedTypeToType;
- private bool is3D;
+ private readonly bool is3D;
#endregion
@@ -29,19 +31,6 @@ public abstract class GeoJson
[JsonProperty(PropertyName = "type")]
public GeoJsonType Type { get; }
- ///
- /// A GeoJSON object MAY have a member named "bbox" to include
- /// information on the coordinate range for its Geometries, Features, or
- /// FeatureCollections.The value of the bbox member MUST be an array of
- /// length 2*n where n is the number of dimensions represented in the
- /// contained geometries, with all axes of the most southwesterly point
- /// followed by all axes of the more northeasterly point. The axes order
- /// of a bbox follows the axes order of geometries.
- ///
- /// Takes the form [west, south, east, north] for 2D or of the form [west, south, min-altitude, east, north, max-altitude] for 3D
- ///
- [JsonProperty(PropertyName = "bbox", NullValueHandling = NullValueHandling.Ignore)]
- public IEnumerable BoundingBox { get; }
#endregion
@@ -54,6 +43,7 @@ static GeoJson()
{
typeToDerivedType = new Dictionary()
{
+ { typeof(LineSegment), GeoJsonType.LineSegment },
{ typeof(LineString), GeoJsonType.LineString },
{ typeof(MultiLineString), GeoJsonType.MultiLineString },
{ typeof(MultiPoint), GeoJsonType.MultiPoint },
@@ -72,25 +62,10 @@ static GeoJson()
/// Base constructor that all derived classes must implement
///
/// The type of the GeoJson object
- protected GeoJson(GeoJsonType type, bool is3D, IEnumerable boundingBox = null)
+ protected GeoJson(GeoJsonType type, bool is3D)
{
this.Type = type;
- this.BoundingBox = boundingBox;
this.is3D = is3D;
-
- if (this.BoundingBox != null)
- {
- int length = boundingBox.Count();
-
- if (this.is3D && length != 6)
- {
- throw new ArgumentOutOfRangeException("boundingBox", "The bounding box must contain 6 elements for a 3D GeoJSON object.");
- }
- else if (!this.is3D && length != 4)
- {
- throw new ArgumentOutOfRangeException("boundingBox", "The bounding box must contain 4 elements for a 2D GeoJSON object.");
- }
- }
}
#endregion
diff --git a/GeoJSON/GeoJsonType.cs b/GeoJSON/GeoJsonType.cs
index d7ed40d..f35fa1a 100644
--- a/GeoJSON/GeoJsonType.cs
+++ b/GeoJSON/GeoJsonType.cs
@@ -12,6 +12,10 @@ public enum GeoJsonType
LineString,
+ LineSegment,
+
+ LinearRing,
+
MultiLineString,
Polygon,
diff --git a/GeoJSON/Geometry.cs b/GeoJSON/Geometry.cs
index 003a131..6bac4de 100644
--- a/GeoJSON/Geometry.cs
+++ b/GeoJSON/Geometry.cs
@@ -5,19 +5,30 @@
using System.Collections.Generic;
using System.Linq;
+
+
namespace BAMCIS.GeoJSON
{
+
+
///
/// A base abstract class for geometry types
///
[JsonConverter(typeof(GeometryConverter))]
- public abstract class Geometry : GeoJson
+ public abstract class Geometry: GeoJson
{
#region Private Fields
private static readonly Dictionary typeToDerivedType;
private static readonly Dictionary derivedTypeToType;
+ [JsonProperty(PropertyName = "BoundingBox")]
+ [JsonIgnore]
+ public abstract Rectangle BoundingBox { get; }
+
+ [JsonIgnore]
+ internal Rectangle _BoundingBox { get; set; }
+
#endregion
#region Constructors
@@ -29,7 +40,9 @@ static Geometry()
{
typeToDerivedType = new Dictionary()
{
+ { typeof(LineSegment), GeoJsonType.LineSegment },
{ typeof(LineString), GeoJsonType.LineString },
+ { typeof(LinearRing), GeoJsonType.LinearRing },
{ typeof(MultiLineString), GeoJsonType.MultiLineString },
{ typeof(MultiPoint), GeoJsonType.MultiPoint },
{ typeof(MultiPolygon), GeoJsonType.MultiPolygon },
@@ -46,7 +59,42 @@ static Geometry()
///
/// The GeoJson type
[JsonConstructor]
- protected Geometry(GeoJsonType type, bool is3D, IEnumerable boundingBox = null) : base(type, is3D, boundingBox)
+ protected Geometry(GeoJsonType type, bool is3D) : base(type, is3D)
+ {
+ ConstructorEvaluator(type);
+ }
+
+
+ ///
+ /// Converts angles from Radians to Degrees
+ ///
+ ///
+ ///
+ public static double RadiansToDegrees(double radians)
+ {
+ return ( radians * 180 / Math.PI );
+ }
+
+ ///
+ /// Converts angles from Degrees to Radians
+ ///
+ ///
+ ///
+ public static double DegreesToRadians(double radians)
+ {
+ return ( radians * 180 / Math.PI );
+ }
+
+ ///
+ /// Each inherited class must implement this constructor
+ ///
+ /// The GeoJson type
+ protected Geometry(IEnumerable geometries = null) : base(GeoJsonType.Feature, geometries.FirstOrDefault()?.IsThreeDimensional() ?? false)
+ {
+
+ }
+
+ protected static void ConstructorEvaluator(GeoJsonType type)
{
if (!derivedTypeToType.ContainsKey(type))
{
@@ -58,9 +106,13 @@ protected Geometry(GeoJsonType type, bool is3D, IEnumerable boundingBox
#region Public Methods
+ #region Converters
+
public static new Geometry FromJson(string json)
{
- return JsonConvert.DeserializeObject(json);
+ var geometry = JsonConvert.DeserializeObject(json);
+
+ return geometry;
}
///
@@ -93,6 +145,8 @@ public byte[] ToWkb(Endianness endianness = Endianness.LITTLE)
return WkbConverter.ToBinary(this, endianness);
}
+
+
///
/// Gets the appropriate class type corresponding to the enum
/// representing the type
@@ -111,6 +165,10 @@ public byte[] ToWkb(Endianness endianness = Endianness.LITTLE)
}
}
+ #endregion Converters
+
+ #region Equality Evaluators
+
public static bool operator ==(Geometry left, Geometry right)
{
if (ReferenceEquals(left, right))
@@ -135,6 +193,10 @@ public byte[] ToWkb(Endianness endianness = Endianness.LITTLE)
public abstract override int GetHashCode();
+
+ #endregion Equality Evaluators
+
#endregion
}
+
}
diff --git a/GeoJSON/GeometryCollection.cs b/GeoJSON/GeometryCollection.cs
index 3ac1af4..6c42a0a 100644
--- a/GeoJSON/GeometryCollection.cs
+++ b/GeoJSON/GeometryCollection.cs
@@ -23,7 +23,11 @@ public class GeometryCollection : Geometry
[JsonProperty(PropertyName = "geometries")]
public IEnumerable Geometries { get; }
- #endregion
+ [JsonProperty(PropertyName = "BoundingBox")]
+ [JsonIgnore]
+ public override Rectangle BoundingBox { get; }
+
+ #endregion Public Properties
#region Constructors
@@ -32,20 +36,29 @@ public class GeometryCollection : Geometry
///
/// The geometries that are part of the collection
[JsonConstructor]
- public GeometryCollection(IEnumerable geometries, IEnumerable boundingBox = null) : base(GeoJsonType.GeometryCollection, geometries.Any(x => x.IsThreeDimensional()), boundingBox)
+ public GeometryCollection(IEnumerable geometries) : base(GeoJsonType.GeometryCollection, geometries.Any(x => x.IsThreeDimensional()))
{
- this.Geometries = geometries ?? throw new ArgumentNullException("geometries");
+ this.Geometries = geometries.ToList() ?? throw new ArgumentNullException("geometries");
+
+ this.BoundingBox = FetchBoundingBox();
}
#endregion
#region Public Methods
+ #region Converters
+
public new static GeometryCollection FromJson(string json)
{
return JsonConvert.DeserializeObject(json);
}
+
+ #endregion Converters
+
+ #region Equality Evaluators
+
public override bool Equals(object obj)
{
if (ReferenceEquals(this, obj))
@@ -112,6 +125,68 @@ public override int GetHashCode()
return !(left == right);
}
+ #endregion Equality Evaluators
+
+ #region Topological Operations
+
+ public Rectangle FetchBoundingBox()
+ {
+ double MaxLatitude = double.MinValue;
+ double MaxLongitude = double.MinValue;
+ double MinLatitude = double.MaxValue;
+ double MinLongitude = double.MaxValue;
+ bool allGeomsHaveEmptyBoundingBox = true;
+ foreach (var geometry in this.Geometries)
+ {
+ var bbox = geometry?.BoundingBox ?? null;
+
+ if (bbox != null)
+ {
+ allGeomsHaveEmptyBoundingBox = false;
+ if (MaxLatitude < bbox.MaxLatitude)
+ {
+ MaxLatitude = bbox.MaxLatitude;
+ }
+
+ if (MaxLongitude < bbox.MaxLongitude)
+ {
+ MaxLongitude = bbox.MaxLongitude;
+ }
+
+ if (MinLatitude > bbox.MinLatitude)
+ {
+ MinLatitude = bbox.MinLatitude;
+ }
+
+ if (MinLongitude > bbox.MinLongitude)
+ {
+ MinLongitude = bbox.MinLongitude;
+ }
+ }
+
+ }
+
+ if (allGeomsHaveEmptyBoundingBox)
+ {
+ return null;
+ }
+ else
+ {
+ Point LL = new Point(new Coordinate(MinLongitude, MinLatitude));
+ Point LR = new Point(new Coordinate(MaxLongitude, MinLatitude));
+ Point UL = new Point(new Coordinate(MinLongitude, MaxLatitude));
+ Point UR = new Point(new Coordinate(MaxLongitude, MaxLatitude));
+
+ return new Rectangle(LL, LR, UL, UR);
+
+ }
+
+ }
+
+
+ #endregion Topological Operations
+
+
#endregion
}
}
diff --git a/GeoJSON/GeometryEnumerator.cs b/GeoJSON/GeometryEnumerator.cs
new file mode 100644
index 0000000..c9d2bba
--- /dev/null
+++ b/GeoJSON/GeometryEnumerator.cs
@@ -0,0 +1,67 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace BAMCIS.GeoJSON
+{
+ public class GeometryEnumerator : IEnumerator where T: Geometry
+ {
+
+ public int Position { get; private set; } = 0;
+
+ public T[] Geometries { get; private set; }
+
+ public GeometryEnumerator(IEnumerable geometries)
+ {
+ Geometries = geometries.ToArray();
+ }
+
+ public T Current()
+ {
+ return this.Geometries[Position];
+ }
+
+ object IEnumerator.Current
+ {
+ get
+ {
+ return Current();
+ }
+ }
+
+ T IEnumerator.Current
+ {
+ get
+ {
+ return Current();
+ }
+ }
+
+ public void Dispose()
+ {
+ // Suppress finalization.
+ GC.SuppressFinalize(this);
+
+ }
+
+ public bool MoveNext()
+ {
+ this.Position ++;
+
+ if (this.Position < Geometries.Count())
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ public void Reset()
+ {
+ this.Position = 0;
+ }
+ }
+}
\ No newline at end of file
diff --git a/GeoJSON/GlobalSuppressions.cs b/GeoJSON/GlobalSuppressions.cs
new file mode 100644
index 0000000..a9d7852
--- /dev/null
+++ b/GeoJSON/GlobalSuppressions.cs
@@ -0,0 +1,16 @@
+// This file is used by Code Analysis to maintain SuppressMessage
+// attributes that are applied to this project.
+// Project-level suppressions either have no target or are given
+// a specific target and scoped to a namespace, type, member, etc.
+
+using System.Diagnostics.CodeAnalysis;
+
+[assembly: SuppressMessage("Style", "IDE0059:Atribuição desnecessária de um valor", Justification = "internal Policy", Scope = "member", Target = "~M:BAMCIS.GeoJSON.FeatureCollection.Equals(System.Object)~System.Boolean")]
+[assembly: SuppressMessage("Style", "IDE0059:Atribuição desnecessária de um valor", Justification = "internal Policy", Scope = "member", Target = "~M:BAMCIS.GeoJSON.GeometryCollection.Equals(System.Object)~System.Boolean")]
+[assembly: SuppressMessage("Style", "IDE0059:Atribuição desnecessária de um valor", Justification = "internal Policy", Scope = "member", Target = "~M:BAMCIS.GeoJSON.Polygon.Touches(BAMCIS.GeoJSON.Point,System.Double)~System.Boolean")]
+[assembly: SuppressMessage("Style", "IDE0037:Usar o nome do membro inferido", Justification = "internal Policy", Scope = "member", Target = "~M:BAMCIS.GeoJSON.Serde.PolygonConverter.WriteJson(Newtonsoft.Json.JsonWriter,System.Object,Newtonsoft.Json.JsonSerializer)")]
+[assembly: SuppressMessage("CodeQuality", "IDE0051:Remover membros privados não utilizados", Justification = "internal Policy", Scope = "member", Target = "~M:BAMCIS.GeoJSON.Point.Copy~BAMCIS.GeoJSON.Point")]
+[assembly: SuppressMessage("Usage", "CA2208:Instanciar exceções de argumentos corretamente", Justification = "internal Policy", Scope = "member", Target = "~M:BAMCIS.GeoJSON.Point.#ctor(System.Double,System.Double,System.Double)")]
+[assembly: SuppressMessage("Style", "IDE0090:Usar 'new(...)'", Justification = "internal Policy", Scope = "member", Target = "~M:BAMCIS.GeoJSON")]
+[assembly: SuppressMessage("Microsoft.Naming", "CA1707:IdentifiersShouldNotContainUnderscores", Justification = "Test methods require underscores for readability.", Scope = "namespaceanddescendants", Target = "~M:BAMCIS.GeoJSON")]
+[assembly: SuppressMessage("Style", "IDE0016:Use a expressão 'throw'", Justification = "internal Policy", Scope = "member", Target = "~M:BAMCIS.GeoJSON.MultiPoint.#ctor(System.Collections.Generic.IEnumerable{BAMCIS.GeoJSON.Point})")]
diff --git a/GeoJSON/ITopology.cs b/GeoJSON/ITopology.cs
new file mode 100644
index 0000000..db43e94
--- /dev/null
+++ b/GeoJSON/ITopology.cs
@@ -0,0 +1,44 @@
+using System;
+
+namespace BAMCIS.GeoJSON
+{
+ public interface ITouch
+ {
+ bool Touches(T other, double eps = double.MinValue * 100);
+ }
+
+ public interface IIntersects
+ {
+ bool Intersects(T other, double eps = double.MinValue * 100);
+ }
+
+ public interface IWithin
+ {
+ bool Within(T other, double eps = double.MinValue * 100);
+ }
+
+ public interface IContains
+ {
+ bool Contains(T other, double eps = double.MinValue * 100);
+ }
+
+ public interface IAdimTopology: IEquatable, ITouch, IWithin
+ {
+
+
+ }
+
+ public interface ILine: IEquatable, IIntersects, ITouch, IWithin
+ {
+
+ }
+
+
+
+ public interface ITopology: ILine, IContains
+ {
+
+ }
+
+ public interface IPolygon : ITouch, IContains, IIntersects { }
+}
\ No newline at end of file
diff --git a/GeoJSON/LineSegment.cs b/GeoJSON/LineSegment.cs
new file mode 100644
index 0000000..4bbd432
--- /dev/null
+++ b/GeoJSON/LineSegment.cs
@@ -0,0 +1,723 @@
+using BAMCIS.GeoJSON.Serde;
+using Extensions.ArrayExtensions;
+using Extensions.ListExtensions;
+using Newtonsoft.Json;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+
+namespace BAMCIS.GeoJSON
+{
+ ///
+ /// A LineSegment is composed solely of two points:
+ /// 1) an origin Point (p1)
+ ///
+ /// 2) a terminal Point (p2)
+ ///
+ [JsonConverter(typeof(InheritanceBlockerConverter))]
+ public class LineSegment: Geometry,
+ ILine,
+ ILine,
+ ILine,
+ ILine,
+ IEquatable,
+ IComparable,
+ IComparer,
+ IEnumerable,
+ IEnumerable
+
+ {
+ #region Properties
+
+ public IEnumerable Points {
+
+ get
+ {
+ return new Point[2] { P1, P2 };
+ }
+ }
+
+ [JsonProperty(PropertyName = "Initial Point")]
+ public Point P1 { get; set; }
+
+ [JsonProperty(PropertyName = "Final Point")]
+ public Point P2 { get; set; }
+
+ [JsonProperty(PropertyName = "Length")]
+ public double Length { get; set; }
+
+ public double MinLongitude {
+
+ get {
+ return Math.Min(P1.GetLongitude(), P2.GetLongitude());
+ }
+ }
+
+ public double MaxLongitude
+ {
+
+ get
+ {
+ return Math.Max(P1.GetLongitude(), P2.GetLongitude());
+ }
+ }
+
+
+ public double MinLatitude
+ {
+
+ get
+ {
+ return Math.Min(P1.GetLatitude(), P2.GetLatitude());
+ }
+ }
+
+ public double MaxLatitude
+ {
+
+ get
+ {
+ return Math.Max(P1.GetLatitude(), P2.GetLatitude());
+ }
+ }
+
+ [JsonProperty(PropertyName = "BoundingBox")]
+ public override Rectangle BoundingBox
+ {
+ get
+ {
+
+ if (this._BoundingBox == null)
+ {
+ this._BoundingBox = FetchBoundingBox();
+ }
+ return this._BoundingBox;
+ }
+ }
+
+ ///
+ /// Angle in radians of this lineSegment
+ ///
+ public double Angle { get; }
+
+
+ #endregion Properties
+
+
+ #region Constructors
+ [JsonConstructor]
+ public LineSegment(IEnumerable p1p2) : base(GeoJsonType.LineSegment, p1p2.First().Coordinates.HasElevation())
+ {
+
+ var P1P2List = p1p2.ToList();
+
+ if (P1P2List.Count != 2)
+ {
+ throw new ArgumentException($"{nameof(LineSegment)} only accepts an IEnumerable of size two");
+ }
+
+ this.P1 = P1P2List[0];
+
+ this.P2 = P1P2List[1];
+
+
+ var dx = ( P1P2List[0].GetLongitude() - P1P2List[1].GetLongitude() );
+
+ var dy = ( P1P2List[0].GetLatitude() - P1P2List[1].GetLatitude() );
+
+ this.Length = Math.Sqrt(Math.Pow(dx, 2) + Math.Pow(dy, 2));
+
+ this.Angle = Math.Atan2(dy, dx);
+
+ }
+
+ public LineSegment(IEnumerable coordinates) : this(coordinates.Select(c => c.ToPoint()))
+ {
+
+ }
+
+
+ public LineSegment(Point p1, Point p2) : base(GeoJsonType.LineSegment, p1.Coordinates.HasElevation())
+ {
+
+ this.P1 = p1;
+
+ this.P2 = p2;
+
+
+ var dx = ( p1.GetLongitude() - p2.GetLongitude() );
+
+ var dy = ( p1.GetLatitude() - p2.GetLatitude() );
+
+ this.Length = Math.Sqrt(Math.Pow(dx, 2) + Math.Pow(dy, 2));
+
+ this.Angle = Math.Atan2(dy, dx);
+
+ }
+
+
+ public Rectangle FetchBoundingBox()
+ {
+ var LL = new Point(new Coordinate(MinLongitude, MinLatitude));
+
+ var LR = new Point(new Coordinate(MaxLongitude, MinLatitude));
+
+ var UL = new Point(new Coordinate(MinLongitude, MaxLatitude));
+
+ var UR = new Point(new Coordinate(MaxLongitude, MaxLatitude));
+
+ return new Rectangle(LL, LR, UL, UR);
+ }
+
+
+ #endregion Constructors
+
+ #region Methods
+
+ #region Private Methods
+
+
+
+
+ #endregion Private Methods
+
+
+ #region Public Methods
+
+ #region Comparables
+
+ public bool Equals(Point other)
+ {
+ return false;
+ }
+
+
+ public bool Equals(LineSegment other)
+ {
+ return (this.P1.Equals(other.P1) &&
+ this.P2.Equals(other.P2)
+ );
+ }
+
+ public bool Equals(LinearRing other)
+ {
+ if (other.LineSegments.Count() > 1)
+ {
+ return false;
+ }
+ else
+ {
+ return this.Equals(other.LineSegments.First());
+ }
+ }
+
+ public bool Equals(LineString other)
+ {
+ if (other.LineSegments.Count() > 1)
+ {
+ return false;
+ }
+ else
+ {
+ return this.CompareTo(other.LineSegments.First()) == 0;
+ }
+ }
+
+ public bool Equals(Polygon other)
+ {
+ return false;
+ }
+
+ public override bool Equals(object obj)
+ {
+ var objCasted = obj as LineSegment;
+
+
+ if (ReferenceEquals(this, objCasted))
+ {
+ return true;
+ }
+
+ if (objCasted is null)
+ {
+ return false;
+ }
+
+ return this.Equals(objCasted);
+ }
+
+ public int CompareTo(LineSegment other)
+ {
+ if (this.Length < other.Length)
+ {
+ return -1;
+ }
+ else if (this.Length > other.Length)
+ {
+ return 1;
+ }
+ else{ return 0; }
+ }
+
+ public int Compare(LineSegment x, LineSegment y)
+ {
+ return x.CompareTo(y);
+ }
+
+ public override int GetHashCode()
+ {
+ return Tuple.Create(P1.GetHashCode(), P2.GetHashCode()).GetHashCode();
+ }
+
+ public static bool operator ==(LineSegment left, LineSegment right)
+ {
+ if (left is null)
+ {
+ return right is null;
+ }
+
+ return left.P1 == right.P1 && left.P2 == right.P2;
+ }
+
+ public static bool operator !=(LineSegment left, LineSegment right)
+ {
+ return !( left == right );
+ }
+
+ public static bool operator <(LineSegment left, LineSegment right)
+ {
+ return left.Length < right.Length;
+ }
+
+ public static bool operator <=(LineSegment left, LineSegment right)
+ {
+ return left is null || left.CompareTo(right) <= 0;
+ }
+
+ public static bool operator >(LineSegment left, LineSegment right)
+ {
+ return left is not null && left.CompareTo(right) > 0;
+ }
+
+ public static bool operator >=(LineSegment left, LineSegment right)
+ {
+ return left is null ? right is null : left.CompareTo(right) >= 0;
+ }
+
+ #endregion Comparables
+
+ #region Enumerators
+
+ public IEnumerator GetEnumerator()
+ {
+ foreach (Point point in Points)
+ {
+ yield return point;
+ }
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+
+ #endregion Enumerators
+
+ #region Topological Operations
+
+ internal bool HasElevation()
+ {
+ return this.Points.Any(p => p.Coordinates.HasElevation());
+ }
+ public bool Within(Polygon otherGeometry)
+ {
+ return this.Points.All(p => otherGeometry.Contains(p));
+ }
+
+ public bool Intersects(Polygon otherGeometry)
+ {
+ return this.Points.All(p => otherGeometry.Contains(p));
+ }
+
+ ///
+ /// The four endpoints are:
+ /// P1) (x0, y0)
+ /// P2) (x1,y1)
+ /// P3) (a0,b0)
+ /// P4) (a1,b1)
+ ///
+ /// The returned values xy and ab are the fractional distance along xy and ab
+ /// and are only defined when the result is true
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ private static bool FindIntersection(double x0, double y0,
+ double x1, double y1,
+ double a0, double b0,
+ double a1, double b1)
+ {
+
+ double xy;
+ double ab;
+
+ bool partial = false;
+ double denom = ( b0 - b1 ) * ( x0 - x1 ) - ( y0 - y1 ) * ( a0 - a1 );
+ if (denom == 0)
+ {
+ xy = -1;
+ ab = -1;
+
+ return false;
+ }
+ else
+ {
+ xy = ( a0 * ( y1 - b1 ) + a1 * ( b0 - y1 ) + x1 * ( b1 - b0 ) ) / denom;
+
+ partial = IsBetween(0, xy, 1);
+
+ if (partial)
+ {
+ // no point calculating this unless xy is between 0 & 1
+ ab = ( y1 * ( x0 - a1 ) + b1 * ( x1 - x0 ) + y0 * ( a1 - x1 ) ) / denom;
+
+ if (IsBetween(0, ab, 1))
+ {
+ ab = 1 - ab;
+ xy = 1 - xy;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ else
+ {
+ return false;
+ }
+ }
+ }
+
+
+ private static bool IsBetween(double x0, double x, double x1)
+ {
+ return ( x >= x0 ) && ( x <= x1 );
+ }
+
+ public bool WithinBoundaries(LineSegment otherLineSegment)
+ {
+ return this.BoundingBox.WithinBoundaries(otherLineSegment.BoundingBox);
+ }
+
+ ///
+ /// Verifies whether a point is aligned to this linesegment.
+ ///
+ /// This approach is done by means of the cross-product between the initial point of the LineSegment (p1) and the
+ /// provided unknown point (p3) and the final point of the LineSegment (p2) and the
+ ///
+ ///
+ ///
+ public bool IsAligned([NotNull] Point p3, double eps = double.MinValue * 100)
+ {
+ if (eps == double.NegativeInfinity)
+ {
+ eps = double.MinValue * 100;
+ }
+
+ var p1p3LineSeg = new LineSegment(this.P1, p3);
+
+ double k = Math.Abs(this.Angle - p1p3LineSeg.Angle);
+
+ if (k == 0)
+ {
+ return true;
+ }
+ else if (k <= eps)
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ public bool Intersects(Point other, double eps = double.MinValue * 100)
+ {
+ return false;
+ }
+
+ ///
+ /// Verifies whether a point intersects to this linesegment.
+ ///
+ /// A point (p) intersects a lineSegment if (and only if) two conditions are met:
+ /// 1) the direction of the vector composed of the points p and any of the edges of the linesegment is equal to the direction of the lineSegment itself
+ /// 2) the coordinates of the point must be within the boundaries of the linesegment.
+ ///
+ ///
+ ///
+ public bool Touches(Point point, double eps = double.MinValue * 100)
+ {
+ if (IsAligned(point, eps))
+ {
+ if (point.GetLatitude() < this.MinLatitude ||
+ point.GetLatitude() > this.MaxLatitude ||
+ point.GetLongitude() < this.MinLongitude ||
+ point.GetLongitude() > this.MaxLongitude)
+ {
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ public bool Within(Point other, double eps = double.MinValue * 100)
+ {
+ return false;
+ }
+
+
+
+ public bool Intersects(LineSegment otherGeometry, double eps = double.MinValue * 100)
+ {
+ var angleDif = Math.Abs(otherGeometry.Angle - this.Angle);
+
+ if (angleDif < eps)
+ {
+ return FindIntersection(this.P1.Coordinates.Longitude, this.P1.Coordinates.Latitude,
+ this.P2.Coordinates.Longitude, this.P2.Coordinates.Latitude,
+ otherGeometry.P1.Coordinates.Longitude, otherGeometry.P1.Coordinates.Latitude,
+ otherGeometry.P2.Coordinates.Longitude, otherGeometry.P2.Coordinates.Latitude
+ );
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ public bool Touches(LineSegment otherlineSegment, double eps = double.MinValue * 100)
+ {
+
+ // Checking first if any of the points that compose both lineSegments are equal, and also verifying if the lines do not intersect each other.
+ // If so, then, the lineSegments do touch each other;
+ if (otherlineSegment.Intersects(this))
+ {
+ return false;
+ }
+ else
+ {
+ if (otherlineSegment.Any(pOther => this.Equals(pOther)))
+ {
+ return true;
+ }
+ // In this second, case, we must verify if the lines touch, and, furthermore, if they do not intersects.
+ else
+ {
+ if (otherlineSegment.Any(pOther => this.Touches(pOther))
+ )
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ }
+ }
+
+ public bool Within(LineSegment other, double eps = double.MinValue * 100)
+ {
+ return false;
+ }
+
+
+
+ public bool Intersects(LinearRing other, double eps = double.MinValue * 100)
+ {
+ foreach(LineSegment lineSegmentFromOther in other)
+ {
+ if (this.Intersects(lineSegmentFromOther))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public bool Touches(LinearRing other, double eps = double.MinValue * 100)
+ {
+ foreach (LineSegment lineSegmentFromOther in other)
+ {
+ if (this.Touches(lineSegmentFromOther))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public bool Within(LinearRing other, double eps = double.MinValue * 100)
+ {
+ return false;
+ }
+
+
+
+ public bool Intersects(Polygon other, double eps = double.MinValue * 100)
+ {
+ return other.Intersects(this, eps);
+ }
+
+ public bool Touches(Polygon other, double eps = double.MinValue * 100)
+ {
+ foreach(LinearRing linearRingFromOther in other)
+ {
+ if (this.Touches(linearRingFromOther))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public bool Within(Polygon other, double eps = double.MinValue * 100)
+ {
+ return other.Contains(this);
+ }
+
+
+
+
+ #endregion Topological Operations
+
+
+ #endregion Public Methods
+
+ #region Converters
+
+ public static LineString CoordinatesToLineString(IEnumerable coordinates)
+ {
+ List lineSegments = PositionsToLineSegments(coordinates);
+
+ return new LineString(lineSegments);
+ }
+
+ public static List PositionsToLineSegments(IEnumerable _positions)
+ {
+ var positions = _positions.ToList();
+ var lineSegments = new List { };
+
+ var position_init = positions.Pop();
+
+ var NPositions = positions.Count;
+
+ for (var i = 0; i < NPositions; i++)
+ {
+
+ var position_final = positions.Pop();
+
+ var lineSegment = new LineSegment(new Point(position_init),
+ new Point(position_final)
+ );
+
+ lineSegments.Add(lineSegment);
+
+ position_init = position_final;
+
+ }
+
+ return lineSegments;
+ }
+
+ public static new LineSegment FromJson(string json)
+ {
+ return JsonConvert.DeserializeObject(json);
+ }
+ #endregion Converters
+
+
+ #endregion Methods
+
+ }
+
+
+
+ internal class LineSegmentEnumerator : IEnumerator, IDisposable
+ {
+
+ #region Proprieties
+ public int Position { get; private set; } = 0;
+
+ public List Coordinates { get; private set; }
+
+ #endregion Proprieties
+
+ public LineSegmentEnumerator(LineSegment lineSegment)
+ {
+ Coordinates = new List { lineSegment.P1, lineSegment.P2 };
+ }
+
+ public Point Current()
+ {
+ return this.Coordinates[Position];
+ }
+
+ object IEnumerator.Current
+ {
+ get
+ {
+ return Current();
+ }
+ }
+
+
+ public void Dispose()
+ {
+ // Suppress finalization.
+ GC.SuppressFinalize(this);
+
+ }
+
+ public bool MoveNext()
+ {
+ this.Position++;
+
+ if (this.Position < Coordinates.Count)
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ public void Reset()
+ {
+ this.Position = 0;
+ }
+ }
+
+}
diff --git a/GeoJSON/LineString.cs b/GeoJSON/LineString.cs
index fe08c4f..ee963c1 100644
--- a/GeoJSON/LineString.cs
+++ b/GeoJSON/LineString.cs
@@ -1,6 +1,8 @@
using BAMCIS.GeoJSON.Serde;
+using Extensions.ListExtensions;
using Newtonsoft.Json;
using System;
+using System.Collections;
using System.Collections.Generic;
using System.Linq;
@@ -10,16 +12,55 @@ namespace BAMCIS.GeoJSON
/// For type "LineString", the "coordinates" member is an array of two or
/// more positions.
///
- [JsonConverter(typeof(InheritanceBlockerConverter))]
- public class LineString : Geometry
+ [JsonConverter(typeof(LineStringConverter))]
+ public class LineString : Geometry,
+ IEnumerable,
+ ILine,
+ ILine,
+ ILine,
+ ILine,
+ IContains,
+ IContains,
+ IContains,
+ IContains,
+ IEquatable,
+ IComparable,
+ IComparer
+
{
#region Public Properties
///
/// The coordinates of a linestring are an array of positions
///
+ [JsonProperty(PropertyName = "LineSegments")]
+ public IEnumerable LineSegments { get; }
+
[JsonProperty(PropertyName = "coordinates")]
- public IEnumerable Coordinates { get; }
+ public IEnumerable Coordinates { get { return Points.Select(p => p.Coordinates).ToList(); } }
+
+ [JsonIgnore]
+ public IEnumerable Points { get; init; }
+
+ public double Length
+ {
+ get {
+ return LineSegments.Select(x => x.Length).Sum();
+ }
+ }
+
+ [JsonProperty(PropertyName = "BoundingBox")]
+ [JsonIgnore]
+ public override Rectangle BoundingBox {
+ get {
+
+ if (this._BoundingBox == null)
+ {
+ this._BoundingBox = FetchBoundingBox(this.Points);
+ }
+ return this._BoundingBox;
+ }
+ }
#endregion
@@ -29,25 +70,88 @@ public class LineString : Geometry
/// Creates a new LineString
///
/// The coordinates in the line string
- public LineString(IEnumerable coordinates, IEnumerable boundingBox = null) : base(GeoJsonType.LineString, coordinates.Any(x => x.HasElevation()), boundingBox)
+ [JsonConstructor]
+ public LineString(IEnumerable lineSegments) : base(GeoJsonType.LineString, lineSegments.Any(x => x.HasElevation()))
+ {
+ this.Points = lineSegments.SelectMany(lineSeg => lineSeg.Points.ToList()).ToList();
+
+ this.LineSegments = lineSegments ?? throw new ArgumentNullException(nameof(lineSegments));
+
+ }
+
+
+ public LineString(IEnumerable points) : base(GeoJsonType.LineString, points.Any(x => x.HasElevation()))
+ {
+ this.Points = points;
+
+ this.LineSegments = ConvertPointsToLineSegments(points.ToList()) ?? throw new ArgumentNullException("lineSegments");
+ }
+
+ public LineString(IEnumerable coordinates) : base(GeoJsonType.LineString, CoordinatesToPoints(coordinates).Any(x => x.HasElevation()))
+ {
+ this.Points = CoordinatesToPoints(coordinates);
+
+ this.LineSegments = ConvertPointsToLineSegments(this.Points.ToList()) ?? throw new ArgumentNullException("lineSegments");
+ }
+
+ public static List CoordinatesToPoints(IEnumerable positions)
+ {
+ List points = positions.Select(x => new Point(x)).ToList();
+
+ return points;
+
+ }
+
+ private static IEnumerable ConvertPointsToLineSegments(List points)
{
- this.Coordinates = coordinates ?? throw new ArgumentNullException("coordinates");
+ var lineSegments = new List();
+ if (points.Count < 2)
+ {
+ throw new ArgumentException("lineString must have at least two points to define itself");
+ }
+
+ var previousPoint = points.Pop();
+
+ int NSize = points.Count;
- if (this.Coordinates.Count() < 2)
+ for (int i=0; i < NSize; i++)
{
- throw new ArgumentOutOfRangeException("coordinates", "A LineString must have at least two positions.");
+ var nextPoint = points.Pop();
+
+ var lineSeg = new LineSegment(previousPoint, nextPoint);
+
+ lineSegments.Add(lineSeg);
+
+ previousPoint = nextPoint;
+
}
+
+ return lineSegments;
+
}
#endregion
#region Public Methods
+
+ #region Converters
+
+ public LineSegment[] ToVector()
+ {
+
+ return this.LineSegments.ToArray();
+ }
+
public static new LineString FromJson(string json)
{
return JsonConvert.DeserializeObject(json);
}
+ #endregion Converters
+
+ #region Equality Evaluators
+
public override bool Equals(object obj)
{
if (ReferenceEquals(this, obj))
@@ -73,15 +177,15 @@ public override bool Equals(object obj)
bBoxEqual = (this.BoundingBox == null && other.BoundingBox == null);
}
- bool coordinatesEqual = true;
+ bool coordinatesEqual;
- if (this.Coordinates != null && other.Coordinates != null)
+ if (this.LineSegments != null && other.LineSegments != null)
{
- coordinatesEqual = this.Coordinates.SequenceEqual(other.Coordinates);
+ coordinatesEqual = this.LineSegments.SequenceEqual(other.LineSegments);
}
else
{
- coordinatesEqual = (this.Coordinates == null && other.Coordinates == null);
+ coordinatesEqual = (this.LineSegments == null && other.LineSegments == null);
}
return this.Type == other.Type &&
@@ -91,7 +195,7 @@ public override bool Equals(object obj)
public override int GetHashCode()
{
- return Hashing.Hash(this.Type, this.Coordinates, this.BoundingBox);
+ return Hashing.Hash(this.Type, this.LineSegments, this.BoundingBox);
}
public static bool operator ==(LineString left, LineString right)
@@ -114,6 +218,395 @@ public override int GetHashCode()
return !(left == right);
}
- #endregion
+ public int CompareTo(LineSegment other)
+ {
+ return this.Length.CompareTo(other.Length);
+ }
+
+ public int Compare(LineSegment x, LineSegment y)
+ {
+ return x.Length.CompareTo(y.Length);
+ }
+
+
+ #endregion Equality Evaluators
+
+
+ #region Enumerable
+ public IEnumerator GetEnumerator()
+ {
+ foreach (LineSegment lineSegment in LineSegments)
+ {
+ yield return lineSegment;
+ }
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return new LineStringEnumerator(this);
+ }
+
+ #endregion Enumerable
+
+
+ #region Topological Operations
+
+ public static Rectangle FetchBoundingBox(IEnumerable points)
+ {
+ double MinLongitude = double.MaxValue;
+
+ double MaxLongitude = double.MinValue;
+
+
+ double MinLatitude = double.MaxValue;
+
+
+ double MaxLatitude = double.MinValue;
+
+
+ foreach (var point in points)
+ {
+ if (MinLongitude > point.GetLongitude())
+ {
+ MinLongitude = point.GetLongitude();
+ }
+
+ if (MaxLongitude < point.GetLongitude())
+ {
+ MaxLongitude = point.GetLongitude();
+ }
+
+ if (MinLatitude > point.GetLatitude())
+ {
+ MinLatitude = point.GetLatitude();
+ }
+
+ if (MaxLatitude < point.GetLatitude())
+ {
+ MaxLatitude = point.GetLatitude();
+ }
+ }
+
+ var LL = new Point(new Coordinate(MinLongitude, MinLatitude));
+
+ var LR = new Point(new Coordinate(MaxLongitude, MinLatitude));
+
+ var UL = new Point(new Coordinate(MinLongitude, MaxLatitude));
+
+ var UR = new Point(new Coordinate(MaxLongitude, MaxLatitude));
+
+ return new Rectangle(LL, LR, UL, UR);
+ }
+
+
+ public static bool Intersects(LineString left, LineString right, double eps = double.MinValue * 100)
+ {
+ foreach(var left_LineStringSegment in left.LineSegments)
+ {
+ foreach(var right_LineStringSegment in right.LineSegments)
+ {
+
+ if (left_LineStringSegment.Intersects(right_LineStringSegment, eps))
+ {
+ return true;
+ }
+ else
+ {
+ continue;
+ }
+ }
+
+ }
+
+ return false;
+
+ }
+
+
+ public bool Intersects(LineString other, double eps = double.MinValue * 100)
+ {
+ return Intersects(this, other, eps);
+ }
+
+ ///
+ /// Given a LineString and a point, this method evaluates if this point intersects any of the LineSegments that compose the provided LineString.
+ ///
+ /// If so, then this point is said to intersect this LineString.
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static bool Intersects(LineString left, Point point, double eps = double.MinValue * 100)
+ {
+
+ LineSegment[] lineSegments = left.LineSegments.ToArray();
+
+ foreach(var linesegment in left.LineSegments)
+ {
+ if (linesegment.Intersects(point, eps))
+ {
+ return true;
+ }
+ else
+ {
+ continue;
+ }
+ }
+
+ return false;
+ }
+
+ public bool Within(Polygon otherGeometry)
+ {
+ return otherGeometry.Contains(this);
+ }
+
+ public bool Intersects(LineString lineString)
+ {
+ foreach (LineSegment lineSegment in lineString)
+ {
+ if (this.Intersects(lineSegment))
+ {
+ return true;
+ }
+ else
+ {
+ continue;
+ }
+ }
+ return false;
+ }
+
+
+ public bool Intersects(LineSegment other)
+ {
+ foreach (LineSegment lineSegment in this)
+ {
+ if (lineSegment.Intersects(other))
+ {
+ return true;
+ }
+ else
+ {
+ continue;
+ }
+ }
+ return false;
+ }
+
+ public bool Touches(LineString other, double eps = double.MinValue * 100)
+ {
+
+ bool condition = false;
+ foreach(LineSegment lineSegmentFromOther in other)
+ {
+ foreach(var point in lineSegmentFromOther)
+ {
+ if (this.LineSegments.Any(line => line.Touches(point)))
+ {
+ condition = true;
+ }
+ }
+ }
+
+ if (Intersects(other, eps))
+ {
+ return false;
+ }
+ else
+ {
+ return condition;
+ }
+ }
+
+
+ public bool Equals(Point other)
+ {
+ return false;
+ }
+
+ public bool Intersects(Point other, double eps = double.MinValue * 100)
+ {
+ return false;
+ }
+
+ public bool Touches(Point other, double eps = double.MinValue * 100)
+ {
+ return other.Touches(this, eps);
+ }
+
+
+
+ public bool Within(Point other, double eps = double.MinValue * 100)
+ {
+ return false;
+ }
+
+ public bool Equals(LineSegment other)
+ {
+ if (this.LineSegments.Count() == 1)
+ {
+ return other.Equals(this);
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ public bool Intersects(LineSegment other, double eps = double.MinValue * 100)
+ {
+ if (this.LineSegments.Count() == 1)
+ {
+ return this.LineSegments.First().Intersects(other, eps);
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ public bool Touches(LineSegment other, double eps = double.MinValue * 100)
+ {
+ return this.LineSegments.Any(line => line.Touches(other, eps));
+ }
+
+ public bool Within(LineSegment other, double eps = double.MinValue * 100)
+ {
+ return false;
+ }
+
+ public bool Equals(LinearRing other)
+ {
+
+ var ls1 = this.LineSegments.ToList();
+ var ls2 = this.LineSegments.ToList();
+
+ for (int i = 0; i < this.LineSegments.Count();i ++)
+ {
+ if (!ls1[i].Equals(ls2[i]))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public bool Intersects(LinearRing other, double eps = double.MinValue * 100)
+ {
+ return this.Intersects(other as LineString, eps);
+ }
+
+ public bool Touches(LinearRing other, double eps = double.MinValue * 100)
+ {
+ return this.Touches(other as LineString, eps);
+ }
+
+ public bool Within(LinearRing other, double eps = double.MinValue * 100)
+ {
+ return false;
+ }
+
+ public bool Equals(Polygon other)
+ {
+ return false;
+ }
+
+ public bool Intersects(Polygon other, double eps = double.MinValue * 100)
+ {
+ return other.LinearRings.Any(l => l.Intersects(this));
+ }
+
+ public bool Touches(Polygon other, double eps = double.MinValue * 100)
+ {
+ return other.LinearRings.Any(l => l.Touches(this));
+ }
+
+ public bool Within(Polygon other, double eps = double.MinValue * 100)
+ {
+ return other.Contains(this);
+ }
+
+ public bool Contains(Point other, double eps = double.NegativeInfinity)
+ {
+ return false;
+ }
+
+ public bool Contains(LineSegment other, double eps = double.NegativeInfinity)
+ {
+ return false;
+ }
+
+ public bool Contains(LinearRing other, double eps = double.NegativeInfinity)
+ {
+ return false;
+ }
+
+ public bool Contains(Polygon other, double eps = double.NegativeInfinity)
+ {
+ return false;
+ }
+
+
+ #endregion Topological Operations
+
+ #endregion Public Methods
+
}
+
+ internal class LineStringEnumerator : IEnumerator where T: LineSegment
+ {
+
+ #region Proprieties
+ public int Position { get; private set; } = 0;
+
+ public List LineSegments { get; private set; }
+
+ T IEnumerator.Current => Current();
+
+ object IEnumerator.Current => Current();
+
+
+ #endregion Proprieties
+
+ public LineStringEnumerator(LineString lineString)
+ {
+ LineSegments = lineString.LineSegments.Select(x => (T) x).ToList();
+ }
+
+ public T Current()
+ {
+ return this.LineSegments[Position];
+ }
+
+ public void Dispose()
+ {
+ // Suppress finalization.
+ GC.SuppressFinalize(this);
+
+ }
+
+ public bool MoveNext()
+ {
+ this.Position++;
+
+ if (this.Position < LineSegments.Count)
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ public void Reset()
+ {
+ this.Position = 0;
+ }
+ }
+
+
}
diff --git a/GeoJSON/LinearRing.cs b/GeoJSON/LinearRing.cs
index b7a0939..12fbf77 100644
--- a/GeoJSON/LinearRing.cs
+++ b/GeoJSON/LinearRing.cs
@@ -1,4 +1,7 @@
-using Newtonsoft.Json;
+using BAMCIS.GeoJSON.Serde;
+using Extensions.ArrayExtensions;
+using Extensions.ListExtensions;
+using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -15,30 +18,275 @@ namespace BAMCIS.GeoJSON
/// area it bounds, i.e., exterior rings are counterclockwise, and
/// holes are clockwise.
///
+ [JsonConverter(typeof(LinearRingConverter))]
public class LinearRing : LineString
{
+
+
+
#region Constructors
+
///
/// Creates a new LinearRing
///
/// The coordinates that make up the linear ring
[JsonConstructor]
- public LinearRing(IEnumerable coordinates, IEnumerable boundingBox = null) : base(coordinates, boundingBox)
+ public LinearRing(IEnumerable lineSegments) : base(lineSegments)
+ {
+ LineSegment[] lines = lineSegments.ToArray();
+
+ if (lines.Length < 3)
+ {
+ throw new ArgumentOutOfRangeException("A linear ring requires at least 3 lineSegments (or 4 points).");
+ }
+
+ if (!lines.First().P1.Equals(lines.Last().P2))
+ {
+ throw new ArgumentException("The first and last Points of the LinearRing must be equivalent.");
+ }
+ }
+
+ ///
+ /// Creates a new LinearRing
+ ///
+ /// The coordinates that make up the linear ring
+ public LinearRing(IEnumerable coordinates) : this(LinearRing.PositionsToLineSegments(coordinates))
+ {
+ this.Points = coordinates.Select(x => x.ToPoint()).ToList() ;
+
+ if (Points.Count() < 4)
+ {
+ throw new ArgumentOutOfRangeException("A linear ring requires at least 4 coordinates.");
+ }
+
+ var c1 = this.Points.First();
+
+ var c2 = this.Points.Last();
+
+ if (!c1.Equals(c2))
+ {
+ throw new ArgumentException("The first and last Points of the LinearRing must be equivalent.");
+ }
+
+ }
+
+ #endregion Constructors
+
+ #region Topographic Operations
+
+ internal new bool Touches(LineString lineString, double eps)
+ {
+ foreach(LineSegment lineSeg in lineString.LineSegments)
+ {
+ if (this.Touches(lineSeg, eps))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public bool Contains(Point point)
+ {
+ if (!this.BoundingBox.Contains(point))
+ {
+ return false;
+ }
+
+ // To be considered within, no point can be an edge of the linearRing.
+ if (this.Points.Any(p => p.Equals(point)))
+ {
+ return false;
+ }
+
+ return LinearRing_Point_Within_LinearRing_TopologicalOperations.RayCastingAlgorithm(this, point);
+ }
+
+
+ public bool Contains(LineSegment lineSegment)
+ {
+ return lineSegment.All(p => this.Contains(p));
+ }
+
+ public bool Contains(LineString lineString)
+ {
+ return lineString.Points.All(p => this.Contains(p));
+ }
+
+ public static bool Within(LineString _)
+ {
+ return false;
+ }
+
+ public bool Within(LinearRing lineRing)
+ {
+ return this.Points.All(p => lineRing.Contains(p));
+ }
+
+ public new bool Within(Polygon polygon)
+ {
+ return polygon.Contains(this);
+ }
+
+ public bool Contains(Polygon polygon)
+ {
+ return this.Contains(polygon.LinearRings.First());
+ }
+
+ #endregion Topological Operations
+
+
+
+ #region Public Methods
+
+ public int CountPointsthatComposeThisLineRing()
{
- Position[] coords = this.Coordinates.ToArray();
+ int points = 0;
- if (coords.Length < 4)
+ foreach(LineSegment line in LineSegments) {
+
+ // Each linesegment is composed of solely 2 points:
+
+ points += 2;
+ }
+
+ return points;
+ }
+
+ public static LineString CoordinatesToLineString(IEnumerable coordinates)
+ {
+ List lineSegments = PositionsToLineSegments(coordinates);
+
+ return new LineString(lineSegments);
+ }
+
+ public static List PositionsToLineSegments(IEnumerable coordinates)
+ {
+ var positions = coordinates.ToList();
+ var lineSegments = new List { };
+
+ var position_init = positions.Pop();
+
+ Coordinate prior_position = position_init.Copy();
+
+ Coordinate after_position;
+
+ var NPositions = positions.Count;
+
+ for (var i = 0; i < NPositions; i++)
{
- throw new ArgumentOutOfRangeException("A linear ring requires 4 or more positions.");
+
+ after_position = positions.Pop();
+
+ var lineSegment = new LineSegment(new Point(prior_position),
+ new Point(after_position)
+ );
+
+ lineSegments.Add(lineSegment);
+
+ prior_position = after_position;
+
}
- if (!coords.First().Equals(coords.Last()))
+ var closingLineSegment = new LineSegment(new Point(prior_position),
+ new Point(position_init)
+ );
+
+ lineSegments.Add(closingLineSegment);
+
+ return lineSegments;
+ }
+
+ #endregion Public Methods
+
+
+ }
+
+ internal class LinearRing_Point_Within_LinearRing_TopologicalOperations
+ {
+
+ public static bool CrossVectorProductAlgorithm(LinearRing lineRing, Point point)
+ {
+ var points = lineRing.Points.ToList();
+
+ int signIsAlwaysEquals = 0;
+
+ for (int i = 1; i < points.Count; i++)
{
- throw new ArgumentException("The first and last value must be equivalent.", "coordinates");
+ var Vii = points[i];
+
+ var Vi = points[i - 1];
+
+ var Vii_Vi = Vii - Vi;
+
+ var PVI = ( point - Vi );
+
+
+ var CrossProd = Vii_Vi.ToArray().CrossProduct2D(PVI.ToArray());
+
+ if (CrossProd > 0)
+ {
+ if (signIsAlwaysEquals < 0)
+ {
+ return false;
+ }
+ else
+ {
+ signIsAlwaysEquals = -1;
+ }
+ }
+ else if (CrossProd == 0)
+ {
+ continue;
+ }
+ else
+ {
+ if (signIsAlwaysEquals > 0)
+ {
+ return false;
+ }
+ else
+ {
+ signIsAlwaysEquals = -1;
+ }
+ }
}
+
+ return true;
}
- #endregion
+ public static bool RayCastingAlgorithm(LinearRing lineRing, Point point)
+ {
+ int lineSegmentsThatAreIntersectedByPoint = 0;
+ var ypoint = point.GetLatitude();
+ var xpoint = point.GetLongitude();
+
+ foreach (LineSegment lineSeg in lineRing.LineSegments)
+ {
+ var p1 = lineSeg.P1;
+ var x1 = p1.GetLongitude();
+ var y1 = p1.GetLatitude();
+
+ var p2 = lineSeg.P2;
+ var x2 = p2.GetLongitude();
+ var y2 = p2.GetLatitude();
+
+ var xLimit = x1 + ( ( ypoint - y1 ) * ( x2 - x1 ) / ( y2 - y1 ) );
+
+ // for the casting ray (to the right from the provided point) to be capable of
+ // intersecting the line segment, the xpoint must be to the left of the linesegment.
+ if (xpoint < xLimit &&
+ ypoint > lineSeg.BoundingBox.MinLatitude &&
+ ypoint < lineSeg.BoundingBox.MaxLatitude
+ )
+ {
+ lineSegmentsThatAreIntersectedByPoint++;
+ }
+ }
+
+ return lineSegmentsThatAreIntersectedByPoint % 2 == 1;
+ }
}
}
diff --git a/GeoJSON/MultiLineString.cs b/GeoJSON/MultiLineString.cs
index c56d943..bc77e64 100644
--- a/GeoJSON/MultiLineString.cs
+++ b/GeoJSON/MultiLineString.cs
@@ -1,6 +1,7 @@
using BAMCIS.GeoJSON.Serde;
using Newtonsoft.Json;
using System;
+using System.Collections;
using System.Collections.Generic;
using System.Linq;
@@ -11,7 +12,7 @@ namespace BAMCIS.GeoJSON
/// LineString coordinate arrays.
///
[JsonConverter(typeof(MultiLineStringConverter))]
- public class MultiLineString : Geometry
+ public class MultiLineString : Geometry, IEnumerable
{
#region Public Properties
@@ -19,10 +20,25 @@ public class MultiLineString : Geometry
/// For type "MultiLineString", the "coordinates" member is an array of
/// LineString coordinate arrays.
///
- [JsonProperty(PropertyName = "coordinates")]
- public IEnumerable Coordinates { get; }
+ [JsonProperty(PropertyName = "Points")]
+ public IEnumerable LineStrings { get; }
- #endregion
+ [JsonProperty(PropertyName = "BoundingBox")]
+ [JsonIgnore]
+ public override Rectangle BoundingBox
+ {
+ get
+ {
+
+ if (this._BoundingBox == null)
+ {
+ this._BoundingBox = FetchBoundingBox();
+ }
+ return this._BoundingBox;
+ }
+ }
+
+ #endregion Public Properties
#region Constructors
@@ -31,20 +47,53 @@ public class MultiLineString : Geometry
///
/// The line strings that make up the object
[JsonConstructor]
- public MultiLineString(IEnumerable coordinates, IEnumerable boundingBox = null) : base(GeoJsonType.MultiLineString, coordinates.Any(x => x.IsThreeDimensional()), boundingBox)
+ public MultiLineString(IEnumerable coordinates) : base(GeoJsonType.MultiLineString, coordinates.Any(x => x.IsThreeDimensional()))
{
- this.Coordinates = coordinates ?? throw new ArgumentNullException("coordinates");
+ this.LineStrings = coordinates ?? throw new ArgumentNullException("Points");
}
#endregion
#region Public Methods
+ #region Converters
+
public static new MultiLineString FromJson(string json)
{
return JsonConvert.DeserializeObject(json);
}
+ #endregion Converters
+
+ #region Enumerable
+
+
+ public IEnumerable ToList()
+ {
+ foreach (var geometry in this.LineStrings)
+ {
+
+ yield return geometry;
+ }
+ }
+
+ public IEnumerator GetEnumerator()
+ {
+ foreach (var line in LineStrings)
+ {
+ yield return line;
+ }
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+
+ #endregion Enumerable
+
+ #region Equality Evaluators
public override bool Equals(object obj)
{
if (ReferenceEquals(this, obj))
@@ -72,13 +121,13 @@ public override bool Equals(object obj)
bool coordinatesEqual = true;
- if (this.Coordinates != null && other.Coordinates != null)
+ if (this.LineStrings != null && other.LineStrings != null)
{
- coordinatesEqual = this.Coordinates.SequenceEqual(other.Coordinates);
+ coordinatesEqual = this.LineStrings.SequenceEqual(other.LineStrings);
}
else
{
- coordinatesEqual = (this.Coordinates == null && other.Coordinates == null);
+ coordinatesEqual = (this.LineStrings == null && other.LineStrings == null);
}
return this.Type == other.Type &&
@@ -88,9 +137,10 @@ public override bool Equals(object obj)
public override int GetHashCode()
{
- return Hashing.Hash(this.Type, this.Coordinates, this.BoundingBox);
+ return Hashing.Hash(this.Type, this.LineStrings, this.BoundingBox);
}
+
public static bool operator ==(MultiLineString left, MultiLineString right)
{
if (ReferenceEquals(left, right))
@@ -111,6 +161,53 @@ public override int GetHashCode()
return !(left == right);
}
+ #endregion Equality Evaluators
+
+ #region Topological Operations
+ public Rectangle FetchBoundingBox()
+ {
+
+ double MaxLatitude = double.MinValue;
+ double MaxLongitude = double.MinValue;
+ double MinLatitude = double.MaxValue;
+ double MinLongitude = double.MaxValue;
+
+ foreach (var geometry in this.LineStrings)
+ {
+ if (MaxLatitude < geometry.BoundingBox.MaxLatitude)
+ {
+ MaxLatitude = geometry.BoundingBox.MaxLatitude;
+ }
+
+ if (MaxLongitude < geometry.BoundingBox.MaxLongitude)
+ {
+ MaxLongitude = geometry.BoundingBox.MaxLongitude;
+ }
+
+ if (MinLatitude > geometry.BoundingBox.MinLatitude)
+ {
+ MinLatitude = geometry.BoundingBox.MinLatitude;
+ }
+
+ if (MinLongitude > geometry.BoundingBox.MinLongitude)
+ {
+ MinLongitude = geometry.BoundingBox.MinLongitude;
+ }
+ }
+
+ Point LL = new Point(new Coordinate(MinLongitude, MinLatitude));
+ Point LR = new Point(new Coordinate(MaxLongitude, MinLatitude));
+ Point UL = new Point(new Coordinate(MinLongitude, MaxLatitude));
+ Point UR = new Point(new Coordinate(MaxLongitude, MaxLatitude));
+
+ return new Rectangle(LL, LR, UL, UR);
+
+ }
+
+
+
+ #endregion Topological Operations
+
#endregion
}
}
diff --git a/GeoJSON/MultiPoint.cs b/GeoJSON/MultiPoint.cs
index 0f6d809..3d0ba96 100644
--- a/GeoJSON/MultiPoint.cs
+++ b/GeoJSON/MultiPoint.cs
@@ -2,6 +2,8 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics.CodeAnalysis;
using System.Linq;
namespace BAMCIS.GeoJSON
@@ -9,7 +11,7 @@ namespace BAMCIS.GeoJSON
///
/// For type "MultiPoint", the "coordinates" member is an array of positions.
///
- [JsonConverter(typeof(InheritanceBlockerConverter))]
+ [JsonConverter(typeof(MultiPointConverter))]
public class MultiPoint : Geometry
{
#region Public Properties
@@ -17,10 +19,27 @@ public class MultiPoint : Geometry
///
/// The coordinates of a multipoint are an array of positions
///
- [JsonProperty(PropertyName = "coordinates")]
- public IEnumerable Coordinates { get; }
+ [JsonProperty(PropertyName = "Points")]
+ [Description("Points")]
+ public IEnumerable Points { get; }
- #endregion
+
+ [JsonProperty(PropertyName = "BoundingBox")]
+ [JsonIgnore]
+ public override Rectangle BoundingBox
+ {
+ get
+ {
+
+ if (this._BoundingBox == null)
+ {
+ this._BoundingBox = FetchBoundingBox();
+ }
+ return this._BoundingBox;
+ }
+ }
+
+ #endregion Public Properties
#region Constructors
@@ -28,15 +47,48 @@ public class MultiPoint : Geometry
/// Creates a new multipoint object
///
///
- public MultiPoint(IEnumerable coordinates, IEnumerable boundingBox = null) : base(GeoJsonType.MultiPoint, coordinates.Any(x => x.HasElevation()), boundingBox)
+ [JsonConstructor]
+ public MultiPoint([NotNull] IEnumerable coordinates) : base(GeoJsonType.MultiPoint, coordinates.Any(x => x.HasElevation()))
+ {
+ if (coordinates == null)
+ throw new ArgumentNullException(nameof(Coordinate));
+
+ this.Points = coordinates.Select(c => new Point(c));
+ }
+
+ ///
+ /// Creates a new multipoint object
+ ///
+ ///
+
+ public MultiPoint([NotNull] IEnumerable> coordinates) : base(GeoJsonType.MultiPoint, coordinates.Any(x => x.Any(y => y.HasElevation())))
+ {
+ var points = new List();
+
+ foreach(IEnumerable pointCoordinates in coordinates)
+ {
+ points.AddRange(pointCoordinates.Select(p => p.ToPoint()).ToList());
+ }
+
+ this.Points = points;
+ }
+
+
+
+ public MultiPoint([NotNull] IEnumerable points) : base(GeoJsonType.MultiPoint, points.Any(x => x.HasElevation()))
{
- this.Coordinates = coordinates ?? throw new ArgumentNullException("coordinates");
+ if (points == null)
+ throw new ArgumentNullException(nameof(Coordinate));
+
+ this.Points = points;
}
- #endregion
+ #endregion Constructors
#region Public Methods
+ #region Comparers
+
public static new MultiPoint FromJson(string json)
{
return JsonConvert.DeserializeObject(json);
@@ -69,13 +121,13 @@ public override bool Equals(object obj)
bool coordinatesEqual = true;
- if (this.Coordinates != null && other.Coordinates != null)
+ if (this.Points != null && other.Points != null)
{
- coordinatesEqual = this.Coordinates.SequenceEqual(other.Coordinates);
+ coordinatesEqual = this.Points.SequenceEqual(other.Points);
}
else
{
- coordinatesEqual = (this.Coordinates == null && other.Coordinates == null);
+ coordinatesEqual = (this.Points == null && other.Points == null);
}
return this.Type == other.Type &&
@@ -85,7 +137,7 @@ public override bool Equals(object obj)
public override int GetHashCode()
{
- return Hashing.Hash(this.Type, this.Coordinates, this.BoundingBox);
+ return Hashing.Hash(this.Type, this.Points, this.BoundingBox);
}
public static bool operator ==(MultiPoint left, MultiPoint right)
@@ -108,6 +160,54 @@ public override int GetHashCode()
return !(left == right);
}
- #endregion
+ #endregion Comparers
+
+
+ #region Topological Operations
+ public Rectangle FetchBoundingBox()
+ {
+
+ double MaxLatitude = double.MinValue;
+ double MaxLongitude = double.MinValue;
+ double MinLatitude = double.MaxValue;
+ double MinLongitude = double.MaxValue;
+
+ foreach (Point geometry in this.Points)
+ {
+ if (MaxLatitude < geometry.GetLatitude())
+ {
+ MaxLatitude = geometry.GetLatitude();
+ }
+
+ if (MaxLongitude < geometry.GetLongitude())
+ {
+ MaxLongitude = geometry.GetLongitude();
+ }
+
+ if (MinLatitude > geometry.GetLatitude())
+ {
+ MinLatitude = geometry.GetLatitude();
+ }
+
+ if (MinLongitude > geometry.GetLongitude())
+ {
+ MinLongitude = geometry.GetLongitude();
+ }
+ }
+
+ var LL = new Point(new Coordinate(MinLongitude, MinLatitude));
+ var LR = new Point(new Coordinate(MaxLongitude, MinLatitude));
+ var UL = new Point(new Coordinate(MinLongitude, MaxLatitude));
+ var UR = new Point(new Coordinate(MaxLongitude, MaxLatitude));
+
+ return new Rectangle(LL, LR, UL, UR);
+
+ }
+
+
+ #endregion Topological Operations
+
+
+ #endregion Public Methods
}
}
diff --git a/GeoJSON/MultiPolygon.cs b/GeoJSON/MultiPolygon.cs
index f1114b8..6320828 100644
--- a/GeoJSON/MultiPolygon.cs
+++ b/GeoJSON/MultiPolygon.cs
@@ -18,8 +18,23 @@ public class MultiPolygon : Geometry
///
/// The coordinates are an array of polygons.
///
- [JsonProperty(PropertyName = "coordinates")]
- public IEnumerable Coordinates { get; }
+ [JsonProperty(PropertyName = "Polygons")]
+ public IEnumerable Polygons { get; }
+
+ [JsonProperty(PropertyName = "BoundingBox")]
+ [JsonIgnore]
+ public override Rectangle BoundingBox
+ {
+ get
+ {
+
+ if (this._BoundingBox == null)
+ {
+ this._BoundingBox = FetchBoundingBox();
+ }
+ return this._BoundingBox;
+ }
+ }
#endregion
@@ -28,21 +43,68 @@ public class MultiPolygon : Geometry
///
/// Creates a new MultiPolygon
///
- /// The coordinates that make up the multi polygon
+ /// The coordinates that make up the multi polygon
[JsonConstructor]
- public MultiPolygon(IEnumerable coordinates, IEnumerable boundingBox = null) : base(GeoJsonType.MultiPolygon, coordinates.Any(x => x.IsThreeDimensional()), boundingBox)
+ public MultiPolygon(IEnumerable Polygons) : base(GeoJsonType.MultiPolygon, Polygons.Any(x => x.IsThreeDimensional()))
{
- this.Coordinates = coordinates ?? throw new ArgumentNullException("coordinates");
+ this.Polygons = Polygons ?? throw new ArgumentNullException(nameof(Polygons));
- if (!this.Coordinates.Any())
+ if (!this.Polygons.Any())
{
- throw new ArgumentOutOfRangeException("coordinates", "A MultiPolygon must have at least 1 polygon.");
+ throw new ArgumentOutOfRangeException(nameof(Polygons), "A MultiPolygon must have at least 1 polygon.");
}
+
}
#endregion
#region Public Methods
+
+ public Rectangle FetchBoundingBox()
+ {
+
+ double MaxLatitude = double.MinValue ;
+ double MaxLongitude = double.MinValue;
+ double MinLatitude = double.MaxValue;
+ double MinLongitude = double.MaxValue;
+
+ foreach (var polygon in this.Polygons)
+ {
+ if (MaxLatitude < polygon.BoundingBox.MaxLatitude)
+ {
+ MaxLatitude = polygon.BoundingBox.MaxLatitude;
+ }
+
+ if (MaxLongitude < polygon.BoundingBox.MaxLongitude)
+ {
+ MaxLongitude = polygon.BoundingBox.MaxLongitude;
+ }
+
+ if (MinLatitude > polygon.BoundingBox.MinLatitude)
+ {
+ MinLatitude = polygon.BoundingBox.MinLatitude;
+ }
+
+ if (MinLongitude > polygon.BoundingBox.MinLongitude)
+ {
+ MinLongitude = polygon.BoundingBox.MinLongitude;
+ }
+ }
+
+ Point LL = new Point(new Coordinate(MinLongitude, MinLatitude));
+ Point LR = new Point(new Coordinate(MaxLongitude, MinLatitude));
+ Point UL = new Point(new Coordinate(MinLongitude, MaxLatitude));
+ Point UR = new Point(new Coordinate(MaxLongitude, MaxLatitude));
+
+ return new Rectangle(LL, LR, UL, UR);
+
+ }
+
+ ///
+ /// Deserializes the json into a MultiPolygon
+ ///
+ /// The json to deserialize
+ /// A Point object
public static new MultiPolygon FromJson(string json)
{
return JsonConvert.DeserializeObject(json);
@@ -76,13 +138,13 @@ public override bool Equals(object obj)
bool coordinatesEqual = true;
- if (this.Coordinates != null && other.Coordinates != null)
+ if (this.Polygons != null && other.Polygons != null)
{
- coordinatesEqual = this.Coordinates.SequenceEqual(other.Coordinates);
+ coordinatesEqual = this.Polygons.SequenceEqual(other.Polygons);
}
else
{
- coordinatesEqual = (this.Coordinates == null && other.Coordinates == null);
+ coordinatesEqual = (this.Polygons == null && other.Polygons == null);
}
return this.Type == other.Type &&
@@ -92,9 +154,10 @@ public override bool Equals(object obj)
public override int GetHashCode()
{
- return Hashing.Hash(this.Type, this.Coordinates, this.BoundingBox);
+ return Hashing.Hash(this.Type, this.Polygons, this.BoundingBox);
}
+
public static bool operator ==(MultiPolygon left, MultiPolygon right)
{
if (ReferenceEquals(left, right))
diff --git a/GeoJSON/Point.cs b/GeoJSON/Point.cs
index 4ce724e..3e311cc 100644
--- a/GeoJSON/Point.cs
+++ b/GeoJSON/Point.cs
@@ -1,25 +1,28 @@
using BAMCIS.GeoJSON.Serde;
using Newtonsoft.Json;
using System;
-using System.Collections.Generic;
-using System.Linq;
namespace BAMCIS.GeoJSON
{
///
/// For type "Point", the "coordinates" member is a single position.
///
- [JsonConverter(typeof(InheritanceBlockerConverter))]
- public class Point : Geometry
+ [JsonConverter(typeof(PointConverter))]
+ public class Point : Geometry,
+ IAdimTopology,
+ IAdimTopology
{
#region Public Properties
///
/// For type "Point", the "coordinates" member is a single position.
///
- [JsonProperty(PropertyName = "coordinates")]
- public Position Coordinates { get; }
+ [JsonProperty(PropertyName = "Coordinates")]
+ public Coordinate Coordinates { get; }
+ [JsonProperty(PropertyName = "BoundingBox")]
+ [JsonIgnore]
+ public override Rectangle BoundingBox { get { return null; } }
#endregion
#region Constructors
@@ -29,15 +32,30 @@ public class Point : Geometry
///
/// The position of this point
[JsonConstructor]
- public Point(Position coordinates, IEnumerable boundingBox = null) : base(GeoJsonType.Point, coordinates.HasElevation(), boundingBox)
+ public Point(Coordinate coordinates) : base(GeoJsonType.Point, coordinates.HasElevation())
{
- this.Coordinates = coordinates ?? throw new ArgumentNullException("coordinates");
+ this.Coordinates = coordinates ?? throw new ArgumentNullException(nameof(coordinates));
+
+ }
+
+ public Point(double longitude, double latitude) : base(GeoJsonType.Point, false)
+ {
+ this.Coordinates = new Coordinate(longitude, latitude) ?? throw new ArgumentNullException("Longitude and Latitude are invalid");
+
+ }
+
+ public Point(double longitude, double latitude, double altitude) : base(GeoJsonType.Point, true)
+ {
+ this.Coordinates = new Coordinate(longitude, latitude, altitude) ?? throw new ArgumentNullException("Longitude and Latitude and Altitude are invalid");
+
}
#endregion
#region Public Methods
+ #region Converters
+
///
/// Deserializes the json into a Point
///
@@ -48,6 +66,22 @@ public Point(Position coordinates, IEnumerable boundingBox = null) : bas
return JsonConvert.DeserializeObject(json);
}
+ ///
+ /// Converts this Point into a 1D Array of length 2 (longitude and latitude)
+ ///
+ ///
+ public double[] ToArray()
+ {
+ var vector = new double[] { this.Coordinates.Longitude, this.Coordinates.Latitude };
+
+ return vector;
+
+ }
+
+ #endregion Converters
+
+ #region Coordinates
+
///
/// Gets the longitude or easting of the point
///
@@ -66,11 +100,6 @@ public double GetLatitude()
return this.Coordinates.Latitude;
}
- ///
- /// Gets the elevation of the point if it exists
- /// in the coordinates.
- ///
- /// The elevation or if it wasn't set, the returns NaN
public bool TryGetElevation(out double elevation)
{
if (this.Coordinates.HasElevation())
@@ -83,41 +112,45 @@ public bool TryGetElevation(out double elevation)
return false;
}
- public override bool Equals(object obj)
- {
- if (ReferenceEquals(this, obj))
- {
- return true;
- }
- if (obj == null || this.GetType() != obj.GetType())
- {
- return false;
- }
+ #endregion Coordinates
- Point other = (Point)obj;
+ ///
+ /// Gets the elevation of the point if it exists
+ /// in the coordinates.
+ ///
+ /// The elevation or if it wasn't set, the returns NaN
- bool bBoxEqual = true;
+ #region Equality Evaluators
- if (this.BoundingBox != null && other.BoundingBox != null)
- {
- bBoxEqual = this.BoundingBox.SequenceEqual(other.BoundingBox);
- }
- else
+ public bool Equals(Point obj)
+ {
+ return obj.Coordinates.Equals(this.Coordinates);
+ }
+
+ public bool Equals(Polygon other)
+ {
+ return false;
+ }
+
+ public override bool Equals(object obj)
+ {
+ var point = obj as Point;
+
+ if (point == null )
{
- bBoxEqual = (this.BoundingBox == null && other.BoundingBox == null);
+ return false;
}
- return this.Type == other.Type &&
- this.Coordinates == other.Coordinates &&
- bBoxEqual;
+ return Equals(point);
}
public override int GetHashCode()
{
- return Hashing.Hash(this.Type, this.Coordinates, this.BoundingBox);
+ return Hashing.Hash(this.Type, this.Coordinates);
}
+
public static bool operator ==(Point left, Point right)
{
if (ReferenceEquals(left, right))
@@ -133,11 +166,137 @@ public override int GetHashCode()
return left.Equals(right);
}
+ public static Point operator -(Point left, Point right)
+ {
+
+ Coordinate newPosition = left.Coordinates - right.Coordinates;
+
+ return new Point(newPosition);
+ }
+
+ public static Point operator +(Point left, Point right)
+ {
+ Coordinate newPosition = left.Coordinates + right.Coordinates;
+
+ return new Point(newPosition);
+ }
+
public static bool operator !=(Point left, Point right)
{
return !(left == right);
}
- #endregion
+
+ #endregion Equality Evaluators
+
+ #region Topographic Operations
+
+ #region Touching Rules
+
+ public bool Touches(Point otherPoint, double eps = double.MinValue * 100)
+ {
+ return this.Equals(otherPoint);
+ }
+ public bool Touches(LineSegment lineSegment)
+ {
+ return lineSegment.Touches(this);
+ }
+
+
+ public bool Touches(LineString lineString, double eps = double.MinValue * 100)
+ {
+ foreach (LineSegment lineSegment in lineString)
+ {
+ if (lineSegment.Touches(this, eps))
+ {
+ return true;
+ }
+ else
+ {
+ continue;
+ }
+ }
+ return false;
+ }
+
+ public bool Touches(Polygon polygon, double eps = double.MinValue * 100)
+ {
+ foreach (var lineRing in polygon)
+ {
+ if (this.Touches(lineRing, eps))
+ {
+ return true;
+ }
+ else
+ {
+ continue;
+ }
+ }
+
+ return false;
+ }
+ #endregion Touching Rules
+
+ #region Within Rules
+
+
+
+ public bool Within(Point point, double eps = double.MinValue * 100)
+ {
+ return this.Equals(point);
+ }
+
+
+ public bool Within(Polygon polygon, double eps = double.MinValue * 100)
+ {
+ return polygon.Contains(this, eps);
+ }
+
+ ///
+ /// A point always has an empty Rectangle as its bounding box.
+ ///
+ ///
+ public Rectangle FetchBoundingBox()
+ {
+ return null;
+ }
+
+ public Point Copy()
+ {
+ return new Point(this.Coordinates.Copy());
+ }
+
+ public bool HasElevation()
+ {
+ return this.Coordinates.HasElevation();
+ }
+
+
+
+ #endregion Within Rules
+
+ #region Intersection Rules
+
+ public static bool Intersects(Geometry _)
+ {
+ return false;
+ }
+
+ #endregion Intersection Rules
+
+
+ #region Contain Rules
+
+ public static bool Contains(Geometry _)
+ {
+ return false;
+ }
+
+ #endregion Contain Rules
+
+
+ #endregion Topographic Operations
+
+ #endregion Public Methods
}
-}
+}
\ No newline at end of file
diff --git a/GeoJSON/Polygon.cs b/GeoJSON/Polygon.cs
index 341ed0d..1c0eae5 100644
--- a/GeoJSON/Polygon.cs
+++ b/GeoJSON/Polygon.cs
@@ -1,22 +1,33 @@
using BAMCIS.GeoJSON.Serde;
+using Extensions.ArrayExtensions;
using Newtonsoft.Json;
using System;
+using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace BAMCIS.GeoJSON
{
+
+
///
- /// A polygon is formed with 1 or more linear rings, which are an enclosed LineString
+ /// A polygon is formed with 1 or more linear rings, which are an enclosed LineString.
+ ///
+ ///
+ /// For spatial operations, check:
+ /// https://www.topcoder.com/thrive/articles/Geometry%20Concepts%20part%203:%20Using%20Geometry%20in%20Topcoder%20Problems
///
[JsonConverter(typeof(PolygonConverter))]
- public class Polygon : Geometry
- {
- #region Private Fields
+ public class Polygon : Geometry,
+ IEquatable,
+ IContains,
+ ITouch,
+ IPolygon,
+ IPolygon,
+ IEnumerable
- private IEnumerable _coordinates;
-
- #endregion
+ {
+
#region Public Properties
@@ -27,11 +38,36 @@ public class Polygon : Geometry
/// exterior ring bounds the surface, and the interior rings(if
/// present) bound holes within the surface.
///
- [JsonProperty(PropertyName = "coordinates")]
- public IEnumerable Coordinates {
+ [JsonProperty(PropertyName = "LinearRings")]
+ [JsonIgnore]
+ public IEnumerable LinearRings { get; set; }
+
+
+ ///
+ /// The points that represent the linearRings of the polygon
+ ///
+ [JsonProperty(PropertyName = "Points")]
+ [JsonIgnore]
+ public IEnumerable Points
+ {
+ get
+ {
+ return this.LinearRings.SelectMany(lineRing => lineRing.Points).ToList();
+ }
+ }
+
+ [JsonProperty(PropertyName = "BoundingBox")]
+ [JsonIgnore]
+ public override Rectangle BoundingBox
+ {
get
{
- return this._coordinates;
+
+ if (this._BoundingBox == null)
+ {
+ this._BoundingBox = FetchBoundingBox();
+ }
+ return this._BoundingBox;
}
}
@@ -44,39 +80,74 @@ public IEnumerable Coordinates {
///
/// The linear rings that make up the polygon
[JsonConstructor]
- public Polygon(IEnumerable coordinates, IEnumerable boundingBox = null) : base(GeoJsonType.Polygon, coordinates.Any(x => x.IsThreeDimensional()), boundingBox)
+ public Polygon(IEnumerable linearRings) : base(GeoJsonType.Polygon, linearRings.Any(x => x.IsThreeDimensional()))
{
- this._coordinates = coordinates ?? throw new ArgumentNullException("coordinates");
+ this.LinearRings = linearRings ?? throw new ArgumentNullException("Points");
- if (!this.Coordinates.Any())
+ if (!this.LinearRings.Any())
{
- throw new ArgumentOutOfRangeException("coordinates", "A polygon must have at least 1 linear ring.");
+ throw new ArgumentOutOfRangeException("Points", "A polygon must have at least 1 linear ring.");
}
}
- #endregion
-
- #region Public Methods
+
///
- /// Removes the interior linear rings that bound holes within the surface from the polygon's coordinates
- /// leaving just 1 linear ring in the coordinates.
+ /// Creates a new Polygon
///
- /// Returns true if the polygon had more than linear ring and false if there were no linear rings to remove
- public bool RemoveInteriorRings()
+ /// The linear rings that make up the polygon
+
+ public Polygon(LinearRing linearRing) : base(GeoJsonType.Polygon, linearRing.Any(x => x.HasElevation()))
{
- // If there is more than element
- if (this._coordinates != null && this._coordinates.Skip(1).Any())
+ this.LinearRings = new List { linearRing } ?? throw new ArgumentNullException("Points");
+
+ if (!this.LinearRings.Any())
{
- this._coordinates = this._coordinates.Take(1);
- return true;
+ throw new ArgumentOutOfRangeException("Points", "A polygon must have at least 1 linear ring.");
}
- else
+ }
+
+
+ public Polygon(IEnumerable>> coordinates) : this(CoordinatesToLinearRings(coordinates))
+ {
+
+ }
+
+ public static IEnumerable CoordinatesToLinearRings(IEnumerable>> coordinates)
+ {
+ var rings = new List();
+
+ foreach (var lineRing in coordinates)
{
- return false;
+ var positionsOfASingleLineRing = new List();
+
+ foreach (var lineSegments in lineRing)
+ {
+ List