diff --git a/NetTopologySuite.IO/NetTopologySuite.IO.GeoJSON/Converters/AttributesTableConverter.cs b/NetTopologySuite.IO/NetTopologySuite.IO.GeoJSON/Converters/AttributesTableConverter.cs index 33e8d5290..792e08b59 100644 --- a/NetTopologySuite.IO/NetTopologySuite.IO.GeoJSON/Converters/AttributesTableConverter.cs +++ b/NetTopologySuite.IO/NetTopologySuite.IO.GeoJSON/Converters/AttributesTableConverter.cs @@ -24,7 +24,10 @@ public override void WriteJson(JsonWriter writer, object value, JsonSerializer s IAttributesTable attributes = value as IAttributesTable; if (attributes == null) + { + writer.WriteNull(); return; + } writer.WriteStartObject(); string[] names = attributes.GetNames(); @@ -120,41 +123,47 @@ private static object InternalReadJson(JsonReader reader, JsonSerializer seriali if (reader.TokenType != JsonToken.StartObject) throw new ArgumentException("Expected token '{' not found."); + // Advance reader reader.Read(); - AttributesTable attributesTable = new AttributesTable(); - while (reader.TokenType == JsonToken.PropertyName) + + AttributesTable attributesTable = null; + if (reader.TokenType != JsonToken.Null) { - string attributeName = (string)reader.Value; - reader.Read(); - object attributeValue; - if (reader.TokenType == JsonToken.StartObject) - { - // inner object - attributeValue = InternalReadJson(reader, serializer); - if (reader.TokenType != JsonToken.EndObject) - throw new ArgumentException("Expected token '}' not found."); - // read EndObject token - reader.Read(); - } - else if (reader.TokenType == JsonToken.StartArray) - { - attributeValue = InternalReadJsonArray(reader, serializer); - //reader.Read(); // move to first item - //IList array = new List(); - //do - //{ - // object inner = InternalReadJson(reader, serializer); - // array.Add(inner); - // reader.Read(); // move to next item - //} while (reader.TokenType != JsonToken.EndArray); - //attributeValue = array; - } - else + attributesTable = new AttributesTable(); + while (reader.TokenType == JsonToken.PropertyName) { - attributeValue = reader.Value; + string attributeName = (string) reader.Value; reader.Read(); + object attributeValue; + if (reader.TokenType == JsonToken.StartObject) + { + // inner object + attributeValue = InternalReadJson(reader, serializer); + if (reader.TokenType != JsonToken.EndObject) + throw new ArgumentException("Expected token '}' not found."); + // read EndObject token + reader.Read(); + } + else if (reader.TokenType == JsonToken.StartArray) + { + attributeValue = InternalReadJsonArray(reader, serializer); + //reader.Read(); // move to first item + //IList array = new List(); + //do + //{ + // object inner = InternalReadJson(reader, serializer); + // array.Add(inner); + // reader.Read(); // move to next item + //} while (reader.TokenType != JsonToken.EndArray); + //attributeValue = array; + } + else + { + attributeValue = reader.Value; + reader.Read(); + } + attributesTable.AddAttribute(attributeName, attributeValue); } - attributesTable.AddAttribute(attributeName, attributeValue); } // TODO: refactor to remove check when reading TopoJSON if (reader.TokenType != JsonToken.EndObject) diff --git a/NetTopologySuite.IO/NetTopologySuite.IO.GeoJSON/Converters/CoordinateConverters.cs b/NetTopologySuite.IO/NetTopologySuite.IO.GeoJSON/Converters/CoordinateConverters.cs index 0149320d6..4d6d39a66 100644 --- a/NetTopologySuite.IO/NetTopologySuite.IO.GeoJSON/Converters/CoordinateConverters.cs +++ b/NetTopologySuite.IO/NetTopologySuite.IO.GeoJSON/Converters/CoordinateConverters.cs @@ -12,7 +12,11 @@ public class CoordinateConverter : JsonConverter { public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { - writer.WritePropertyName("coordinates"); + if (value == null) + { + writer.WriteToken(JsonToken.Null); + return; + } List> coordinatesss = value as List>; if (coordinatesss != null) diff --git a/NetTopologySuite.IO/NetTopologySuite.IO.GeoJSON/Converters/EnvelopeConverter.cs b/NetTopologySuite.IO/NetTopologySuite.IO.GeoJSON/Converters/EnvelopeConverter.cs index ba400e5a8..dd188c1f8 100644 --- a/NetTopologySuite.IO/NetTopologySuite.IO.GeoJSON/Converters/EnvelopeConverter.cs +++ b/NetTopologySuite.IO/NetTopologySuite.IO.GeoJSON/Converters/EnvelopeConverter.cs @@ -15,14 +15,17 @@ public class EnvelopeConverter : JsonConverter public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { Envelope envelope = value as Envelope; - if (envelope == null) return; + if (envelope == null) + { + writer.WriteToken(null); + return; + } - writer.WritePropertyName("bbox"); writer.WriteStartArray(); - writer.WriteValue(envelope.MinX.ToString(NumberFormatInfo.InvariantInfo)); - writer.WriteValue(envelope.MinY.ToString(NumberFormatInfo.InvariantInfo)); - writer.WriteValue(envelope.MaxX.ToString(NumberFormatInfo.InvariantInfo)); - writer.WriteValue(envelope.MaxY.ToString(NumberFormatInfo.InvariantInfo)); + writer.WriteValue(envelope.MinX); + writer.WriteValue(envelope.MinY); + writer.WriteValue(envelope.MaxX); + writer.WriteValue(envelope.MaxY); writer.WriteEndArray(); } @@ -32,19 +35,25 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist Debug.Assert((string)reader.Value == "bbox"); reader.Read(); // move to array start - JArray envelope = serializer.Deserialize(reader); - Debug.Assert(envelope.Count == 4); + if (reader.TokenType != JsonToken.Null) + { + JArray envelope = serializer.Deserialize(reader); + Debug.Assert(envelope.Count == 4); - double minX = Double.Parse((string) envelope[0], NumberFormatInfo.InvariantInfo); - double minY = Double.Parse((string) envelope[1], NumberFormatInfo.InvariantInfo); - double maxX = Double.Parse((string) envelope[2], NumberFormatInfo.InvariantInfo); - double maxY = Double.Parse((string) envelope[3], NumberFormatInfo.InvariantInfo); + double minX = Double.Parse((string) envelope[0], NumberFormatInfo.InvariantInfo); + double minY = Double.Parse((string) envelope[1], NumberFormatInfo.InvariantInfo); + double maxX = Double.Parse((string) envelope[2], NumberFormatInfo.InvariantInfo); + double maxY = Double.Parse((string) envelope[3], NumberFormatInfo.InvariantInfo); - Debug.Assert(minX <= maxX); - Debug.Assert(minY <= maxY); + Debug.Assert(minX <= maxX); + Debug.Assert(minY <= maxY); + + reader.Read(); // move away from array end + return new Envelope(minX, maxX, minY, maxY); + } reader.Read(); // move away from array end - return new Envelope(minX, maxX, minY, maxY); + return null; } public override bool CanConvert(Type objectType) diff --git a/NetTopologySuite.IO/NetTopologySuite.IO.GeoJSON/Converters/FeatureCollectionConverter.cs b/NetTopologySuite.IO/NetTopologySuite.IO.GeoJSON/Converters/FeatureCollectionConverter.cs index 55ed71f90..6c1088e3d 100644 --- a/NetTopologySuite.IO/NetTopologySuite.IO.GeoJSON/Converters/FeatureCollectionConverter.cs +++ b/NetTopologySuite.IO/NetTopologySuite.IO.GeoJSON/Converters/FeatureCollectionConverter.cs @@ -21,23 +21,29 @@ public override void WriteJson(JsonWriter writer, object value, JsonSerializer s FeatureCollection coll = value as FeatureCollection; if (coll == null) + { + if (serializer.NullValueHandling == NullValueHandling.Ignore) + { + writer.WriteToken(null); + } return; + } writer.WriteStartObject(); writer.WritePropertyName("type"); writer.WriteValue(coll.Type); writer.WritePropertyName("features"); serializer.Serialize(writer, coll.Features); - if (coll.CRS != null) + if (serializer.NullValueHandling == NullValueHandling.Include || coll.CRS != null) { writer.WritePropertyName("crs"); serializer.Serialize(writer, coll.CRS); } var bbox = coll.BoundingBox; - if (bbox != null) + if (serializer.NullValueHandling == NullValueHandling.Include || bbox != null) { writer.WritePropertyName("bbox"); - serializer.Serialize(writer, new[] { bbox.MinX, bbox.MinY, bbox.MaxX, bbox.MaxY }, typeof(double[])); + serializer.Serialize(writer, bbox, typeof(Envelope)); } writer.WriteEndObject(); } diff --git a/NetTopologySuite.IO/NetTopologySuite.IO.GeoJSON/Converters/FeatureConverter.cs b/NetTopologySuite.IO/NetTopologySuite.IO.GeoJSON/Converters/FeatureConverter.cs index 08547ee36..414bbb8c0 100644 --- a/NetTopologySuite.IO/NetTopologySuite.IO.GeoJSON/Converters/FeatureConverter.cs +++ b/NetTopologySuite.IO/NetTopologySuite.IO.GeoJSON/Converters/FeatureConverter.cs @@ -23,7 +23,7 @@ public override void WriteJson(JsonWriter writer, object value, JsonSerializer s if (serializer == null) throw new ArgumentNullException("serializer"); - IFeature feature = value as Feature; + var feature = value as IFeature; if (feature == null) return; @@ -35,7 +35,7 @@ public override void WriteJson(JsonWriter writer, object value, JsonSerializer s // Add the id here if present in attributes. // It will be skipped in serialization of properties - if (feature.Attributes.Exists("id")) + if (feature.Attributes != null && feature.Attributes.Exists("id")) { var id = feature.Attributes["id"]; writer.WritePropertyName("id"); @@ -43,19 +43,25 @@ public override void WriteJson(JsonWriter writer, object value, JsonSerializer s } // bbox (optional) - if (feature.BoundingBox != null) + if (serializer.NullValueHandling == NullValueHandling.Include || feature.BoundingBox != null) { writer.WritePropertyName("bbox"); serializer.Serialize(writer, feature.BoundingBox, typeof(Envelope)); } // geometry - writer.WritePropertyName("geometry"); - serializer.Serialize(writer, feature.Geometry); + if (serializer.NullValueHandling == NullValueHandling.Include || feature.Geometry != null) + { + writer.WritePropertyName("geometry"); + serializer.Serialize(writer, feature.Geometry, typeof(IGeometry)); + } // properties - writer.WritePropertyName("properties"); - serializer.Serialize(writer, feature.Attributes); + if (serializer.NullValueHandling == NullValueHandling.Include || feature.Attributes != null) + { + writer.WritePropertyName("properties"); + serializer.Serialize(writer, feature.Attributes, typeof(IAttributesTable)); + } writer.WriteEndObject(); } @@ -161,7 +167,7 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist /// public override bool CanConvert(Type objectType) { - return typeof(Feature).IsAssignableFrom(objectType); + return typeof(IFeature).IsAssignableFrom(objectType); } } } \ No newline at end of file diff --git a/NetTopologySuite.IO/NetTopologySuite.IO.GeoJSON/Converters/GeometryConverter.cs b/NetTopologySuite.IO/NetTopologySuite.IO.GeoJSON/Converters/GeometryConverter.cs index eba8523fd..6823f82ae 100644 --- a/NetTopologySuite.IO/NetTopologySuite.IO.GeoJSON/Converters/GeometryConverter.cs +++ b/NetTopologySuite.IO/NetTopologySuite.IO.GeoJSON/Converters/GeometryConverter.cs @@ -23,7 +23,10 @@ public override void WriteJson(JsonWriter writer, object value, JsonSerializer s { IGeometry geom = value as IGeometry; if (geom == null) + { + writer.WriteNull(); return; + } writer.WriteStartObject(); @@ -34,25 +37,43 @@ public override void WriteJson(JsonWriter writer, object value, JsonSerializer s switch (geomType) { case GeoJsonObjectType.Point: - serializer.Serialize(writer, geom.Coordinate); + if (serializer.NullValueHandling == NullValueHandling.Include || geom.Coordinate != null) + { + writer.WritePropertyName("coordinates"); + serializer.Serialize(writer, geom.Coordinate); + } break; case GeoJsonObjectType.LineString: case GeoJsonObjectType.MultiPoint: - serializer.Serialize(writer, geom.Coordinates); + var linealCoords = geom.Coordinates; + if (serializer.NullValueHandling == NullValueHandling.Include || linealCoords != null) + { + writer.WritePropertyName("coordinates"); + serializer.Serialize(writer, linealCoords); + } break; case GeoJsonObjectType.Polygon: IPolygon poly = geom as IPolygon; Debug.Assert(poly != null); - serializer.Serialize(writer, PolygonCoordinates(poly)); + var polygonCoords = PolygonCoordinates(poly); + if (serializer.NullValueHandling == NullValueHandling.Include || polygonCoords != null) + { + writer.WritePropertyName("coordinates"); + serializer.Serialize(writer, polygonCoords); + } break; case GeoJsonObjectType.MultiPolygon: IMultiPolygon mpoly = geom as IMultiPolygon; Debug.Assert(mpoly != null); - List> list = new List>(); + var list = new List>(); foreach (IPolygon mempoly in mpoly.Geometries) list.Add(PolygonCoordinates(mempoly)); - serializer.Serialize(writer, list); + if (serializer.NullValueHandling == NullValueHandling.Include || list.Count > 0) + { + writer.WritePropertyName("coordinates"); + serializer.Serialize(writer, list); + } break; case GeoJsonObjectType.GeometryCollection: @@ -64,7 +85,11 @@ public override void WriteJson(JsonWriter writer, object value, JsonSerializer s List coordinates = new List(); foreach (IGeometry geometry in ((IGeometryCollection)geom).Geometries) coordinates.Add(geometry.Coordinates); - serializer.Serialize(writer, coordinates); + if (serializer.NullValueHandling == NullValueHandling.Include || coordinates.Count > 0) + { + writer.WritePropertyName("coordinates"); + serializer.Serialize(writer, coordinates); + } break; } diff --git a/NetTopologySuite.IO/NetTopologySuite.IO.GeoJSON/Converters/ICRSObjectConverter.cs b/NetTopologySuite.IO/NetTopologySuite.IO.GeoJSON/Converters/ICRSObjectConverter.cs index d13f3a233..af5ceba77 100644 --- a/NetTopologySuite.IO/NetTopologySuite.IO.GeoJSON/Converters/ICRSObjectConverter.cs +++ b/NetTopologySuite.IO/NetTopologySuite.IO.GeoJSON/Converters/ICRSObjectConverter.cs @@ -19,7 +19,10 @@ public override void WriteJson(JsonWriter writer, object value, JsonSerializer s ICRSObject crs = value as ICRSObject; if (crs == null) + { + writer.WriteToken(JsonToken.Null); return; + } writer.WriteStartObject(); writer.WritePropertyName("type"); diff --git a/NetTopologySuite.IO/NetTopologySuite.IO.GeoJSON/GeoJsonSerializer.cs b/NetTopologySuite.IO/NetTopologySuite.IO.GeoJSON/GeoJsonSerializer.cs index 8d0354ef4..e691caae9 100644 --- a/NetTopologySuite.IO/NetTopologySuite.IO.GeoJSON/GeoJsonSerializer.cs +++ b/NetTopologySuite.IO/NetTopologySuite.IO.GeoJSON/GeoJsonSerializer.cs @@ -10,6 +10,39 @@ namespace NetTopologySuite.IO /// public class GeoJsonSerializer : JsonSerializer { + public new static JsonSerializer CreateDefault() + { + var s = JsonSerializer.CreateDefault(); + AddGeoJsonConverters(s, GeometryFactory.Default); + return s; + } + + public static JsonSerializer Create(IGeometryFactory factory) + { + return Create(new JsonSerializerSettings {NullValueHandling = NullValueHandling.Ignore}, factory); + } + + public static JsonSerializer Create(JsonSerializerSettings settings, IGeometryFactory factory) + { + var s = JsonSerializer.Create(settings); + AddGeoJsonConverters(s, factory); + return s; + } + + private static void AddGeoJsonConverters(JsonSerializer s, IGeometryFactory factory) + { + var c = s.Converters; + c.Add(new ICRSObjectConverter()); + c.Add(new FeatureCollectionConverter()); + c.Add(new FeatureConverter()); + c.Add(new AttributesTableConverter()); + c.Add(new GeometryConverter(factory)); + c.Add(new GeometryArrayConverter()); + c.Add(new CoordinateConverter()); + c.Add(new EnvelopeConverter()); + + } + /// /// Initializes a new instance of the class. /// diff --git a/NetTopologySuite.IO/NetTopologySuite.IO.GeoJSON/GeoJsonWriter.cs b/NetTopologySuite.IO/NetTopologySuite.IO.GeoJSON/GeoJsonWriter.cs index 96ade9f4b..6493b9a3d 100644 --- a/NetTopologySuite.IO/NetTopologySuite.IO.GeoJSON/GeoJsonWriter.cs +++ b/NetTopologySuite.IO/NetTopologySuite.IO.GeoJSON/GeoJsonWriter.cs @@ -3,6 +3,7 @@ using System.Text; using GeoAPI.Geometries; using NetTopologySuite.Features; +using NetTopologySuite.Geometries; using Newtonsoft.Json; namespace NetTopologySuite.IO @@ -13,6 +14,14 @@ namespace NetTopologySuite.IO /// public class GeoJsonWriter { + public GeoJsonWriter() + { + SerializerSettings = new JsonSerializerSettings(); + SerializerSettings.NullValueHandling = NullValueHandling.Ignore; + } + + public JsonSerializerSettings SerializerSettings { get; set; } + /// /// Writes the specified geometry. /// @@ -23,7 +32,8 @@ public string Write(IGeometry geometry) if (geometry == null) throw new ArgumentNullException("geometry"); - JsonSerializer g = new GeoJsonSerializer(geometry.Factory); + JsonSerializer g = GeoJsonSerializer.Create(SerializerSettings, geometry.Factory); + StringBuilder sb = new StringBuilder(); using (StringWriter sw = new StringWriter(sb)) g.Serialize(sw, geometry); @@ -37,7 +47,7 @@ public string Write(IGeometry geometry) /// public string Write(IFeature feature) { - JsonSerializer g = new GeoJsonSerializer(); + JsonSerializer g = GeoJsonSerializer.Create(SerializerSettings, feature.Geometry.Factory); ; StringBuilder sb = new StringBuilder(); using (StringWriter sw = new StringWriter(sb)) g.Serialize(sw, feature); @@ -51,7 +61,7 @@ public string Write(IFeature feature) /// public string Write(FeatureCollection featureCollection) { - JsonSerializer g = new GeoJsonSerializer(); + JsonSerializer g = GeoJsonSerializer.Create(SerializerSettings, featureCollection.Features[0].Geometry.Factory); ; StringBuilder sb = new StringBuilder(); using (StringWriter sw = new StringWriter(sb)) g.Serialize(sw, featureCollection); @@ -65,7 +75,7 @@ public string Write(FeatureCollection featureCollection) /// public string Write(object value) { - JsonSerializer g = new GeoJsonSerializer(); + JsonSerializer g = GeoJsonSerializer.Create(SerializerSettings, GeometryFactory.Default); ; StringBuilder sb = new StringBuilder(); using (StringWriter sw = new StringWriter(sb)) g.Serialize(sw, value); diff --git a/NetTopologySuite.IO/NetTopologySuite.IO.Tests/GeoJSON/FeatureConverterTest.cs b/NetTopologySuite.IO/NetTopologySuite.IO.Tests/GeoJSON/FeatureConverterTest.cs index bf4924f87..670a53e9d 100644 --- a/NetTopologySuite.IO/NetTopologySuite.IO.Tests/GeoJSON/FeatureConverterTest.cs +++ b/NetTopologySuite.IO/NetTopologySuite.IO.Tests/GeoJSON/FeatureConverterTest.cs @@ -23,7 +23,7 @@ public class FeatureConverterTest public void CanConvertTest() { FeatureConverter target = new FeatureConverter(); - Type objectType = typeof(Feature); + Type objectType = typeof(IFeature); const bool expected = true; bool actual = target.CanConvert(objectType); Assert.AreEqual(expected, actual); @@ -38,15 +38,18 @@ public void WriteJsonTest() FeatureConverter target = new FeatureConverter(); StringBuilder sb = new StringBuilder(); JsonTextWriter writer = new JsonTextWriter(new StringWriter(sb)); + AttributesTable attributes = new AttributesTable(); attributes.AddAttribute("test1", "value1"); IFeature value = new Feature(new Point(23, 56), attributes); - JsonSerializer serializer = new GeoJsonSerializer(); + JsonSerializer serializer = new GeoJsonSerializer {NullValueHandling = NullValueHandling.Ignore}; target.WriteJson(writer, value, serializer); writer.Flush(); + Assert.AreEqual("{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[23.0,56.0]},\"properties\":{\"test1\":\"value1\"}}", sb.ToString()); } + /// /// A test for WriteJson /// @@ -59,7 +62,7 @@ public void WriteJsonWithArrayTest() AttributesTable attributes = new AttributesTable(); attributes.AddAttribute("test1", new [] { "value1", "value2" }); IFeature value = new Feature(new Point(23, 56), attributes); - JsonSerializer serializer = new GeoJsonSerializer(); + JsonSerializer serializer = new GeoJsonSerializer {NullValueHandling = NullValueHandling.Ignore}; target.WriteJson(writer, value, serializer); writer.Flush(); Assert.AreEqual("{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[23.0,56.0]},\"properties\":{\"test1\":[\"value1\",\"value2\"]}}", sb.ToString()); diff --git a/NetTopologySuite.IO/NetTopologySuite.IO.Tests/GeoJSON/GeoJsonSerializerTest.cs b/NetTopologySuite.IO/NetTopologySuite.IO.Tests/GeoJSON/GeoJsonSerializerTest.cs index 0f80c9aa2..50793c146 100644 --- a/NetTopologySuite.IO/NetTopologySuite.IO.Tests/GeoJSON/GeoJsonSerializerTest.cs +++ b/NetTopologySuite.IO/NetTopologySuite.IO.Tests/GeoJSON/GeoJsonSerializerTest.cs @@ -30,7 +30,7 @@ public void GeoJsonSerializerFeatureCollectionTest() IFeature feature = new Feature(new Point(23, 56), attributes); FeatureCollection featureCollection = new FeatureCollection(new Collection {feature}) {CRS = new NamedCRS("name1")}; - JsonSerializer serializer = new GeoJsonSerializer(); + JsonSerializer serializer = new GeoJsonSerializer {NullValueHandling = NullValueHandling.Ignore}; serializer.Serialize(writer, featureCollection); writer.Flush(); Assert.AreEqual("{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[23.0,56.0]},\"properties\":{\"test1\":\"value1\"}}],\"crs\":{\"type\":\"name\",\"properties\":{\"name\":\"name1\"}}}", sb.ToString()); @@ -47,7 +47,7 @@ public void GeoJsonSerializerFeatureTest() AttributesTable attributes = new AttributesTable(); attributes.AddAttribute("test1", "value1"); IFeature feature = new Feature(new Point(23, 56), attributes); - JsonSerializer serializer = new GeoJsonSerializer(); + JsonSerializer serializer = new GeoJsonSerializer { NullValueHandling = NullValueHandling.Ignore }; serializer.Serialize(writer, feature); writer.Flush(); Assert.AreEqual("{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[23.0,56.0]},\"properties\":{\"test1\":\"value1\"}}", sb.ToString()); diff --git a/NetTopologySuite.IO/NetTopologySuite.IO.Tests/GeoJSON/GeoJsonWriterTest.cs b/NetTopologySuite.IO/NetTopologySuite.IO.Tests/GeoJSON/GeoJsonWriterTest.cs index 7589b582e..9d33603c2 100644 --- a/NetTopologySuite.IO/NetTopologySuite.IO.Tests/GeoJSON/GeoJsonWriterTest.cs +++ b/NetTopologySuite.IO/NetTopologySuite.IO.Tests/GeoJSON/GeoJsonWriterTest.cs @@ -27,7 +27,9 @@ public void GeoJsonWriterWriteFeatureCollectionTest() attributes.AddAttribute("test1", "value1"); IFeature feature = new Feature(new Point(23, 56), attributes); FeatureCollection featureCollection = new FeatureCollection(new Collection { feature }) { CRS = new NamedCRS("name1") }; - string actual = new GeoJsonWriter().Write(featureCollection); + var gjw = new GeoJsonWriter(); + gjw.SerializerSettings.NullValueHandling = NullValueHandling.Ignore; + string actual = gjw.Write(featureCollection); Assert.AreEqual("{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[23.0,56.0]},\"properties\":{\"test1\":\"value1\"}}],\"crs\":{\"type\":\"name\",\"properties\":{\"name\":\"name1\"}}}", actual); } @@ -75,7 +77,7 @@ public void GeoJsonWriterWriteAnyObjectTest() AttributesTable attributes = new AttributesTable(); DateTime Date = new DateTime(2012, 8, 8).Date; - JsonSerializer g = new GeoJsonSerializer(); + JsonSerializer g = new GeoJsonSerializer { NullValueHandling = NullValueHandling.Ignore }; StringBuilder sb = new StringBuilder(); using (StringWriter sw = new StringWriter(sb)) g.Serialize(sw, Date); @@ -86,7 +88,9 @@ public void GeoJsonWriterWriteAnyObjectTest() IFeature feature = new Feature(new Point(23, 56), attributes); FeatureCollection featureCollection = new FeatureCollection(new Collection { feature }) { CRS = new NamedCRS("name1") }; - string actual = new GeoJsonWriter().Write(new { featureCollection, Date = Date }); + var gjw = new GeoJsonWriter(); + gjw.SerializerSettings.NullValueHandling = NullValueHandling.Ignore; + string actual = gjw.Write(new { featureCollection, Date = Date }); Assert.AreEqual(expectedResult, actual); } } diff --git a/NetTopologySuite.IO/NetTopologySuite.IO.Tests/GeoJSON/GitHubIssues.cs b/NetTopologySuite.IO/NetTopologySuite.IO.Tests/GeoJSON/GitHubIssues.cs index 3d72a8e0d..208e63760 100644 --- a/NetTopologySuite.IO/NetTopologySuite.IO.Tests/GeoJSON/GitHubIssues.cs +++ b/NetTopologySuite.IO/NetTopologySuite.IO.Tests/GeoJSON/GitHubIssues.cs @@ -1,4 +1,5 @@ using System; +using System.Configuration; using System.Globalization; using System.IO; using System.Text; @@ -23,7 +24,7 @@ public void TestIssue83() ""id"": 1, ""properties"": { ""Name"": ""test"" } }"; - var s = new GeoJsonSerializer(); + var s = GeoJsonSerializer.Create(GeometryFactory.Default); Feature f = null; Assert.DoesNotThrow(() => f = s.Deserialize(new JsonTextReader(new StringReader(geoJson))) @@ -338,9 +339,18 @@ public void TestWhenFeatureCollectionHasBBox() } [Category("GitHub Issue")] - [Test(Description = "Testcase for GitHub Issue 95, FeatureCollection having \"bbox\" property")] + [Test(Description = "Testcase for GitHub Issue 120, Feature having null properties")] public void TestRoundtripSerializingDeserializingFeature() { + var gf = new GeometryFactory(); + var f1 = new Feature(gf.CreatePoint(new Coordinate(-104.50348159865847, 40.891762392617345)), null); + var f2 = SandD(f1); + DoCheck(f1, f2); + + var t1 = new TestFeature { Geometry = f1.Geometry }; + var t2 = SandD(t1); + DoCheck(t1, t2); + string jsonWithoutProps = "{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-104.50348159865847,40.891762392617345]}}"; DoCheck(jsonWithoutProps); string jsonWithValidProps = "{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-104.50348159865847,40.891762392617345]},\"properties\":{\"aaa\":1,\"bbb\":2}}"; @@ -360,5 +370,197 @@ private static void DoCheck(string json, bool nullProps = true) Assert.AreEqual(nullProps, feature.Attributes == null); Assert.IsNull(feature.BoundingBox); } + + private static IFeature SandD(IFeature input) + { + var s = new GeoJsonSerializer {NullValueHandling = NullValueHandling.Ignore, FloatFormatHandling = FloatFormatHandling.DefaultValue, FloatParseHandling = FloatParseHandling.Double}; + var sb = new StringBuilder(); + s.Serialize(new JsonTextWriter(new StringWriter(sb)), input, typeof(IFeature)); + return (IFeature)s.Deserialize(new JsonTextReader(new StringReader(sb.ToString()))); + + } + + private static void DoCheck(IFeature f1, IFeature f2) + { + if (f1 == null) + { + Assert.That(f2, Is.Null); + return; + } + if (f1.Geometry != null) Assert.That(f2.Geometry, Is.Not.Null, "f2.Geometry is not null"); + if (f1.Geometry != null) Assert.That(f1.Geometry.EqualsExact(f2.Geometry), Is.True, "f1.Geometry.EqualsExact(f2.Geometry)"); + if (f1.BoundingBox != null) + { + if (f1.BoundingBox.Equals(f2.BoundingBox)) + Assert.That(true, Is.True); + else + { + Assert.That(Math.Abs(f1.BoundingBox.MinX - f2.BoundingBox.MinX), Is.LessThanOrEqualTo(1e-10), "f1.BoundingBox.Equals(f2.BoundingBox)"); + Assert.That(Math.Abs(f1.BoundingBox.MaxX - f2.BoundingBox.MaxX), Is.LessThanOrEqualTo(1e-10), "f1.BoundingBox.Equals(f2.BoundingBox)"); + Assert.That(Math.Abs(f1.BoundingBox.MinY - f2.BoundingBox.MinY), Is.LessThanOrEqualTo(1e-10), "f1.BoundingBox.Equals(f2.BoundingBox)"); + Assert.That(Math.Abs(f1.BoundingBox.MaxY - f2.BoundingBox.MaxY), Is.LessThanOrEqualTo(1e-10), "f1.BoundingBox.Equals(f2.BoundingBox)"); + } + + } + if (f1.Attributes != null) + { + Assert.That(f2.Attributes, Is.Not.Null); + var names1 = f1.Attributes.GetNames(); + var names2 = f2.Attributes.GetNames(); + Assert.That(names1.Length, Is.EqualTo(names2.Length)); + foreach (var name in names1) + { + var v1 = f1.Attributes[name]; + object v2 = null; + Assert.DoesNotThrow(() => v2 = f2.Attributes[name]); + if (v1 == null) + { + Assert.That(v2, Is.Null); + } + else + { + Assert.That(v1, Is.EqualTo(v2)); + } + } + } + else + Assert.That(f2.Attributes, Is.Null); + } + + + private class TestFeature : IFeature + { + private readonly AttributeMapper _mapper; + + public TestFeature() + { + _mapper = new AttributeMapper(this); + Item1 = true; + Item2 = "The quick brown fox jumped over the fence."; + Item3 = double.Epsilon; + Item4 = new[] {1, 2, 3, 4}; + } + + public IAttributesTable Attributes + { + get { return _mapper; } + set { throw new NotSupportedException(); } + } + + public IGeometry Geometry { get; set; } + + public Envelope BoundingBox + { + get { return Geometry != null ? Geometry.EnvelopeInternal : null; } + set { throw new NotSupportedException();} + } + + public bool Item1 { get; set; } + public string Item2 { get; set; } + + public double Item3 { get; set; } + + public int[] Item4 { get; set; } + + private class AttributeMapper : IAttributesTable + { + private readonly TestFeature _feature; + public AttributeMapper(TestFeature feature) + { + _feature = feature; + } + public void AddAttribute(string attributeName, object value) + { + switch (attributeName.ToLowerInvariant()) + { + case "item1": + _feature.Item1 = (bool) value; + break; + case "item2": + _feature.Item2 = (string)value; + break; + case "item3": + _feature.Item3 = (double)value; + break; + case "item4": + _feature.Item4 = (int[])value; + break; + default: + throw new ArgumentException("attributeName"); + } + } + + public void DeleteAttribute(string attributeName) + { + throw new NotSupportedException(); + } + + public Type GetType(string attributeName) + { + switch (attributeName.ToLowerInvariant()) + { + case "item1": + return _feature.Item1.GetType(); + case "item2": + return _feature.Item2.GetType(); + case "item3": + return _feature.Item3.GetType(); + case "item4": + return _feature.Item4.GetType(); + } + throw new ArgumentException("attributeName"); + } + + public object this[string attributeName] + { + get + { + switch (attributeName.ToLowerInvariant()) + { + case "item1": + return _feature.Item1; + case "item2": + return _feature.Item2; + case "item3": + return _feature.Item3; + case "item4": + return _feature.Item4; + default: + throw new ArgumentException("attributeName"); + } + } + set { AddAttribute(attributeName, value); } + } + + public bool Exists(string attributeName) + { + switch (attributeName.ToLowerInvariant()) + { + case "item1": + case "item2": + case "item3": + case "item4": + return true; + } + return false; + } + + public int Count { get { return 4; } } + public string[] GetNames() + { + return new[] {"Item1", "Item2", "Item3", "Item4"}; + } + + public object[] GetValues() + { + return new object[] + {_feature.Item1, _feature.Item2, _feature.Item3, _feature.Item4}; + } + } + } + + } + + } \ No newline at end of file diff --git a/NetTopologySuite.Samples.Console/Tests/Github/Issues.cs b/NetTopologySuite.Samples.Console/Tests/Github/Issues.cs index 02bfe6aba..416754c57 100644 --- a/NetTopologySuite.Samples.Console/Tests/Github/Issues.cs +++ b/NetTopologySuite.Samples.Console/Tests/Github/Issues.cs @@ -1,9 +1,17 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; using System.Linq; using GeoAPI.Geometries; using NetTopologySuite.Geometries; +using NetTopologySuite.Geometries.Utilities; using NetTopologySuite.Index.KdTree; +using NetTopologySuite.IO; using NetTopologySuite.Operation.Polygonize; +using NetTopologySuite.Operation.Valid; using NUnit.Framework; namespace NetTopologySuite.Samples.Tests.Github @@ -67,5 +75,236 @@ public void Should_Find_Correct_Number_Of_Polygons_From_Lines() var polygons = polygonizer.GetPolygons(); Assert.AreEqual(64, polygons.Count); } + + [Test(Description = "GitHub Issue #122/1")] + public void Polygon_intersection_Error1() + { + //Arrange + var reader = new WKTReader(); + var g1 = reader.Read( +@"LINEARRING (0.0000000000000000 -6.1026585365860800, 3.8000000000000000 -5.7690000000000000, +7.9000000000000000 -5.4090000000000000, 12.0000000000000000 -5.3690000000000000, +16.1000000000000000 -5.0990000000000000, 20.3000000000000000 -5.1390000000000000, +24.5000000000000000 -4.9090000000000000, 28.5000000000000000 -4.6390000000000000, +32.6000000000000000 -4.1890000000000000, 36.6000000000000000 -3.2890000000000000, +40.8000000000000000 -3.1890000000000000, 44.8000000000000000 -2.9490000000000000, +49.0000000000000000 -2.8200000000000000, 53.3000000000000000 -2.3600000000000000, +57.3000000000000000 -2.1400000000000000, 61.3000000000000000 -1.5200000000000000, +65.5000000000000000 -1.3900000000000000, 69.6000000000000000 -1.1700000000000000, +73.6000000000000000 -1.0100000000000000, 77.9000000000000000 -0.9700000000000000, +82.0000000000000000 -0.8000000000000000, 86.2000000000000000 -0.7400000000000000, +90.2000000000000000 -0.6700000000000000, 94.5000000000000000 -0.6000000000000000, +98.6000000000000000 -0.5000000000000000, 102.6000000000000000 -0.3900000000000000, +106.9000000000000000 -0.2400000000000000, 111.1000000000000000 -0.1500000000000000, +115.1000000000000000 -0.1510000000000000, 119.2000000000000000 -0.1510000000000000, +123.1000000000000000 -0.0510000000000000, 127.2000000000000000 0.0590000000000000, +131.2000000000000000 0.2090000000000000, 132.2000000000000000 1.0250000000000000, +132.6000000000000000 2.6400000000000000, 132.6000000000000000 2.6400000000000000, +132.6000000000000000 0.5000000000000000, 128.6000000000000000 0.5000000000000000, +106.2000000000000000 -2.3000000000000000, 49.9000000000000000 -2.3000000000000000, +31.0000000000000000 -5.4500000000000000, 0.0000000000000000 -5.4500000000000000, +0.0000000000000000 -6.1026585365860800)"); + var g2 = reader.Read( +@"LINEARRING (0.0000000000000000 2.6400000000000000, 0.0000000000000000 -6.0030697674418800, +0.3200000000000000 -5.9800000000000000, 4.6200000000000000 -5.6700000000000000, +8.8100000000000000 -5.1600000000000000, 13.1000000000000000 -5.2100000000000000, +17.1200000000000000 -4.8400000000000000, 21.2800000000000000 -5.1700000000000000, +25.5000000000000000 -5.1600000000000000, 29.7100000000000000 -4.6700000000000000, +34.1300000000000000 -3.9000000000000000, 38.2800000000000000 -3.4800000000000000, +42.6400000000000000 -3.3000000000000000, 46.9800000000000000 -3.2600000000000000, +51.1600000000000000 -2.8900000000000000, 55.3200000000000000 -2.8300000000000000, +59.3000000000000000 -2.9200000000000000, 63.6800000000000000 -2.3600000000000000, +67.4800000000000000 -2.5700000000000000, 70.8900000000000000 -2.4800000000000000, +74.8900000000000000 -2.5500000000000000, 78.8800000000000000 -2.6900000000000000, +82.4400000000000000 -2.4600000000000000, 87.5300000000000000 -2.8100000000000000, +91.6200000000000000 -2.7500000000000000, 95.9000000000000000 -2.1000000000000000, +100.2200000000000000 -2.2100000000000000, 104.4400000000000000 -2.3000000000000000, +109.1500000000000000 -1.9500000000000000, 113.2400000000000000 -2.8200000000000000, +117.2400000000000000 -1.9600000000000000, 120.9400000000000000 -1.3000000000000000, +125.0300000000000000 -0.8200000000000000, 126.6800000000000000 0.1300000000000000, +132.5500000000000000 1.5400000000000000, 132.6000000000000000 2.6400000000000000, +132.6000000000000000 2.6400000000000000, 132.6000000000000000 2.6400000000000000, +0.0000000000000000 2.6400000000000000)"); + + //Act + var res = g1.Intersection(g2); + + // + ToImage(1, g1, g2, res); + + // Assert + Assert.That(res, Is.Not.Null); + Debug.WriteLine(res.AsText()); + + } + + [Test(Description = "GitHub Issue #122/2")] + public void Polygon_intersection_Error2() + { + //Arrange + var reader = new WKTReader(); + var g1 = reader.Read( +@"POLYGON ((0.0000000000000000 -6.1026585365860800, 3.8000000000000000 -5.7690000000000000, +7.9000000000000000 -5.4090000000000000, 12.0000000000000000 -5.3690000000000000, +16.1000000000000000 -5.0990000000000000, 20.3000000000000000 -5.1390000000000000, +24.5000000000000000 -4.9090000000000000, 28.5000000000000000 -4.6390000000000000, +32.6000000000000000 -4.1890000000000000, 36.6000000000000000 -3.2890000000000000, +40.8000000000000000 -3.1890000000000000, 44.8000000000000000 -2.9490000000000000, +49.0000000000000000 -2.8200000000000000, 53.3000000000000000 -2.3600000000000000, +57.3000000000000000 -2.1400000000000000, 61.3000000000000000 -1.5200000000000000, +65.5000000000000000 -1.3900000000000000, 69.6000000000000000 -1.1700000000000000, +73.6000000000000000 -1.0100000000000000, 77.9000000000000000 -0.9700000000000000, +82.0000000000000000 -0.8000000000000000, 86.2000000000000000 -0.7400000000000000, +90.2000000000000000 -0.6700000000000000, 94.5000000000000000 -0.6000000000000000, +98.6000000000000000 -0.5000000000000000, 102.6000000000000000 -0.3900000000000000, +106.9000000000000000 -0.2400000000000000, 111.1000000000000000 -0.1500000000000000, +115.1000000000000000 -0.1510000000000000, 119.2000000000000000 -0.1510000000000000, +123.1000000000000000 -0.0510000000000000, 127.2000000000000000 0.0590000000000000, +131.2000000000000000 0.2090000000000000, 132.2000000000000000 1.0250000000000000, +132.6000000000000000 2.6400000000000000, 132.6000000000000000 2.6400000000000000, +132.6000000000000000 0.5000000000000000, 128.6000000000000000 0.5000000000000000, +106.2000000000000000 -2.3000000000000000, 49.9000000000000000 -2.3000000000000000, +31.0000000000000000 -5.4500000000000000, 0.0000000000000000 -5.4500000000000000, +0.0000000000000000 -6.1026585365860800))"); + var isValidOp = new IsValidOp(g1); + if (!isValidOp.IsValid) + { + Debug.WriteLine("g1 is not valid:" + isValidOp.ValidationError); + g1 = g1.Buffer(0); + Debug.WriteLine(g1.AsText()); + } + var g2 = reader.Read( +@"POLYGON ((0.0000000000000000 2.6400000000000000, 0.0000000000000000 -6.0030697674418800, +0.3200000000000000 -5.9800000000000000, 4.6200000000000000 -5.6700000000000000, +8.8100000000000000 -5.1600000000000000, 13.1000000000000000 -5.2100000000000000, +17.1200000000000000 -4.8400000000000000, 21.2800000000000000 -5.1700000000000000, +25.5000000000000000 -5.1600000000000000, 29.7100000000000000 -4.6700000000000000, +34.1300000000000000 -3.9000000000000000, 38.2800000000000000 -3.4800000000000000, +42.6400000000000000 -3.3000000000000000, 46.9800000000000000 -3.2600000000000000, +51.1600000000000000 -2.8900000000000000, 55.3200000000000000 -2.8300000000000000, +59.3000000000000000 -2.9200000000000000, 63.6800000000000000 -2.3600000000000000, +67.4800000000000000 -2.5700000000000000, 70.8900000000000000 -2.4800000000000000, +74.8900000000000000 -2.5500000000000000, 78.8800000000000000 -2.6900000000000000, +82.4400000000000000 -2.4600000000000000, 87.5300000000000000 -2.8100000000000000, +91.6200000000000000 -2.7500000000000000, 95.9000000000000000 -2.1000000000000000, +100.2200000000000000 -2.2100000000000000, 104.4400000000000000 -2.3000000000000000, +109.1500000000000000 -1.9500000000000000, 113.2400000000000000 -2.8200000000000000, +117.2400000000000000 -1.9600000000000000, 120.9400000000000000 -1.3000000000000000, +125.0300000000000000 -0.8200000000000000, 126.6800000000000000 0.1300000000000000, +132.5500000000000000 1.5400000000000000, 132.6000000000000000 2.6400000000000000, +132.6000000000000000 2.6400000000000000, 132.6000000000000000 2.6400000000000000, +0.0000000000000000 2.6400000000000000))"); + isValidOp = new IsValidOp(g1); + if (!isValidOp.IsValid) + { + Debug.WriteLine("g2 is not valid:" + isValidOp.ValidationError); + g2 = g2.Buffer(0); + Debug.WriteLine(g2.AsText()); + } + //Act + var res = g1.Intersection(g2); + + // + ToImage(1, g1, g2, res); + + // Assert + Assert.That(res, Is.Not.Null); + Debug.WriteLine(res.AsText()); + + } + + [Test(Description = "GitHub Issue #123")] + public void LineString_Intersection_with_Polygon_return_null1() + { + //Arrange + var reader = new WKTReader(); + var g1 = reader.Read(@"LINESTRING (0 -5.15, 30 -5.15, 48.9 -2, 105.2 -2, 127.6 0.8, 132.6 0.8)"); + var g2 = reader.Read(@"LINEARRING (15.325555555555553 0.8, 42.309375 0.8, 42.309375 -5.15, 15.325555555555553 -5.15, 15.325555555555553 0.8)"); + + //Act + var res = g1.Intersection(g2); + + // + ToImage(1, g1, g2, res); + + // Assert + Assert.That(res, Is.Not.Null); + Debug.WriteLine(res.AsText()); + } + + + static void ToImage(int nr, IGeometry geom1, IGeometry geom2, IGeometry geom3) + { + + var gpw = new Windows.Forms.GraphicsPathWriter(); + + var extent = geom1.EnvelopeInternal; + if (geom2 != null) + extent.ExpandToInclude(geom2.EnvelopeInternal); + extent.ExpandBy(0.05 * extent.Width); + + using (var img = new Bitmap(ImageWidth, ImageHeight)) + { + using (var gr = Graphics.FromImage(img)) + { + var at = CreateAffineTransformation(extent); + gr.Clear(Color.WhiteSmoke); + gr.SmoothingMode = SmoothingMode.AntiAlias; + //gr.Transform = CreateTransform(extent); + + var gp1 = gpw.ToShape(at.Transform(geom1)); + if (geom1 is IPolygonal) + gr.FillPath(new SolidBrush(Color.FromArgb(64, Color.Blue)), gp1); + gr.DrawPath(Pens.Blue, gp1); + + var gp2 = gpw.ToShape(at.Transform(geom2)); + if (geom2 is IPolygonal) + gr.FillPath(new SolidBrush(Color.FromArgb(64, Color.OrangeRed)), gp2); + gr.DrawPath(Pens.OrangeRed, gp2); + + //at = CreateAffineTransformation(extent, ImageWidth); + + var gp3 = gpw.ToShape(at.Transform(geom3)); + if (geom3 is IPolygonal) + gr.FillPath(new SolidBrush(Color.FromArgb(64, Color.Gold)), gp3); + gr.DrawPath(Pens.Gold, gp3); + + + } + var path = System.IO.Path.ChangeExtension(System.IO.Path.GetTempFileName(), "png"); + img.Save(path, ImageFormat.Png); + Console.WriteLine("Image for Test {0} written to {1}", nr, new Uri(path).AbsoluteUri); + } + } + + private const int ImageWidth = 640; + private const int ImageHeight = 480; + + private static AffineTransformation CreateAffineTransformation(Envelope env, int offsetX = 0) + { + var imageRatio = ImageWidth / ImageHeight; + var ratio = env.Width / env.Height; + if (ratio > imageRatio) + { + var growHeight = (env.Width / imageRatio - env.Height) / 2; + env.ExpandBy(0, growHeight); + } + else if (ratio < imageRatio) + { + var growWidth = (env.Height * imageRatio - env.Width) / 2; + env.ExpandBy(growWidth, 0); + } + + var s1 = new Coordinate(env.MinX, env.MaxY); + var t1 = new Coordinate(offsetX, 0); + var s2 = new Coordinate(env.MaxX, env.MaxY); + var t2 = new Coordinate(offsetX + ImageWidth, 0); + var s3 = new Coordinate(env.MaxX, env.MinY); + var t3 = new Coordinate(offsetX + ImageWidth, ImageHeight); + + var atb = new AffineTransformationBuilder(s1, s2, s3, t1, t2, t3); + return atb.GetTransformation(); + } + } } \ No newline at end of file