diff --git a/NetTopologySuite.IO.Esri.sln b/NetTopologySuite.IO.Esri.sln index 6c3ae6d..f8ba00d 100644 --- a/NetTopologySuite.IO.Esri.sln +++ b/NetTopologySuite.IO.Esri.sln @@ -13,7 +13,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetTopologySuite.IO.Esri.Shapefile", "src\NetTopologySuite.IO.Esri.Shapefile\NetTopologySuite.IO.Esri.Shapefile.csproj", "{6E95BC91-8066-4420-B61F-854DE5F3A746}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NetTopologySuite.IO.Esri.TestConsole", "test\NetTopologySuite.IO.Esri.TestConsole\NetTopologySuite.IO.Esri.TestConsole.csproj", "{AA37EFC6-737F-4E8A-A79C-2D179C1B38EC}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetTopologySuite.IO.Esri.TestConsole", "test\NetTopologySuite.IO.Esri.TestConsole\NetTopologySuite.IO.Esri.TestConsole.csproj", "{AA37EFC6-737F-4E8A-A79C-2D179C1B38EC}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Test", "Test", "{800A3D3F-D350-4AAF-89F2-CBA8C0D54862}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Src", "Src", "{FDC792AB-D6EB-4963-A3F5-1D7A11AA47FC}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetTopologySuite.IO.Esri.Test", "test\NetTopologySuite.IO.Esri.Test\NetTopologySuite.IO.Esri.Test.csproj", "{64B2B87A-EFA0-4BEA-B5D8-7EA989630D68}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -29,10 +35,19 @@ Global {AA37EFC6-737F-4E8A-A79C-2D179C1B38EC}.Debug|Any CPU.Build.0 = Debug|Any CPU {AA37EFC6-737F-4E8A-A79C-2D179C1B38EC}.Release|Any CPU.ActiveCfg = Release|Any CPU {AA37EFC6-737F-4E8A-A79C-2D179C1B38EC}.Release|Any CPU.Build.0 = Release|Any CPU + {64B2B87A-EFA0-4BEA-B5D8-7EA989630D68}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {64B2B87A-EFA0-4BEA-B5D8-7EA989630D68}.Debug|Any CPU.Build.0 = Debug|Any CPU + {64B2B87A-EFA0-4BEA-B5D8-7EA989630D68}.Release|Any CPU.ActiveCfg = Release|Any CPU + {64B2B87A-EFA0-4BEA-B5D8-7EA989630D68}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {6E95BC91-8066-4420-B61F-854DE5F3A746} = {FDC792AB-D6EB-4963-A3F5-1D7A11AA47FC} + {AA37EFC6-737F-4E8A-A79C-2D179C1B38EC} = {800A3D3F-D350-4AAF-89F2-CBA8C0D54862} + {64B2B87A-EFA0-4BEA-B5D8-7EA989630D68} = {800A3D3F-D350-4AAF-89F2-CBA8C0D54862} + EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {06DEE0C5-480C-4A70-BC80-3791D98E6A89} EndGlobalSection diff --git a/README.md b/README.md index 5679118..9991e90 100644 --- a/README.md +++ b/README.md @@ -4,23 +4,19 @@ This library provides forward-only readers and writers for [Esri shapefiles](htt ## DBF -Shapefile feature attributes are held in a dBASE format file (.dbf extension). Each attribute record +Shapefile feature attributes are held in a [dBASE format file](dBASE.md) (.dbf extension). Each attribute record has a one-to-one relationship with the associated shape record. Classes whose name starts with `Dbf` (eg. `DbfReader`) provide direct access to dBASE files. ```c# -using (var dbf = new DbfReader(dbfPath)) +using var dbf = new DbfReader(dbfPath); +foreach (var record in dbf) { - foreach (var fields in dbf) + foreach (var fieldName in record.GetNames()) { - Console.WriteLine("Record ID: " + fields["Id"]); - var fieldNames = fields.Keys; - foreach (var fieldName in fieldNames) - { - Console.WriteLine($"{fieldName, 10} {fields[fieldName]}"); - } - Console.WriteLine(); + Console.WriteLine($"{fieldName,10} {record[fieldName]}"); } + Console.WriteLine(); } ``` @@ -31,13 +27,9 @@ a shape with a list of its vertices. Classes whose name starts with `Shp` (eg. ` provide direct access to main file. ```c# -using (var shpStream = File.OpenRead(shpPath)) -using (var shp = new ShpPointReader(shpStream, GeometryFactory.Default)) +foreach (var geometry in Shapefile.ReadAllGeometries(shpPath)) { - while (shp.Read()) - { - Console.WriteLine(shp.Geometry); - } + Console.WriteLine(geometry); } ``` @@ -58,7 +50,6 @@ Under the hood they are decorators wrapping `Dbf` and `Shp` classes. ```c# foreach (var feature in Shapefile.ReadAllFeatures(shpPath)) { - Console.WriteLine(" Record ID: " + feature.Attributes["Id"]); foreach (var attrName in feature.Attributes.GetNames()) { Console.WriteLine($"{attrName,10}: {feature.Attributes[attrName]}"); @@ -74,24 +65,27 @@ foreach (var feature in Shapefile.ReadAllFeatures(shpPath)) var features = new List(); for (int i = 1; i < 5; i++) { - var lineCoords = new List(); - lineCoords.Add(new CoordinateZ(i, i + 1, i)); - lineCoords.Add(new CoordinateZ(i, i, i)); - lineCoords.Add(new CoordinateZ(i + 1, i, i)); + var lineCoords = new List + { + new CoordinateZ(i, i + 1, i), + new CoordinateZ(i, i, i), + new CoordinateZ(i + 1, i, i) + }; var line = new LineString(lineCoords.ToArray()); var mline = new MultiLineString(new LineString[] { line }); - var attributes = new AttributesTable(); - attributes.Add("date", new DateTime(2000, 1, i + 1)); - attributes.Add("float", i * 0.1); - attributes.Add("int", i); - attributes.Add("logical", i % 2 == 0); - attributes.Add("text", i.ToString("0.00")); + var attributes = new AttributesTable + { + { "date", new DateTime(2000, 1, i + 1) }, + { "float", i * 0.1 }, + { "int", i }, + { "logical", i % 2 == 0 }, + { "text", i.ToString("0.00") } + }; var feature = new Feature(mline, attributes); features.Add(feature); } - Shapefile.WriteAllFeatures(features, shpPath); ``` diff --git a/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/DbfReader.cs b/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/DbfReader.cs index c207bd3..1ad2eb6 100644 --- a/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/DbfReader.cs +++ b/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/DbfReader.cs @@ -1,3 +1,4 @@ +using NetTopologySuite.Features; using NetTopologySuite.IO.Esri.Dbf.Fields; using System; using System.Collections; @@ -15,7 +16,7 @@ namespace NetTopologySuite.IO.Esri.Dbf /// /// Class that allows records in a dbase file to be enumerated. /// - public class DbfReader : ManagedDisposable, IEnumerable> + public class DbfReader : ManagedDisposable, IEnumerable { private int HeaderSize; private int CurrentIndex = 0; @@ -160,6 +161,10 @@ private Encoding GetEncoding(string dbfPath) try { + if (int.TryParse(cpgText, out var cpgId)) + { + return Encoding.GetEncoding(cpgId); + } return Encoding.GetEncoding(cpgText); } catch (Exception) @@ -178,6 +183,31 @@ private string ReadFileText(string filePath, string fileExtension) return File.ReadAllText(filePath).Trim(); } + + + /// + /// Reads field values from underlying stream and advances the enumerator to the next record. + /// + /// + /// true if the enumerator was successfully advanced to the next record; + /// false if the enumerator has passed the end of the table. + /// + public bool Read() + { + var readSucceeded = Read(out var deleted); + if (!readSucceeded) + { + return false; + } + if (deleted) + { + return Read(); + } + return true; + } + + + /// /// Reads field values from underlying stream and advances the enumerator to the next record. /// @@ -217,22 +247,41 @@ public bool Read(out bool deleted) /// true if the enumerator was successfully advanced to the next element; /// false if the enumerator has passed the end of the collection. /// - public bool Read(out IReadOnlyDictionary values, out bool deleted) + public bool Read(out IAttributesTable values, out bool deleted) { if (!Read(out deleted)) { - //values = null; // This would cause recreating array in next iteration values = DbfField.EmptyFieldValues; return false; } - values = Fields.GetValues(); + values = Fields.ToAttributesTable(); return true; } + /// + /// Moves enumerator to specified position and reads values from underlying stream. + /// + /// Thre record index. + /// + /// Table containing record fields (attributes). + /// + public IAttributesTable ReadEntry(int index) + { + if (index < 0 || index >= RecordCount) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + + DbfStream.Position = HeaderSize + (index * RecordSize); + CurrentIndex = index; + Read(out var _); + return Fields.ToAttributesTable(); + } + #region IEnumerable - IEnumerator> IEnumerable>.GetEnumerator() + IEnumerator IEnumerable.GetEnumerator() { return new DbfEnumerator(this); } @@ -242,10 +291,10 @@ IEnumerator IEnumerable.GetEnumerator() return new DbfEnumerator(this); } - private class DbfEnumerator : IEnumerator> + private class DbfEnumerator : IEnumerator { private readonly DbfReader Owner; - public IReadOnlyDictionary Current { get; private set; } + public IAttributesTable Current { get; private set; } object IEnumerator.Current => Current; public DbfEnumerator(DbfReader owner) diff --git a/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/DbfStreamExtensions.cs b/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/DbfStreamExtensions.cs index 51a66fb..bdb6ebd 100644 --- a/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/DbfStreamExtensions.cs +++ b/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/DbfStreamExtensions.cs @@ -119,7 +119,7 @@ public static DbfField ReadDbaseFieldDescriptor(this Stream stream, Encoding enc } else if (type == DbfType.Numeric) { - return new DbfNumericField(name, length, precision); + return DbfNumericField.Create(name, length, precision); } else if (type == DbfType.Float) { diff --git a/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/DbfWriter.cs b/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/DbfWriter.cs index f392948..300b3ef 100644 --- a/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/DbfWriter.cs +++ b/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/DbfWriter.cs @@ -1,5 +1,6 @@ using NetTopologySuite.IO.Esri.Dbf.Fields; using System; +using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; @@ -15,6 +16,8 @@ namespace NetTopologySuite.IO.Esri.Dbf public class DbfWriter : ManagedDisposable { private Stream DbfStream; + private int Id = 1; + private DbfField IdField; /// /// Returns the fields in the dbase file. @@ -46,8 +49,8 @@ public class DbfWriter : ManagedDisposable /// /// Stream of source DBF file. /// dBASE field definitions. - /// DBF file encoding or null if encoding should be resolved from DBF reserved bytes. - public DbfWriter(Stream stream, IReadOnlyList fields, Encoding encoding) + /// DBF file encoding. Defaults to UTF8. + public DbfWriter(Stream stream, IReadOnlyList fields, Encoding encoding = null) { Encoding = encoding ?? Encoding.UTF8; IntializeFields(fields); @@ -60,8 +63,8 @@ public DbfWriter(Stream stream, IReadOnlyList fields, Encoding encodin /// /// Path to DBF file. /// dBASE field definitions. - /// DBF file encoding or null if encoding should be resolved from DBF reserved bytes. - public DbfWriter(string dbfPath, IReadOnlyList fields, Encoding encoding) + /// DBF file encoding. Defaults to UTF8. + public DbfWriter(string dbfPath, IReadOnlyList fields, Encoding encoding = null) { Encoding = encoding ?? Encoding.UTF8; IntializeFields(fields); @@ -80,20 +83,41 @@ public DbfWriter(string dbfPath, IReadOnlyList fields, Encoding encodi private void IntializeFields(IReadOnlyList fields) { if (fields == null || fields.Count < 1) - throw new ArgumentException("dBASE file must contain at least one field.", nameof(fields)); + { + // https://desktop.arcgis.com/en/arcmap/latest/manage-data/shapefiles/geoprocessing-considerations-for-shapefile-output.htm + // # Attribute limitations + // The dBASE file must contain at least one field. + // When you create a shapefile or dBASE table, an integer ID field is created as a default. + fields = new List() { new DbfNumericInt32Field("Id") }; + } if (fields.Count > Dbf.MaxFieldCount) + { throw new ArgumentException($"dBASE file must contain no more than {Dbf.MaxFieldCount} fields.", nameof(fields)); + } Fields = new DbfFieldCollection(fields.Count); - for (int i = 0; i < fields.Count; i++) + foreach (var field in fields) { - if (fields[i] is DbfCharacterField textField) - textField.Encoding = Encoding; - Fields.Add(fields[i]); + InitializeField(field); } } + private void InitializeField(DbfField field) + { + if (field is DbfCharacterField textField) + { + textField.Encoding = Encoding; + } + + if (field.Name.Equals("Id", StringComparison.OrdinalIgnoreCase)) + { + IdField = field; + } + + Fields.Add(field); + } + private void WriteHeader(Stream stream) { DbfStream = stream ?? throw new ArgumentNullException("Uninitialized dBASE stream.", nameof(stream)); @@ -148,6 +172,7 @@ private void WriteCpgEncoding(string dbfPath, Encoding encoding) /// public void Write() { + UpdateIdField(); DbfStream.WriteByte(Dbf.ValidRecordMark); for (int i = 0; i < Fields.Count; i++) @@ -188,6 +213,18 @@ protected override void DisposeManagedResources() FinalizeWriting(); base.DisposeManagedResources(); // FinalizeWriting() is using underlying file streams. Dispose them at the end. } + + private void UpdateIdField() + { + if (IdField == null) + { + return; + } + if (IdField.IsNull) + { + IdField.Value = Id++; + } + } } diff --git a/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfCharacterField.cs b/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfCharacterField.cs index f6a86ef..f003f6b 100644 --- a/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfCharacterField.cs +++ b/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfCharacterField.cs @@ -39,6 +39,9 @@ public override object Value set { StringValue = value?.ToString(); } } + /// + public override bool IsNull => StringValue == null; + internal override void ReadValue(Stream stream) { StringValue = stream.ReadString(Length, Encoding)?.TrimEnd(); diff --git a/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfDateField.cs b/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfDateField.cs index 2b05504..39f4e99 100644 --- a/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfDateField.cs +++ b/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfDateField.cs @@ -35,6 +35,9 @@ public override object Value set { DateValue = (DateTime?)value; } } + /// + public override bool IsNull => DateValue == null; + internal override void ReadValue(Stream stream) { diff --git a/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfField.cs b/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfField.cs index 9cacfc6..5a254bd 100644 --- a/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfField.cs +++ b/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfField.cs @@ -1,4 +1,5 @@ -using System; +using NetTopologySuite.Features; +using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; @@ -65,10 +66,14 @@ internal DbfField(string name, DbfType type, int length, int precision) */ if (length < 1) + { throw new ArgumentException($"Ivalid dBASE field length: {length}.", nameof(length)); + } if (precision < 0) - precision = 0; // throw new ArgumentException($"Ivalid dBASE III field decimal places count: {precision}.", nameof(precision)); + { + throw new ArgumentException($"Ivalid dBASE field precision: {precision}.", nameof(precision)); + } Name = name; FieldType = type; @@ -103,9 +108,14 @@ internal Exception GetFieldValueError(object value, string additionalDescription /// public abstract object Value { get; set; } + /// + /// Determines if the field currently does not contain any value. + /// + public virtual bool IsNull => Value == null; - internal static readonly IReadOnlyDictionary EmptyFieldValues = new ReadOnlyDictionary(new Dictionary()); + + internal static readonly IAttributesTable EmptyFieldValues = new AttributesTable(); /// /// Creates dBASE field determined using specified value. @@ -127,25 +137,28 @@ public static DbfField Create(string name, Type type) if (type == typeof(DateTime) || type == typeof(DateTime?)) return new DbfDateField(name); - if (type == typeof(sbyte) || type == typeof(sbyte?) || type == typeof(byte) || type == typeof(byte?)) - return new DbfNumericField(name, 4, 0); - - if (type == typeof(short) || type == typeof(short?) || type == typeof(ushort) || type == typeof(ushort?)) - return new DbfNumericField(name, 6, 0); - - if (type == typeof(int) || type == typeof(int?) || type == typeof(uint) || type == typeof(uint?)) - return new DbfNumericField(name, 11, 0); - - if (type == typeof(long) || type == typeof(long?) || type == typeof(ulong) || type == typeof(ulong?)) - return new DbfNumericField(name, DbfNumericField.MaxFieldLength, 0); - - if (type == typeof(decimal) || type == typeof(decimal?)) - return new DbfNumericField(name, DbfNumericField.MaxFieldLength, DbfNumericField.MaxFieldPrecision); + if (type == typeof(sbyte) || type == typeof(sbyte?)) + return new DbfNumericInt32Field(name); + if (type == typeof(byte) || type == typeof(byte?)) + return new DbfNumericInt32Field(name); + if (type == typeof(short) || type == typeof(short?)) + return new DbfNumericInt32Field(name); + if (type == typeof(ushort) || type == typeof(ushort?)) + return new DbfNumericInt32Field(name); + if (type == typeof(int) || type == typeof(int?)) + return new DbfNumericInt32Field(name); + if (type == typeof(uint) || type == typeof(uint?)) + return new DbfNumericInt32Field(name); + + if (type == typeof(long) || type == typeof(long?)) + return new DbfNumericInt64Field(name); + if (type == typeof(ulong) || type == typeof(ulong?)) + return new DbfNumericInt64Field(name); if (type == typeof(double) || type == typeof(float) || type == typeof(double?) || type == typeof(float?)) return new DbfFloatField(name); - throw new ArgumentException($"Unsupported dBASE field value: {type} ({type.GetType().Name})"); + throw new NotSupportedException($"Unsupported dBASE field type: {type.GetType().Name} ({type})"); } } diff --git a/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfFieldCollection.cs b/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfFieldCollection.cs index 5be98fb..1c424de 100644 --- a/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfFieldCollection.cs +++ b/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfFieldCollection.cs @@ -1,4 +1,5 @@ -using System; +using NetTopologySuite.Features; +using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; @@ -65,8 +66,8 @@ internal void Add(DbfField field) /// /// Reads current fields values. /// - /// Array of field values. - public IReadOnlyDictionary GetValues() + /// Dictionary containging field names and values. + public IReadOnlyDictionary ToDictionary() { var values = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (var field in Fields) @@ -76,18 +77,41 @@ internal void Add(DbfField field) return new ReadOnlyDictionary(values); } + /// + /// Reads current fields values. + /// + /// Dictionary containging field names and values. + public IAttributesTable ToAttributesTable() + { + var attributes = new AttributesTable(); + foreach (var field in Fields) + { + attributes.Add(field.Name, field.Value); + } + return attributes; + } + + /// + /// Reads current fields values. + /// + /// Array of field values. + public object[] GetValues() + { + var valueArray = new object[Count]; + for (int i = 0; i < Count; i++) + { + valueArray[i] = Fields[i].Value; + } + return valueArray; + } + /// /// Sets current filed values. /// /// public void SetValues(IReadOnlyDictionary values) { - if (values == null) - throw new ArgumentNullException("dBASE record must contain values.", nameof(values)); - - if (values.Count < Fields.Count) - throw new ArgumentNullException("Invalid dBASE record value count: " + values.Count + ". Expected: " + Fields.Count, nameof(values)); - + CheckValuesCount(values); foreach (var field in Fields) { if (values.TryGetValue(field.Name, out var value)) @@ -100,5 +124,27 @@ public void SetValues(IReadOnlyDictionary values) } } } + + /// + /// Sets current filed values. + /// + /// + public void SetValues(IReadOnlyList values) + { + CheckValuesCount(values); + for (int i = 0; i < Count; i++) + { + Fields[i].Value = values[i]; + } + } + + private void CheckValuesCount(IReadOnlyCollection values) + { + if (values == null) + throw new ArgumentNullException("dBASE record must contain values.", nameof(values)); + + if (values.Count < Fields.Count) + throw new ArgumentNullException("Invalid dBASE record value count: " + values.Count + ". Expected: " + Fields.Count, nameof(values)); + } } } diff --git a/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfFieldsExtensions.cs b/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfFieldsExtensions.cs new file mode 100644 index 0000000..3dd0ed3 --- /dev/null +++ b/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfFieldsExtensions.cs @@ -0,0 +1,130 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace NetTopologySuite.IO.Esri.Dbf.Fields +{ + /// + /// DbfField collection extensions. + /// + public static class DbfFieldsExtensions + { + /// + /// Adds a DBF field. + /// + /// The field list. + /// Field name. + /// Field type. + /// + public static DbfField AddField(this ICollection fields, string name, Type type) + { + var field = DbfField.Create(name, type); + fields.Add(field); + return field; + } + + /// + /// Adds a DBF field. + /// + /// The field list. + /// Field name. + /// + public static DbfField AddField(this ICollection fields, string name) + where T : struct, IComparable, IConvertible, IFormattable + { + return AddField(fields, name, typeof(T)); + } + + /// + /// Adds a character string field. + /// + /// The field list. + /// Field name. + /// Field lenght. + /// + public static DbfCharacterField AddCharacterField(this ICollection fields, string name, int length = 254) + { + var field = new DbfCharacterField(name, length); + fields.Add(field); + return field; + } + + /// + /// Adds a date field. + /// + /// The field list. + /// Field name. + /// + public static DbfDateField AddDateField(this ICollection fields, string name) + { + var field = new DbfDateField(name); + fields.Add(field); + return field; + } + + /// + /// Adds a logical field. + /// + /// The field list. + /// Field name. + /// + public static DbfLogicalField AddLogicalField(this ICollection fields, string name) + { + var field = new DbfLogicalField(name); + fields.Add(field); + return field; + } + + /// + /// Adds an Int32 field. + /// + /// The field list. + /// Field name. + /// + public static DbfNumericInt32Field AddNumericInt32Field(this ICollection fields, string name) + { + var field = new DbfNumericInt32Field(name); + fields.Add(field); + return field; + } + + /// + /// Adds an Int64 field. + /// + /// The field list. + /// Field name. + /// + public static DbfNumericInt64Field AddNumericInt64Field(this ICollection fields, string name) + { + var field = new DbfNumericInt64Field(name); + fields.Add(field); + return field; + } + + /// + /// Adds a Double field. + /// + /// The field list. + /// Field name. + /// + public static DbfNumericDoubleField AddNumericDoubleField(this ICollection fields, string name) + { + var field = new DbfNumericDoubleField(name); + fields.Add(field); + return field; + } + + /// + /// Adds a float field. + /// + /// The field list. + /// Field name. + /// + public static DbfFloatField AddFloatField(this ICollection fields, string name) + { + var field = new DbfFloatField(name); + fields.Add(field); + return field; + } + } +} diff --git a/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfFloatField.cs b/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfFloatField.cs index 6a160ed..55ce7db 100644 --- a/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfFloatField.cs +++ b/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfFloatField.cs @@ -1,5 +1,4 @@ -using System; -using System.Globalization; +using System.Globalization; namespace NetTopologySuite.IO.Esri.Dbf.Fields { @@ -17,21 +16,18 @@ public class DbfFloatField : DbfNumericField // -1.2346e+004 => Length: 12, DecimalDigits: 4 => 12 - 4 = 8 private static readonly int ExponentialNotationDigitCount = 8; - private static readonly int DefaultFieldLength = 19; // That uses ArcMap 10.6 when creates 'Double' field. - private static readonly int DefaultFieldPrecision = 11; // That uses ArcMap 10.6 when creates 'Double' field. + internal static readonly int DefaultFieldLength = 19; // That uses ArcMap 10.6 when creates 'Double' field. + internal static readonly int DefaultFieldPrecision = 11; // That uses ArcMap 10.6 when creates 'Double' field. private readonly string NumberFormat; - /// - /// Initializes a new instance of the field class. + /// Intializes new instance of the numerif field class. /// /// Field name. - public DbfFloatField(string name) : this(name, DefaultFieldLength, DefaultFieldPrecision) - { - } - - internal DbfFloatField(string name, int length, int precision) + /// The number of significant digits (including decimal separator). + /// The number of fractional digits. + public DbfFloatField(string name, int length, int precision) : base(name, DbfType.Float, length, precision) { // Esri uses exponential notation for float fields: @@ -43,27 +39,18 @@ internal DbfFloatField(string name, int length, int precision) NumberFormat = "e" + (Length - ExponentialNotationDigitCount).ToString(); } - /// - public override object Value + /// + /// Initializes a new instance of the field class. + /// + /// Field name. + public DbfFloatField(string name) : this(name, DefaultFieldLength, DefaultFieldPrecision) { - get { return NumericValue; } - set - { - if (value == null) - { - NumericValue = null; - } - else - { - NumericValue = Convert.ToDouble(value); - } - } } /// - protected override double StringToNumber(string number) + protected override double StringToNumber(string s) { - return double.Parse(number, CultureInfo.InvariantCulture); + return double.Parse(s, CultureInfo.InvariantCulture); } /// diff --git a/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfLogicalField.cs b/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfLogicalField.cs index 4920e7d..36c9610 100644 --- a/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfLogicalField.cs +++ b/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfLogicalField.cs @@ -39,6 +39,9 @@ public override object Value set { LogicalValue = (bool?)value; } } + /// + public override bool IsNull => LogicalValue == null; + internal override void ReadValue(Stream stream) { var logicalValue = stream.ReadByteChar(); diff --git a/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfNumericDoubleField.cs b/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfNumericDoubleField.cs new file mode 100644 index 0000000..c7a802b --- /dev/null +++ b/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfNumericDoubleField.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; + +namespace NetTopologySuite.IO.Esri.Dbf.Fields +{ + /// + /// Int64 field definition. + /// + public class DbfNumericDoubleField : DbfNumericField + { + /// + /// Intializes new instance of the numerif field class. + /// + /// Field name. + /// The number of significant digits (including decimal separator). + /// The number of fractional digits. + public DbfNumericDoubleField(string name, int length, int precision) + : base(name, DbfType.Numeric, length, precision) + { + } + + /// + /// Initializes a new instance of the field class. + /// + /// Field name. + public DbfNumericDoubleField(string name) : this(name, MaxFieldLength, MaxFieldPrecision) + { + } + + /// + protected override double StringToNumber(string s) + { + return double.Parse(s, CultureInfo.InvariantCulture); + } + } +} diff --git a/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfNumericField.cs b/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfNumericField.cs index 5a9c41a..718ed0b 100644 --- a/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfNumericField.cs +++ b/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfNumericField.cs @@ -9,26 +9,57 @@ namespace NetTopologySuite.IO.Esri.Dbf.Fields /// /// Numeric field definition. /// - public abstract class DbfNumericField : DbfField where T : struct, IConvertible, IFormattable + public abstract class DbfNumericField : DbfField { internal static readonly int MaxFieldLength = 19; // Decimal point and any sign is included in field width, if present. internal static readonly int MaxFieldPrecision = 17; // dBASE specs states it could be max 15, but Esri uses up to 17. internal DbfNumericField(string name, DbfType type, int length, int precision) - : base(name, type, GetProperLength(length), GetProperPrecision(length, precision)) + : base(name, type, length, precision) { + // -0.12345 => legth: 8, decimalCount: 5 => should be (length - 3). But Esri allows decimalCount: 6 => (length - 2). } - /// - /// Initializes a new instance of the field class. - /// - /// Field name. - /// Field length. - /// Decmial places count. - protected DbfNumericField(string name, int length, int precision) - : this(name, DbfType.Numeric, length, precision) + internal static DbfField Create(string name, int length, int precision) { + // Consider writing a field based on `Byte` value. + // Such field would get the length=3 (Byte.MaxValue=255). + // Now consider you are reading the the same field. + // From the DBF point of view you know only that it has length=3 (and precision=0). + // What type would you use for such field? + // - The `Byte` type? (because it's you who create that field) + // - The `Int16` type? (because in the meantime one was able to write there 999 or even -3) + + // For above reasons use only very basic numeric types - Int32, Int64 and Double. + + if (precision <= 0 && length <= DbfNumericInt32Field.DefaultFieldLength) + { + return new DbfNumericInt32Field(name, length); + } + + if (precision <= 0) + { + return new DbfNumericInt64Field(name, length); + } + return new DbfNumericDoubleField(name, length, precision); + } + + } + + /// + /// Numeric field definition. + /// + public abstract class DbfNumericField : DbfNumericField where T : struct, IConvertible, IFormattable + { + private readonly string NumberFormat; + + internal DbfNumericField(string name, DbfType type, int length, int precision) + : base(name, type, length, precision) + { + NumberFormat = precision > 0 + ? "0." + new string('#', precision) + : "0"; } /// @@ -36,25 +67,25 @@ protected DbfNumericField(string name, int length, int precision) /// public T? NumericValue { get; set; } - internal static int GetProperLength(int length) + /// + public override object Value { - if (length < 1) - return 1; - return Math.Min(length, MaxFieldLength); + get { return NumericValue; } + set + { + if (value == null) + { + NumericValue = null; + } + else + { + NumericValue = (T)Convert.ChangeType(value, typeof(T), CultureInfo.InvariantCulture); + } + } } - private static int GetProperPrecision(int length, int precision) - { - if (precision < 0) - return 0; - - length = GetProperLength(length); - if (length < 2) - return 0; - - precision = Math.Min(precision, length - 2); // -0.12345 => legth: 8, decimalCount: 5 => should be (length - 3). But Esri allows decimalCount: 6 => (length - 2). - return Math.Min(precision, MaxFieldPrecision); - } + /// + public override bool IsNull => NumericValue == null; internal override void ReadValue(Stream stream) { @@ -65,6 +96,10 @@ internal override void ReadValue(Stream stream) return; } + // https://desktop.arcgis.com/en/arcmap/latest/manage-data/shapefiles/geoprocessing-considerations-for-shapefile-output.htm + // Null values are not supported in shapefiles. If a feature class containing nulls is converted to a shapefile, or a database table is converted to a dBASE file, the null values will be changed: + // Number: -1.7976931348623158e+308 (IEEE standard for the maximum negative value) + NumericValue = StringToNumber(valueText); } @@ -77,21 +112,19 @@ internal override void WriteValue(Stream stream) } var valueText = NumberToString(NumericValue.Value); + if (valueText.Length > this.Length) + { + throw GetFieldValueError(valueText, "Field value out of range."); + } // Length: 4 // 1.2 => " 1.2"; if (valueText.Length < this.Length) { - stream.WriteString(valueText.PadLeft(this.Length, ' '), Length, Encoding.ASCII); // PadLeft for Values - } - else - { - if (valueText.Length > this.Length) - { - throw GetFieldValueError(valueText, "Field value out of range."); - } - stream.WriteString(valueText, Length, Encoding.ASCII); + valueText = valueText.PadLeft(this.Length, ' '); // PadLeft for Values } + + stream.WriteString(valueText, Length, Encoding.ASCII); } @@ -100,53 +133,7 @@ internal override void WriteValue(Stream stream) /// /// Number to convert. /// A string that is equivalent to the specified numer. - protected abstract string NumberToString(T number); - - /// - /// Converts the string representation of a number to its number equivalent. - /// - /// A string that contains a number to convert. - /// A number that is equivalent to the specified string representation of numeric value. - protected abstract T StringToNumber(string number); - } - - - - /// - public class DbfNumericField : DbfNumericField - { - internal static readonly int MaxInt32FieldLength = int.MaxValue.ToString().Length; // 10 (int.MinValue => -2147483648 => 11, but Esri uses 10) - private readonly string NumberFormat = "0"; // For integers - - /// - public DbfNumericField(string name, int length, int precision) - : base(name, DbfType.Numeric, length, precision) - { - if (NumericScale > 0) - NumberFormat = "0." + new string('0', NumericScale); - } - - /// - /// Initializes a new instance of the integer field class. - /// - /// Field name. - public DbfNumericField(string name) - : this(name, MaxInt32FieldLength, 0) - { - } - - /// - protected override decimal StringToNumber(string number) - { - // https://desktop.arcgis.com/en/arcmap/latest/manage-data/shapefiles/geoprocessing-considerations-for-shapefile-output.htm - // Null values are not supported in shapefiles. If a feature class containing nulls is converted to a shapefile, or a database table is converted to a dBASE file, the null values will be changed: - // Number: -1.7976931348623158e+308 (IEEE standard for the maximum negative value) - - return decimal.Parse(number, CultureInfo.InvariantCulture); - } - - /// - protected override string NumberToString(decimal number) + protected virtual string NumberToString(T number) { var valueString = number.ToString(NumberFormat, CultureInfo.InvariantCulture); @@ -174,56 +161,13 @@ protected override string NumberToString(decimal number) return valueString; } - /// - public override object Value - { - get { return NumericValue; } - set - { - if (value == null) - { - NumericValue = null; - } - else - { - NumericValue = Convert.ToDecimal(value); - } - } - } - /// - /// representation of field value. - /// - public int? Int32Value - { - get => ConvertValue(Convert.ToInt32); - set => NumericValue = ConvertToDecimal(value); - } - - /// - /// representation of field value. + /// Converts the string representation of a number to its number equivalent. /// - public double? DoubleValue - { - get => ConvertValue(Convert.ToDouble); - set => NumericValue = ConvertToDecimal(value); - } - - private T? ConvertValue(Func converTo) where T : struct - { - if (NumericValue.HasValue) - return converTo(NumericValue.Value); - else - return null; - } + /// A string that contains a number to convert. + /// A number that is equivalent to the specified string representation of numeric value. + protected abstract T StringToNumber(string s); - private decimal? ConvertToDecimal(IConvertible value) - { - if (value == null) - return null; - else - return value.ToDecimal(CultureInfo.InvariantCulture); - } } diff --git a/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfNumericInt32Field.cs b/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfNumericInt32Field.cs new file mode 100644 index 0000000..9bcb511 --- /dev/null +++ b/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfNumericInt32Field.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; + +namespace NetTopologySuite.IO.Esri.Dbf.Fields +{ + /// + /// Int32 field definition. + /// + public class DbfNumericInt32Field : DbfNumericField + { + internal static readonly int DefaultFieldLength = 10; //-2147483648..2147483647 => 11, but Esri uses 10 + + /// + /// Intializes new instance of the numerif field class. + /// + /// Field name. + /// The number of digits. + public DbfNumericInt32Field(string name, int length) + : base(name, DbfType.Numeric, length, 0) + { + } + + /// + /// Initializes a new instance of the field class. + /// + /// Field name. + public DbfNumericInt32Field(string name) : this(name, DefaultFieldLength) + { + } + + /// + protected override int StringToNumber(string s) + { + return int.Parse(s, CultureInfo.InvariantCulture); + } + } +} diff --git a/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfNumericInt64Field.cs b/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfNumericInt64Field.cs new file mode 100644 index 0000000..2efeace --- /dev/null +++ b/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfNumericInt64Field.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; + +namespace NetTopologySuite.IO.Esri.Dbf.Fields +{ + /// + /// Int64 field definition. + /// + public class DbfNumericInt64Field : DbfNumericField + { + /// + /// Intializes new instance of the numerif field class. + /// + /// Field name. + /// The number of digits. + public DbfNumericInt64Field(string name, int length) + : base(name, DbfType.Numeric, length, 0) + { + } + + /// + /// Initializes a new instance of the field class. + /// + /// Field name. + public DbfNumericInt64Field(string name) : this(name, MaxFieldLength) + { + } + + /// + protected override long StringToNumber(string s) + { + return long.Parse(s, CultureInfo.InvariantCulture); + } + } +} diff --git a/src/NetTopologySuite.IO.Esri.Shapefile/Helpers/FeatureExtensions.cs b/src/NetTopologySuite.IO.Esri.Shapefile/Helpers/FeatureExtensions.cs index fcc3c01..1c53cf6 100644 --- a/src/NetTopologySuite.IO.Esri.Shapefile/Helpers/FeatureExtensions.cs +++ b/src/NetTopologySuite.IO.Esri.Shapefile/Helpers/FeatureExtensions.cs @@ -3,6 +3,7 @@ using NetTopologySuite.IO.Esri.Dbf.Fields; using System; using System.Collections.Generic; +using System.Linq; namespace NetTopologySuite.IO.Esri { @@ -50,12 +51,16 @@ internal static Feature ToFeature(this Geometry geometry, AttributesTable attrib internal static AttributesTable GetAttributesTable(this DbfFieldCollection fields) { - return new AttributesTable(fields.GetValues()); + return new AttributesTable(fields.ToDictionary()); } internal static DbfField[] GetDbfFields(this IAttributesTable attributes) { + if (attributes == null) + { + return null; + } var names = attributes.GetNames(); var fields = new DbfField[names.Length]; @@ -121,6 +126,11 @@ private static Ordinates GetOrdinates(this Geometry geometry) if (geometry is Point point) return point.CoordinateSequence.Ordinates; + if (geometry is MultiPoint multiPoint) + { + return GetOrdinates(multiPoint.Geometries.FirstOrDefault()); + } + if (geometry is LineString line) return line.CoordinateSequence.Ordinates; diff --git a/src/NetTopologySuite.IO.Esri.Shapefile/Shapefile.cs b/src/NetTopologySuite.IO.Esri.Shapefile/Shapefile.cs index 2ff7dc9..4274a6d 100644 --- a/src/NetTopologySuite.IO.Esri.Shapefile/Shapefile.cs +++ b/src/NetTopologySuite.IO.Esri.Shapefile/Shapefile.cs @@ -60,12 +60,23 @@ internal static ShapeType GetShapeType(Stream shpStream) shpStream.Position = 0; var fileCode = shpStream.ReadInt32BigEndian(); if (fileCode != Shapefile.FileCode) - throw new FileLoadException("Invalid shapefile format."); + throw new ShapefileException("Invalid shapefile format."); shpStream.Advance(28); return shpStream.ReadShapeType(); } + + /// + /// Gets default for specified geometry. + /// + /// A Geometry object. + /// Shape type. + public static ShapeType GetShapeType(Geometry geometry) + { + return geometry.GetShapeType(); + } + /// /// Reads shape type information from SHP file. /// @@ -73,9 +84,7 @@ internal static ShapeType GetShapeType(Stream shpStream) /// Shape type. public static ShapeType GetShapeType(string shpPath) { - if (Path.GetExtension(shpPath).ToLowerInvariant() != ".shp") - throw new FileLoadException("Specified file must have .shp extension."); - + shpPath = Path.ChangeExtension(shpPath, ".shp"); using (var shpStream = new FileStream(shpPath, FileMode.Open, FileAccess.Read, FileShare.Read)) { return GetShapeType(shpStream); @@ -86,32 +95,31 @@ public static ShapeType GetShapeType(string shpPath) /// Opens shapefile reader. /// /// Path to shapefile. - /// Geometry factory. - /// DBF file encoding. If null encoding will be guess from related .CPG file or from reserved DBF bytes. + /// Reader options. /// Shapefile reader. - public static ShapefileReader OpenRead(string shpPath, GeometryFactory factory = null, Encoding encoding = null) + public static ShapefileReader OpenRead(string shpPath, ShapefileReaderOptions options = null) { var shapeType = GetShapeType(shpPath); if (shapeType.IsPoint()) { - return new ShapefilePointReader(shpPath, factory, encoding); + return new ShapefilePointReader(shpPath, options); } else if (shapeType.IsMultiPoint()) { - return new ShapefileMultiPointReader(shpPath, factory, encoding); + return new ShapefileMultiPointReader(shpPath, options); } else if (shapeType.IsPolyLine()) { - return new ShapefilePolyLineReader(shpPath, factory, encoding); + return new ShapefilePolyLineReader(shpPath, options); } else if (shapeType.IsPolygon()) { - return new ShapefilePolygonReader(shpPath, factory, encoding); + return new ShapefilePolygonReader(shpPath, options); } else { - throw new FileLoadException("Unsupported shapefile type: " + shapeType, shpPath); + throw new ShapefileException("Unsupported shapefile type: " + shapeType, shpPath); } } @@ -119,13 +127,29 @@ public static ShapefileReader OpenRead(string shpPath, GeometryFactory factory = /// Reads all features from shapefile. /// /// Path to shapefile. - /// Geometry factory. - /// DBF file encoding. If null encoding will be guess from related .CPG file or from reserved DBF bytes. + /// Reader options. /// Shapefile features. - public static Feature[] ReadAllFeatures(string shpPath, GeometryFactory factory = null, Encoding encoding = null) + public static Feature[] ReadAllFeatures(string shpPath, ShapefileReaderOptions options = null) + { + using (var shp = OpenRead(shpPath, options)) + { + return shp.ToArray(); + } + } + + + /// + /// Reads all geometries from SHP file. + /// + /// Path to SHP file. + /// Reader options. + /// Shapefile geometries. + public static Geometry[] ReadAllGeometries(string shpPath, ShapefileReaderOptions options = null) { - using (var shp = OpenRead(shpPath, factory, encoding)) + shpPath = Path.ChangeExtension(shpPath, ".shp"); + using (var shpStream = File.OpenRead(shpPath)) { + var shp = Shp.Shp.OpenRead(shpStream, options); return shp.ToArray(); } } @@ -135,32 +159,30 @@ public static Feature[] ReadAllFeatures(string shpPath, GeometryFactory factory /// Opens shapefile writer. /// /// Path to shapefile. - /// Shape type. - /// Shapefile fields definitions. - /// DBF file encoding. If null encoding will be guess from related .CPG file or from reserved DBF bytes. - /// Projection metadata for the shapefile (.prj file). + /// Writer options. /// Shapefile writer. - public static ShapefileWriter OpenWrite(string shpPath, ShapeType type, IReadOnlyList fields, Encoding encoding = null, string projection = null) + public static ShapefileWriter OpenWrite(string shpPath, ShapefileWriterOptions options) { - if (type.IsPoint()) + options = options ?? throw new ArgumentNullException(nameof(options)); + if (options.ShapeType.IsPoint()) { - return new ShapefilePointWriter(shpPath, type, fields, encoding, projection); + return new ShapefilePointWriter(shpPath, options); } - else if (type.IsMultiPoint()) + else if (options.ShapeType.IsMultiPoint()) { - return new ShapefileMultiPointWriter(shpPath, type, fields, encoding, projection); + return new ShapefileMultiPointWriter(shpPath, options); } - else if (type.IsPolyLine()) + else if (options.ShapeType.IsPolyLine()) { - return new ShapefilePolyLineWriter(shpPath, type, fields, encoding, projection); + return new ShapefilePolyLineWriter(shpPath, options); } - else if (type.IsPolygon()) + else if (options.ShapeType.IsPolygon()) { - return new ShapefilePolygonWriter(shpPath, type, fields, encoding, projection); + return new ShapefilePolygonWriter(shpPath, options); } else { - throw new FileLoadException("Unsupported shapefile type: " + type, shpPath); + throw new ShapefileException("Unsupported shapefile type: " + options.ShapeType, shpPath); } } @@ -170,9 +192,7 @@ public static ShapefileWriter OpenWrite(string shpPath, ShapeType type, IReadOnl /// /// Features to be written. /// Path to shapefile. - /// DBF file encoding. If null encoding will be guess from related .CPG file or from reserved DBF bytes. - /// Projection metadata for the shapefile (.prj file). - public static void WriteAllFeatures(IEnumerable features, string shpPath, Encoding encoding = null, string projection = null) + public static void WriteAllFeatures(IEnumerable features, string shpPath) { if (features == null) throw new ArgumentNullException(nameof(features)); @@ -183,8 +203,9 @@ public static void WriteAllFeatures(IEnumerable features, string shpPa var fields = firstFeature.Attributes.GetDbfFields(); var shapeType = features.FindNonEmptyGeometry().GetShapeType(); + var options = new ShapefileWriterOptions(shapeType, fields); - using (var shpWriter = OpenWrite(shpPath, shapeType, fields, encoding, projection)) + using (var shpWriter = OpenWrite(shpPath, options)) { shpWriter.Write(features); } diff --git a/src/NetTopologySuite.IO.Esri.Shapefile/ShapefileException.cs b/src/NetTopologySuite.IO.Esri.Shapefile/ShapefileException.cs index 811b793..b2ff171 100644 --- a/src/NetTopologySuite.IO.Esri.Shapefile/ShapefileException.cs +++ b/src/NetTopologySuite.IO.Esri.Shapefile/ShapefileException.cs @@ -1,36 +1,31 @@ using System; +using System.IO; using System.Runtime.Serialization; namespace NetTopologySuite.IO.Esri { /// - /// The exception that is thrown when a non-fatal application error occurs related to Topology functionality. + /// The exception that is thrown when a non-fatal application error occurs related to Shapefile functionality. /// - public class ShapefileException : ApplicationException + public class ShapefileException : FileLoadException { - /// - /// Initializes a new instance of the ShapefileException class. - /// + /// public ShapefileException() { } - /// - /// Initializes a new instance of the ShapefileException class with a specified error message. - /// - /// A message that describes the error. + /// public ShapefileException(string message) : base(message) { } - /// - /// Initializes a new instance of the ShapefileException class with serialized data. - /// - /// The object that holds the serialized object data. - /// The contextual information about the source or destination. + /// public ShapefileException(SerializationInfo info, StreamingContext context) : base(info, context) { } - /// - /// Initializes a new instance of the ShapefileException class with a specified error message and a reference to the inner exception that is the cause of this exception. - /// - /// The error message that explains the reason for the exception. - /// The exception that is the cause of the current exception. If the innerException parameter is not a null reference, the current exception is raised in a catch block that handles the inner exception + /// public ShapefileException(string message, Exception innerException) : base(message, innerException) { } + + + /// + public ShapefileException(string message, string fileName) : base(message, fileName) { } + + /// + public ShapefileException(string message, string fileName, Exception inner) : base(message, fileName, inner) { } } } diff --git a/src/NetTopologySuite.IO.Esri.Shapefile/ShapefileReaderOptions.cs b/src/NetTopologySuite.IO.Esri.Shapefile/ShapefileReaderOptions.cs new file mode 100644 index 0000000..9dc29fa --- /dev/null +++ b/src/NetTopologySuite.IO.Esri.Shapefile/ShapefileReaderOptions.cs @@ -0,0 +1,127 @@ +using NetTopologySuite.Geometries; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NetTopologySuite.IO.Esri +{ + /// + /// Shapefile reader options + /// + public class ShapefileReaderOptions + { + /// + /// Geometry factory + /// + public GeometryFactory Factory { get; set; } = null; + + /// + /// DBF file encoding. If null encoding will be guess from related .CPG file or from reserved DBF bytes + /// + public Encoding Encoding { get; set; } = null; + + /// + /// The minimum bounding rectangle (BMR) used to filter out shapes located outside it. + /// + public Envelope MbrFilter { get; set; } = null; + + + /// + /// Minimum bounding rectangle (MBR) filtering options. + /// + public MbrFilterOption MbrFilterOption { get; set; } = MbrFilterOption.FilterByExtent; + + internal int DbfRecordCount { get; set; } = int.MaxValue; + + /// + /// Set it to true if you want to proceed the enumeration until the end of the file is reached + /// even if some features are corrupted (so possibly valid shapes are not ignored). + /// + /// + /// https://github.com/NetTopologySuite/NetTopologySuite.IO.ShapeFile/issues/46 + /// + public bool SkipFailures { get; set; } = false; + + internal static ShapefileReaderOptions Default = new ShapefileReaderOptions(); + + } + + + /// + /// Minimum bounding rectangle (MBR) filtering options. + /// + public enum MbrFilterOption + { + /// + /// Filter by geometry extent. This option is faster but gives less acurate results (it can get some shapes that are not actually in the MBR). + /// + FilterByExtent, + + /// + /// Filter by geometry shape. This option gives precise results but it also greatly affect the performance. + /// + FilterByGeometry + } + + + /// + /// Specifies the geometry building algorithm to use. + /// + /// Rings order explained. + public enum GeometryBuilderMode + { + /// + /// Shape geometry will be read according to ESRI Shapefile specification. + /// For example polygons will be created based on Shapefile's ring orientation semantics. + /// + /// Ring typeOrientation + /// ShellClockwise, Shapefile's left-hand-rule + /// HoleCounter-Clockwise, Shapefile's right-hand-rule + /// + /// Shape geometries that do not follow ESRI Shapefile specification will trhow an ShapefileException. + /// + Strict, + + /// + /// Shape geometry will be read assuming that it does conform to ESRI Shapefile specification. + /// All errors will be ignored wich will result in geometries having property set to false. + /// + IngoreFailures, + + /// + /// Shape geometry that does not conform to ESRI Shapefile specification will be skipped. + /// The reader will omit the geometry and related attributes as if they didn't exist. + /// + SkipFailures, + + /// + /// Invalid shape geometry will will be tried to be fixed using all possible methods. + /// For polygons following logic will be applied: + /// + /// Sort all rings. + /// Consider all rings as a potential shell, search the valid holes for any possible shell. + /// Check if the ring is inside any shell: if true, it can be considered a potential hole for the shell. + /// Check if the ring is inside any hole of the shell: if true, this means that is actually a shell of a distinct geometry, + /// and NOT a valid hole for the shell. A hole inside another hole is not allowed. + /// + /// + /// + /// This option is considerably slower, especially for complex geometries + /// (i.e.: polygons with a large number of holes having large numer of vertices). + /// + FixFailures, + + /// + /// Invalid shape geometry will will be tried to be fixed using only basic methods. + /// For polygons following logic will be applied: + /// + /// Do not sorting rings but assume that polygons are serialized in the following order: Shell[, Holes][, Shell[, Holes][, ...]]. + /// Assume that the first ring that is not contained by the current polygon is the start of a new polygon. + /// + /// + /// + /// This option is is faster than but can result in geometries having property set to false. + /// + FixBasicFailures, + } +} diff --git a/src/NetTopologySuite.IO.Esri.Shapefile/ShapefileWriterOptions.cs b/src/NetTopologySuite.IO.Esri.Shapefile/ShapefileWriterOptions.cs new file mode 100644 index 0000000..a525683 --- /dev/null +++ b/src/NetTopologySuite.IO.Esri.Shapefile/ShapefileWriterOptions.cs @@ -0,0 +1,159 @@ +using NetTopologySuite.IO.Esri.Dbf.Fields; +using NetTopologySuite.IO.Esri.Shapefiles.Readers; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NetTopologySuite.IO.Esri +{ + /// + /// Shapefile writer options + /// + public class ShapefileWriterOptions + { + /// + /// Shape type. + /// + public ShapeType ShapeType { get; } + + /// + /// Shapefile fields definitions. + /// + public List Fields { get; } = new List(); + + private Encoding _encoding = Encoding.UTF8; + /// + /// DBF file encoding. + /// + public Encoding Encoding + { + get => _encoding; + set => _encoding = value ?? Encoding.UTF8; + } + + /// + /// Projection metadata for the shapefile (content of the PRJ file). + /// + public string Projection { get; set; } = null; + + /// + /// Creates new instance of ShapefileWriterOptions class. + /// + /// Shape type. + /// Shapefile fields definitions. + public ShapefileWriterOptions(ShapeType type, params DbfField[] fields) + { + ShapeType = type; + if (fields != null) + { + Fields.AddRange(fields); + } + } + + /// + /// Creates new instance of ShapefileWriterOptions class based on existing ShapefileReader. + /// + /// Shape reader. + public ShapefileWriterOptions(ShapefileReader reader) + { + reader = reader ?? throw new ArgumentNullException(nameof(reader)); + ShapeType = reader.ShapeType; + Fields.AddRange(reader.Fields); + Encoding = reader.Encoding; + Projection = reader.Projection; + } + + /// + /// Adds a DBF field (feature attribute). + /// + /// Field name. + /// Field type. + /// + public DbfField AddField(string name, Type type) + { + return Fields.AddField(name, type); + } + + /// + /// Adds a DBF field. + /// + /// Field name. + /// + public DbfField AddField(string name) + where T : struct, IComparable, IConvertible, IFormattable + { + return Fields.AddField(name); + } + + /// + /// Adds a character string field (feature attribute). + /// + /// Field name. + /// Field lenght. + /// + public DbfCharacterField AddCharacterField(string name, int length = 254) + { + return Fields.AddCharacterField(name, length); + } + + /// + /// Adds a date field (feature attribute). + /// + /// Field name. + /// + public DbfDateField AddDateField(string name) + { + return Fields.AddDateField(name); + } + + /// + /// Adds a float field (feature attribute). + /// + /// Field name. + /// + public DbfFloatField AddFloatField(string name) + { + return Fields.AddFloatField(name); + } + + /// + /// Adds a logical field (feature attribute). + /// + /// Field name. + /// + public DbfLogicalField AddLogicalField(string name) + { + return Fields.AddLogicalField(name); + } + + /// + /// Adds an Int32 field (feature attribute). + /// + /// Field name. + /// + public DbfNumericInt32Field AddNumericInt32Field(string name) + { + return Fields.AddNumericInt32Field(name); + } + + /// + /// Adds an Int64 field (feature attribute). + /// + /// Field name. + /// + public DbfNumericInt64Field AddNumericInt64Field(string name) + { + return Fields.AddNumericInt64Field(name); + } + + /// + /// Adds a Double field (feature attribute). + /// + /// Field name. + /// + public DbfNumericDoubleField AddNumericDoubleField(string name) + { + return Fields.AddNumericDoubleField(name); + } + } +} diff --git a/src/NetTopologySuite.IO.Esri.Shapefile/Shapefiles/Readers/ShapefileMultiPointReader.cs b/src/NetTopologySuite.IO.Esri.Shapefile/Shapefiles/Readers/ShapefileMultiPointReader.cs index c39db85..b826ce0 100644 --- a/src/NetTopologySuite.IO.Esri.Shapefile/Shapefiles/Readers/ShapefileMultiPointReader.cs +++ b/src/NetTopologySuite.IO.Esri.Shapefile/Shapefiles/Readers/ShapefileMultiPointReader.cs @@ -13,20 +13,20 @@ public class ShapefileMultiPointReader : ShapefileReader { /// - public ShapefileMultiPointReader(Stream shpStream, Stream dbfStream, GeometryFactory factory, Encoding encoding = null) - : base(shpStream, dbfStream, factory, encoding) + public ShapefileMultiPointReader(Stream shpStream, Stream dbfStream, ShapefileReaderOptions options = null) + : base(shpStream, dbfStream, options) { } /// - public ShapefileMultiPointReader(string shpPath, GeometryFactory factory, Encoding encoding = null) - : base(shpPath, factory, encoding) + public ShapefileMultiPointReader(string shpPath, ShapefileReaderOptions options = null) + : base(shpPath, options) { } - internal override ShpReader CreateShpReader(Stream shpStream, GeometryFactory factory) + internal override ShpReader CreateShpReader(Stream shpStream, ShapefileReaderOptions options) { - return new ShpMultiPointReader(shpStream, factory); + return new ShpMultiPointReader(shpStream, options); } } diff --git a/src/NetTopologySuite.IO.Esri.Shapefile/Shapefiles/Readers/ShapefilePointReader.cs b/src/NetTopologySuite.IO.Esri.Shapefile/Shapefiles/Readers/ShapefilePointReader.cs index def818f..58a5496 100644 --- a/src/NetTopologySuite.IO.Esri.Shapefile/Shapefiles/Readers/ShapefilePointReader.cs +++ b/src/NetTopologySuite.IO.Esri.Shapefile/Shapefiles/Readers/ShapefilePointReader.cs @@ -13,19 +13,19 @@ namespace NetTopologySuite.IO.Esri.Shapefiles.Readers public class ShapefilePointReader : ShapefileReader { /// - public ShapefilePointReader(Stream shpStream, Stream dbfStream, GeometryFactory factory, Encoding encoding = null) - : base(shpStream, dbfStream, factory, encoding) + public ShapefilePointReader(Stream shpStream, Stream dbfStream, ShapefileReaderOptions options = null) + : base(shpStream, dbfStream, options) { } /// - public ShapefilePointReader(string shpPath, GeometryFactory factory = null, Encoding encoding = null) - : base(shpPath, factory, encoding) + public ShapefilePointReader(string shpPath, ShapefileReaderOptions options = null) + : base(shpPath, options) { } - internal override ShpReader CreateShpReader(Stream shpStream, GeometryFactory factory) + internal override ShpReader CreateShpReader(Stream shpStream, ShapefileReaderOptions options) { - return new ShpPointReader(shpStream, factory); + return new ShpPointReader(shpStream, options); } } diff --git a/src/NetTopologySuite.IO.Esri.Shapefile/Shapefiles/Readers/ShapefilePolyLineReadercs.cs b/src/NetTopologySuite.IO.Esri.Shapefile/Shapefiles/Readers/ShapefilePolyLineReadercs.cs index f5845b6..9e47157 100644 --- a/src/NetTopologySuite.IO.Esri.Shapefile/Shapefiles/Readers/ShapefilePolyLineReadercs.cs +++ b/src/NetTopologySuite.IO.Esri.Shapefile/Shapefiles/Readers/ShapefilePolyLineReadercs.cs @@ -12,18 +12,18 @@ namespace NetTopologySuite.IO.Esri.Shapefiles.Readers public class ShapefilePolyLineReader : ShapefileReader { /// - public ShapefilePolyLineReader(Stream shpStream, Stream dbfStream, GeometryFactory factory, Encoding encoding = null) - : base(shpStream, dbfStream, factory, encoding) + public ShapefilePolyLineReader(Stream shpStream, Stream dbfStream, ShapefileReaderOptions options = null) + : base(shpStream, dbfStream, options) { } /// - public ShapefilePolyLineReader(string shpPath, GeometryFactory factory, Encoding encoding = null) - : base(shpPath, factory, encoding) + public ShapefilePolyLineReader(string shpPath, ShapefileReaderOptions options = null) + : base(shpPath, options) { } - internal override ShpReader CreateShpReader(Stream shpStream, GeometryFactory factory) + internal override ShpReader CreateShpReader(Stream shpStream, ShapefileReaderOptions options) { - return new ShpPolyLineReader(shpStream, factory); + return new ShpPolyLineReader(shpStream, options); } } diff --git a/src/NetTopologySuite.IO.Esri.Shapefile/Shapefiles/Readers/ShapefilePolygonReader.cs b/src/NetTopologySuite.IO.Esri.Shapefile/Shapefiles/Readers/ShapefilePolygonReader.cs index c54beef..4f95cfa 100644 --- a/src/NetTopologySuite.IO.Esri.Shapefile/Shapefiles/Readers/ShapefilePolygonReader.cs +++ b/src/NetTopologySuite.IO.Esri.Shapefile/Shapefiles/Readers/ShapefilePolygonReader.cs @@ -12,18 +12,18 @@ namespace NetTopologySuite.IO.Esri.Shapefiles.Readers public class ShapefilePolygonReader : ShapefileReader { /// - public ShapefilePolygonReader(Stream shpStream, Stream dbfStream, GeometryFactory factory, Encoding encoding = null) - : base(shpStream, dbfStream, factory, encoding) + public ShapefilePolygonReader(Stream shpStream, Stream dbfStream, ShapefileReaderOptions options = null) + : base(shpStream, dbfStream, options) { } /// - public ShapefilePolygonReader(string shpPath, GeometryFactory factory, Encoding encoding = null) - : base(shpPath, factory, encoding) + public ShapefilePolygonReader(string shpPath, ShapefileReaderOptions options = null) + : base(shpPath, options) { } - internal override ShpReader CreateShpReader(Stream shpStream, GeometryFactory factory) + internal override ShpReader CreateShpReader(Stream shpStream, ShapefileReaderOptions options) { - return new ShpPolygonReader(shpStream, factory); + return new ShpPolygonReader(shpStream, options); } } diff --git a/src/NetTopologySuite.IO.Esri.Shapefile/Shapefiles/Readers/ShapefileReader.T.cs b/src/NetTopologySuite.IO.Esri.Shapefile/Shapefiles/Readers/ShapefileReader.T.cs index b23fb16..080e090 100644 --- a/src/NetTopologySuite.IO.Esri.Shapefile/Shapefiles/Readers/ShapefileReader.T.cs +++ b/src/NetTopologySuite.IO.Esri.Shapefile/Shapefiles/Readers/ShapefileReader.T.cs @@ -3,6 +3,7 @@ using NetTopologySuite.IO.Esri.Dbf; using NetTopologySuite.IO.Esri.Dbf.Fields; using NetTopologySuite.IO.Esri.Shp.Readers; +using System; using System.IO; using System.Text; @@ -19,49 +20,57 @@ public abstract class ShapefileReader : ShapefileReader where T : Geometry /// public override ShapeType ShapeType => ShpReader.ShapeType; + /// + public override Envelope BoundingBox => ShpReader.BoundingBox; + /// /// Shape geometry. /// - public T Shape => ShpReader.Geometry; + public T Shape => ShpReader.Shape; /// - public override Geometry Geometry => ShpReader.Geometry; - - + public override Geometry Geometry => ShpReader.Shape; /// public override string Projection { get; } = null; - - /// /// Initializes a new instance of the reader class. /// /// SHP file stream. /// DBF file stream. - /// Geometry factory. - /// DBF file encoding. If null encoding will be guess from related .CPG file or from reserved DBF bytes. - public ShapefileReader(Stream shpStream, Stream dbfStream, GeometryFactory factory, Encoding encoding) - : base(new DbfReader(dbfStream, encoding)) + /// Reader options. + public ShapefileReader(Stream shpStream, Stream dbfStream, ShapefileReaderOptions options) + : base(new DbfReader(dbfStream, options?.Encoding)) { - ShpReader = CreateShpReader(shpStream, factory); - + try + { + options = options ?? ShapefileReaderOptions.Default; + options.DbfRecordCount = DbfReader.RecordCount; + ShpReader = CreateShpReader(shpStream, options); + } + catch + { + DisposeManagedResources(); + throw; + } } /// /// Initializes a new instance of the reader class. /// /// Path to SHP file. - /// Geometry factory. - /// DBF file encoding. If null encoding will be guess from related .CPG file or from reserved DBF bytes. - public ShapefileReader(string shpPath, GeometryFactory factory, Encoding encoding = null) - : base(new DbfReader(Path.ChangeExtension(shpPath, ".dbf"), encoding)) + /// Reader options. + public ShapefileReader(string shpPath, ShapefileReaderOptions options) + : base(new DbfReader(Path.ChangeExtension(shpPath, ".dbf"), options?.Encoding)) { try { + options = options ?? ShapefileReaderOptions.Default; + options.DbfRecordCount = DbfReader.RecordCount; var shpStream = OpenManagedFileStream(shpPath, ".shp", FileMode.Open); - ShpReader = CreateShpReader(shpStream, factory); + ShpReader = CreateShpReader(shpStream, options); var prjFile = Path.ChangeExtension(shpPath, ".prj"); if (File.Exists(prjFile)) @@ -86,14 +95,19 @@ public ShapefileReader(string shpPath, GeometryFactory factory, Encoding encodin /// public override bool Read(out bool deleted) { - var readShpSucceed = ShpReader.Read(); + var readShpSucceed = ShpReader.Read(out var skippedCount); + for (int i = 0; i < skippedCount; i++) + { + if (!DbfReader.Read(out _)) + { + ThrowCorruptedShapefileDataException(); + } + } var readDbfSucceed = DbfReader.Read(out deleted); if (readDbfSucceed != readShpSucceed) { - throw new FileLoadException("Corrupted shapefile data. " - + "The dBASE table must contain feature attributes with one record per feature. " - + "There must be one-to-one relationship between geometry and attributes."); + ThrowCorruptedShapefileDataException(); } return readDbfSucceed; } @@ -102,9 +116,16 @@ public override bool Read(out bool deleted) public override bool Read(out bool deleted, out Feature feature) { var readSucceed = Read(out deleted); - var attributes = new AttributesTable(Fields.GetValues()); + if (!readSucceed) + { + feature = null; + return false; + } + + var attributes = new AttributesTable(Fields.ToDictionary()); feature = new Feature(Shape, attributes); - return readSucceed; + feature.BoundingBox = Shape.EnvelopeInternal; + return true; } /// @@ -123,7 +144,14 @@ protected override void DisposeManagedResources() base.DisposeManagedResources(); // This will dispose streams used by ShpReader and DbfReader. Do it at the end. } - internal abstract ShpReader CreateShpReader(Stream shpStream, GeometryFactory factory); + internal abstract ShpReader CreateShpReader(Stream shpStream, ShapefileReaderOptions options); + + private static void ThrowCorruptedShapefileDataException() + { + throw new ShapefileException("Corrupted shapefile data. " + + "The dBASE table must contain feature attributes with one record per feature. " + + "There must be one-to-one relationship between geometry and attributes."); + } } diff --git a/src/NetTopologySuite.IO.Esri.Shapefile/Shapefiles/Readers/ShapefileReader.cs b/src/NetTopologySuite.IO.Esri.Shapefile/Shapefiles/Readers/ShapefileReader.cs index 7e3b1d6..1d293af 100644 --- a/src/NetTopologySuite.IO.Esri.Shapefile/Shapefiles/Readers/ShapefileReader.cs +++ b/src/NetTopologySuite.IO.Esri.Shapefile/Shapefiles/Readers/ShapefileReader.cs @@ -32,6 +32,12 @@ public abstract class ShapefileReader : Shapefile, IEnumerable /// /> public abstract string Projection { get; } + /// + /// The Bounding Box stored in the SHP file header representing the actual extent of the shapes in the file. + /// If the shapefile is empty (that is, has no records), the Bounding Box values are unspecified. + /// + public abstract Envelope BoundingBox { get; } + /// /// Shape geometry. /// @@ -85,7 +91,22 @@ public ShapefileReader(DbfReader dbfReader) /// public abstract bool Read(out bool deleted); - + /// + /// Reads feature geometry and attributes from underlying SHP and DBF files into and properties. + /// + /// + /// true if the enumerator was successfully advanced to the next record; + /// false if the enumerator has passed the end of the table. + /// + public bool Read() + { + var read = Read(out var deleted); + if (read && deleted) + { + return Read(); + } + return read; + } #region *** Enumerator *** diff --git a/src/NetTopologySuite.IO.Esri.Shapefile/Shapefiles/Writers/ShapefileMultiPointWriter.cs b/src/NetTopologySuite.IO.Esri.Shapefile/Shapefiles/Writers/ShapefileMultiPointWriter.cs index c61a764..7928085 100644 --- a/src/NetTopologySuite.IO.Esri.Shapefile/Shapefiles/Writers/ShapefileMultiPointWriter.cs +++ b/src/NetTopologySuite.IO.Esri.Shapefile/Shapefiles/Writers/ShapefileMultiPointWriter.cs @@ -14,21 +14,15 @@ namespace NetTopologySuite.IO.Esri.Shapefiles.Writers public class ShapefileMultiPointWriter : ShapefileWriter { /// - public ShapefileMultiPointWriter(Stream shpStream, Stream shxStream, Stream dbfStream, ShapeType type, IReadOnlyList fields, Encoding encoding = null) - : base(shpStream, shxStream, dbfStream, type, fields, encoding) + public ShapefileMultiPointWriter(Stream shpStream, Stream shxStream, Stream dbfStream, ShapefileWriterOptions options) + : base(shpStream, shxStream, dbfStream, options) { } /// - public ShapefileMultiPointWriter(string shpPath, ShapeType type, IReadOnlyList fields, Encoding encoding = null, string projection = null) - : base(shpPath, type, fields, encoding, projection) + public ShapefileMultiPointWriter(string shpPath, ShapefileWriterOptions options) + : base(shpPath, options) { } - /// - public ShapefileMultiPointWriter(string shpPath, ShapeType type, params DbfField[] fields) - : base(shpPath, type, fields) - { - } - internal override ShpWriter CreateShpWriter(Stream shpStream, Stream shxStream) { diff --git a/src/NetTopologySuite.IO.Esri.Shapefile/Shapefiles/Writers/ShapefilePointWriter.cs b/src/NetTopologySuite.IO.Esri.Shapefile/Shapefiles/Writers/ShapefilePointWriter.cs index ea88885..eaa851d 100644 --- a/src/NetTopologySuite.IO.Esri.Shapefile/Shapefiles/Writers/ShapefilePointWriter.cs +++ b/src/NetTopologySuite.IO.Esri.Shapefile/Shapefiles/Writers/ShapefilePointWriter.cs @@ -14,21 +14,15 @@ namespace NetTopologySuite.IO.Esri.Shapefiles.Writers public class ShapefilePointWriter : ShapefileWriter { /// - public ShapefilePointWriter(Stream shpStream, Stream shxStream, Stream dbfStream, ShapeType type, IReadOnlyList fields, Encoding encoding = null) - : base(shpStream, shxStream, dbfStream, type, fields, encoding) + public ShapefilePointWriter(Stream shpStream, Stream shxStream, Stream dbfStream, ShapefileWriterOptions options) + : base(shpStream, shxStream, dbfStream, options) { } /// - public ShapefilePointWriter(string shpPath, ShapeType type, IReadOnlyList fields, Encoding encoding = null, string projection = null) - : base(shpPath, type, fields, encoding, projection) + public ShapefilePointWriter(string shpPath, ShapefileWriterOptions options) + : base(shpPath, options) { } - /// - public ShapefilePointWriter(string shpPath, ShapeType type, params DbfField[] fields) - : base(shpPath, type, fields) - { - } - internal override ShpWriter CreateShpWriter(Stream shpStream, Stream shxStream) { return new ShpPointWriter(shpStream, shxStream, ShapeType); diff --git a/src/NetTopologySuite.IO.Esri.Shapefile/Shapefiles/Writers/ShapefilePolyLineWriter.cs b/src/NetTopologySuite.IO.Esri.Shapefile/Shapefiles/Writers/ShapefilePolyLineWriter.cs index a5e40d5..7466306 100644 --- a/src/NetTopologySuite.IO.Esri.Shapefile/Shapefiles/Writers/ShapefilePolyLineWriter.cs +++ b/src/NetTopologySuite.IO.Esri.Shapefile/Shapefiles/Writers/ShapefilePolyLineWriter.cs @@ -14,21 +14,15 @@ namespace NetTopologySuite.IO.Esri.Shapefiles.Writers public class ShapefilePolyLineWriter : ShapefileWriter { /// - public ShapefilePolyLineWriter(Stream shpStream, Stream shxStream, Stream dbfStream, ShapeType type, IReadOnlyList fields, Encoding encoding = null) - : base(shpStream, shxStream, dbfStream, type, fields, encoding) + public ShapefilePolyLineWriter(Stream shpStream, Stream shxStream, Stream dbfStream, ShapefileWriterOptions options) + : base(shpStream, shxStream, dbfStream, options) { } /// - public ShapefilePolyLineWriter(string shpPath, ShapeType type, IReadOnlyList fields, Encoding encoding = null, string projection = null) - : base(shpPath, type, fields, encoding, projection) + public ShapefilePolyLineWriter(string shpPath, ShapefileWriterOptions options) + : base(shpPath, options) { } - /// - public ShapefilePolyLineWriter(string shpPath, ShapeType type, params DbfField[] fields) - : base(shpPath, type, fields) - { - } - internal override ShpWriter CreateShpWriter(Stream shpStream, Stream shxStream) { return new ShpPolyLineWriter(shpStream, shxStream, ShapeType); diff --git a/src/NetTopologySuite.IO.Esri.Shapefile/Shapefiles/Writers/ShapefilePolygonWriter.cs b/src/NetTopologySuite.IO.Esri.Shapefile/Shapefiles/Writers/ShapefilePolygonWriter.cs index d8b096f..3d2ffa8 100644 --- a/src/NetTopologySuite.IO.Esri.Shapefile/Shapefiles/Writers/ShapefilePolygonWriter.cs +++ b/src/NetTopologySuite.IO.Esri.Shapefile/Shapefiles/Writers/ShapefilePolygonWriter.cs @@ -14,21 +14,15 @@ namespace NetTopologySuite.IO.Esri.Shapefiles.Writers public class ShapefilePolygonWriter : ShapefileWriter { /// - public ShapefilePolygonWriter(Stream shpStream, Stream shxStream, Stream dbfStream, ShapeType type, IReadOnlyList fields, Encoding encoding = null) - : base(shpStream, shxStream, dbfStream, type, fields, encoding) + public ShapefilePolygonWriter(Stream shpStream, Stream shxStream, Stream dbfStream, ShapefileWriterOptions options) + : base(shpStream, shxStream, dbfStream, options) { } /// - public ShapefilePolygonWriter(string shpPath, ShapeType type, IReadOnlyList fields, Encoding encoding = null, string projection = null) - : base(shpPath, type, fields, encoding, projection) + public ShapefilePolygonWriter(string shpPath, ShapefileWriterOptions options) + : base(shpPath, options) { } - /// - public ShapefilePolygonWriter(string shpPath, ShapeType type, params DbfField[] fields) - : base(shpPath, type, fields) - { - } - internal override ShpWriter CreateShpWriter(Stream shpStream, Stream shxStream) { return new ShpPolygonWriter(shpStream, shxStream, ShapeType); diff --git a/src/NetTopologySuite.IO.Esri.Shapefile/Shapefiles/Writers/ShapefileWriter.T.cs b/src/NetTopologySuite.IO.Esri.Shapefile/Shapefiles/Writers/ShapefileWriter.T.cs index ec92807..6a2ed44 100644 --- a/src/NetTopologySuite.IO.Esri.Shapefile/Shapefiles/Writers/ShapefileWriter.T.cs +++ b/src/NetTopologySuite.IO.Esri.Shapefile/Shapefiles/Writers/ShapefileWriter.T.cs @@ -3,6 +3,7 @@ using NetTopologySuite.IO.Esri.Dbf; using NetTopologySuite.IO.Esri.Dbf.Fields; using NetTopologySuite.IO.Esri.Shp.Writers; +using System; using System.Collections.Generic; using System.IO; using System.Text; @@ -44,14 +45,21 @@ public override Geometry Geometry /// SHP file stream. /// SHX file stream. /// DBF file stream. - /// Shape type. - /// Shapefile fields definitions. - /// DBF file encoding. If null encoding will be guess from related .CPG file or from reserved DBF bytes. - internal ShapefileWriter(Stream shpStream, Stream shxStream, Stream dbfStream, ShapeType type, IReadOnlyList fields, Encoding encoding) - : base(new DbfWriter(dbfStream, fields, encoding)) + /// Writer options. + internal ShapefileWriter(Stream shpStream, Stream shxStream, Stream dbfStream, ShapefileWriterOptions options) + : base(new DbfWriter(dbfStream, options?.Fields, options?.Encoding)) { - ShapeType = type; - ShpWriter = CreateShpWriter(shpStream, shxStream); + try + { + options = options ?? throw new ArgumentNullException(nameof(options)); + ShapeType = options.ShapeType; + ShpWriter = CreateShpWriter(shpStream, shxStream); + } + catch + { + DisposeManagedResources(); + throw; + } } @@ -59,23 +67,21 @@ internal ShapefileWriter(Stream shpStream, Stream shxStream, Stream dbfStream, S /// Initializes a new instance of the writer class. /// /// Path to SHP file. - /// Shape type. - /// Shapefile attribute definitions. - /// DBF file encoding. If null encoding will be guess from related .CPG file or from reserved DBF bytes. - /// Projection metadata for the shapefile (.prj file). - internal ShapefileWriter(string shpPath, ShapeType type, IReadOnlyList fields, Encoding encoding, string projection) - : base(new DbfWriter(Path.ChangeExtension(shpPath, ".dbf"), fields, encoding)) + /// Writer options. + internal ShapefileWriter(string shpPath, ShapefileWriterOptions options) + : base(new DbfWriter(Path.ChangeExtension(shpPath, ".dbf"), options?.Fields, options?.Encoding)) { try { + options = options ?? throw new ArgumentNullException(nameof(options)); var shpStream = OpenManagedFileStream(shpPath, ".shp", FileMode.Create); var shxStream = OpenManagedFileStream(shpPath, ".shx", FileMode.Create); - ShapeType = type; + ShapeType = options.ShapeType; ShpWriter = CreateShpWriter(shpStream, shxStream); // It calls this.ShapeType - if (!string.IsNullOrWhiteSpace(projection)) - File.WriteAllText(Path.ChangeExtension(shpPath, ".prj"), projection); + if (!string.IsNullOrWhiteSpace(options.Projection)) + File.WriteAllText(Path.ChangeExtension(shpPath, ".prj"), options.Projection); } catch { @@ -85,17 +91,6 @@ internal ShapefileWriter(string shpPath, ShapeType type, IReadOnlyList } - - /// - /// Initializes a new instance of the writer class. - /// - /// Path to SHP file. - /// Shape type. - /// Shapefile attribute definitions. - internal ShapefileWriter(string shpPath, ShapeType type, params DbfField[] fields) : this(shpPath, type, fields, null, null) - { - } - internal abstract ShpWriter CreateShpWriter(Stream shpStream, Stream shxStream); @@ -103,7 +98,7 @@ internal ShapefileWriter(string shpPath, ShapeType type, params DbfField[] field /// Wrties geometry and attributes into underlying SHP and DBF files. /// Attribute values must be set using Value property of DbfFiled(s) provided during initialization. /// - public void Write() + public override void Write() { ShpWriter.Write(); DbfWriter.Write(); @@ -114,7 +109,10 @@ public override void Write(IFeature feature) { foreach (var field in DbfWriter.Fields) { - field.Value = feature.Attributes[field.Name]; + if (feature.Attributes?.Exists(field.Name) == true) + { + field.Value = feature.Attributes[field.Name]; + } } Geometry = (T)feature.Geometry; Write(); diff --git a/src/NetTopologySuite.IO.Esri.Shapefile/Shapefiles/Writers/ShapefileWriter.cs b/src/NetTopologySuite.IO.Esri.Shapefile/Shapefiles/Writers/ShapefileWriter.cs index 041ba01..0b54f1d 100644 --- a/src/NetTopologySuite.IO.Esri.Shapefile/Shapefiles/Writers/ShapefileWriter.cs +++ b/src/NetTopologySuite.IO.Esri.Shapefile/Shapefiles/Writers/ShapefileWriter.cs @@ -13,7 +13,6 @@ namespace NetTopologySuite.IO.Esri.Shapefiles.Writers /// public abstract class ShapefileWriter : Shapefile { - internal readonly DbfWriter DbfWriter; /// @@ -35,6 +34,13 @@ public ShapefileWriter(DbfWriter dbfWriter) } + /// + /// Wrties geometry and attributes into underlying SHP and DBF files. + /// Attribute values must be set using Value property of DbfFiled(s) provided during initialization. + /// + public abstract void Write(); + + /// /// Writes feature to the shapefile. /// diff --git a/src/NetTopologySuite.IO.Esri.Shapefile/Shp/Readers/ShpMultiPointReader.cs b/src/NetTopologySuite.IO.Esri.Shapefile/Shp/Readers/ShpMultiPointReader.cs index d1110e7..98cfb89 100644 --- a/src/NetTopologySuite.IO.Esri.Shapefile/Shp/Readers/ShpMultiPointReader.cs +++ b/src/NetTopologySuite.IO.Esri.Shapefile/Shp/Readers/ShpMultiPointReader.cs @@ -9,20 +9,28 @@ namespace NetTopologySuite.IO.Esri.Shp.Readers /// public class ShpMultiPointReader : ShpReader { + /// - public ShpMultiPointReader(Stream shpStream, GeometryFactory factory) : base(shpStream, factory) + public ShpMultiPointReader(Stream shpStream, ShapefileReaderOptions options = null) + : base(shpStream, options) { if (!ShapeType.IsMultiPoint()) ThrowUnsupportedShapeTypeException(); } + internal override MultiPoint GetEmptyGeometry() { return MultiPoint.Empty; } - internal override MultiPoint ReadGeometry(Stream stream) + internal override bool ReadGeometry(Stream stream, out MultiPoint geometry) { - stream.ReadXYBoundingBox(); + var bbox = stream.ReadXYBoundingBox(); + if (!IsInMbr(bbox)) + { + geometry = null; + return false; + } var pointCount = stream.ReadPointCount(); var coordinateSequence = CreateCoordinateSequence(pointCount); @@ -40,7 +48,12 @@ internal override MultiPoint ReadGeometry(Stream stream) stream.ReadMValues(coordinateSequence); } - return Factory.CreateMultiPoint(coordinateSequence); + geometry = Factory.CreateMultiPoint(coordinateSequence); + if (!IsInMbr(geometry)) + { + return false; + } + return true; } } diff --git a/src/NetTopologySuite.IO.Esri.Shapefile/Shp/Readers/ShpPointReader.cs b/src/NetTopologySuite.IO.Esri.Shapefile/Shp/Readers/ShpPointReader.cs index 76788cf..4a85031 100644 --- a/src/NetTopologySuite.IO.Esri.Shapefile/Shp/Readers/ShpPointReader.cs +++ b/src/NetTopologySuite.IO.Esri.Shapefile/Shp/Readers/ShpPointReader.cs @@ -10,7 +10,8 @@ namespace NetTopologySuite.IO.Esri.Shp.Readers public class ShpPointReader : ShpReader { /// - public ShpPointReader(Stream shpStream, GeometryFactory factory) : base(shpStream, factory) + public ShpPointReader(Stream shpStream, ShapefileReaderOptions options = null) + : base(shpStream, options) { if (!ShapeType.IsPoint()) ThrowUnsupportedShapeTypeException(); @@ -21,11 +22,17 @@ internal override Point GetEmptyGeometry() return Point.Empty; } - internal override Point ReadGeometry(Stream shapeBinary) + internal override bool ReadGeometry(Stream shapeBinary, out Point geometry) { var coordinateSequence = CreateCoordinateSequence(1); shapeBinary.ReadPoint(coordinateSequence); - return Factory.CreatePoint(coordinateSequence); + + geometry = Factory.CreatePoint(coordinateSequence); + if (!IsInMbr(geometry)) + { + return false; + } + return true; } } diff --git a/src/NetTopologySuite.IO.Esri.Shapefile/Shp/Readers/ShpPolyLineReader.cs b/src/NetTopologySuite.IO.Esri.Shapefile/Shp/Readers/ShpPolyLineReader.cs index 1167e43..6b57ae7 100644 --- a/src/NetTopologySuite.IO.Esri.Shapefile/Shp/Readers/ShpPolyLineReader.cs +++ b/src/NetTopologySuite.IO.Esri.Shapefile/Shp/Readers/ShpPolyLineReader.cs @@ -5,7 +5,9 @@ namespace NetTopologySuite.IO.Esri.Shp.Readers { internal class ShpPolyLineReader : ShpReader { - public ShpPolyLineReader(Stream shpStream, GeometryFactory factory) : base(shpStream, factory) + /// + public ShpPolyLineReader(Stream shpStream, ShapefileReaderOptions options = null) + : base(shpStream, options) { if (!ShapeType.IsPolyLine()) ThrowUnsupportedShapeTypeException(); @@ -16,18 +18,31 @@ internal override MultiLineString GetEmptyGeometry() return MultiLineString.Empty; } - internal override MultiLineString ReadGeometry(Stream shapeBinary) + internal override bool ReadGeometry(Stream stream, out MultiLineString geometry) { + var bbox = stream.ReadXYBoundingBox(); + if (!IsInMbr(bbox)) + { + geometry = null; + return false; + } + // SHP Docs: A part is a connected sequence of two or more points. (page 7) var partsBuilder = new ShpMultiPartBuilder(1, 2); - partsBuilder.ReadParts(shapeBinary, HasZ, HasM, CreateCoordinateSequence); + partsBuilder.ReadParts(stream, HasZ, HasM, CreateCoordinateSequence); var lines = new LineString[partsBuilder.Count]; for (int i = 0; i < lines.Length; i++) { lines[i] = Factory.CreateLineString(partsBuilder[i]); } - return Factory.CreateMultiLineString(lines); + + geometry = Factory.CreateMultiLineString(lines); + if (!IsInMbr(geometry)) + { + return false; + } + return true; } } } diff --git a/src/NetTopologySuite.IO.Esri.Shapefile/Shp/Readers/ShpPolygonReader.cs b/src/NetTopologySuite.IO.Esri.Shapefile/Shp/Readers/ShpPolygonReader.cs index b27d50e..fc29bfe 100644 --- a/src/NetTopologySuite.IO.Esri.Shapefile/Shp/Readers/ShpPolygonReader.cs +++ b/src/NetTopologySuite.IO.Esri.Shapefile/Shp/Readers/ShpPolygonReader.cs @@ -1,13 +1,16 @@ using NetTopologySuite.Algorithm; using NetTopologySuite.Geometries; using System.Collections.Generic; +using System.Diagnostics; using System.IO; namespace NetTopologySuite.IO.Esri.Shp.Readers { internal class ShpPolygonReader : ShpReader { - public ShpPolygonReader(Stream shpStream, GeometryFactory factory) : base(shpStream, factory) + /// + public ShpPolygonReader(Stream shpStream, ShapefileReaderOptions options = null) + : base(shpStream, options) { if (!ShapeType.IsPolygon()) ThrowUnsupportedShapeTypeException(); @@ -18,11 +21,18 @@ internal override MultiPolygon GetEmptyGeometry() return MultiPolygon.Empty; } - internal override MultiPolygon ReadGeometry(Stream shapeBinary) + internal override bool ReadGeometry(Stream stream, out MultiPolygon geometry) { + var bbox = stream.ReadXYBoundingBox(); + if (!IsInMbr(bbox)) + { + geometry = null; + return false; + } + // SHP Docs: A ring is a connected sequence of four or more points (page 8) var partsBuilder = new ShpMultiPartBuilder(1, 4); - partsBuilder.ReadParts(shapeBinary, HasZ, HasM, CreateCoordinateSequence); + partsBuilder.ReadParts(stream, HasZ, HasM, CreateCoordinateSequence); var polygons = new List(); var holes = new List(); @@ -30,8 +40,14 @@ internal override MultiPolygon ReadGeometry(Stream shapeBinary) var shell = Factory.CreateLinearRing(partsBuilder[0]); if (shell.IsCCW) { - ThrowInvalidRecordException("Invalid Shapefile polygon - shell coordinates are not in in clockwise order."); + if (partsBuilder.Count > 1) + { + ThrowInvalidRecordException("Invalid Shapefile polygon - shell coordinates are not in in clockwise order."); + } + Debug.Assert(!shell.IsCCW, "Invalid Shapefile polygon - shell coordinates are not in in clockwise order."); + shell = Factory.CreateLinearRing(partsBuilder[0].Reversed()); } + for (int partIndex = 1; partIndex < partsBuilder.Count; partIndex++) { // SHP Docs: Vertices of rings defining holes in polygons are in a counterclockwise direction. @@ -53,7 +69,13 @@ internal override MultiPolygon ReadGeometry(Stream shapeBinary) } } polygons.AddRange(CreatePolygons(shell, holes)); - return Factory.CreateMultiPolygon(polygons.ToArray()); + + geometry = Factory.CreateMultiPolygon(polygons.ToArray()); + if (!IsInMbr(geometry)) + { + return false; + } + return true; } private IEnumerable CreatePolygons(LinearRing shell, List innerRings) diff --git a/src/NetTopologySuite.IO.Esri.Shapefile/Shp/Readers/ShpReader.T.cs b/src/NetTopologySuite.IO.Esri.Shapefile/Shp/Readers/ShpReader.T.cs new file mode 100644 index 0000000..02d4344 --- /dev/null +++ b/src/NetTopologySuite.IO.Esri.Shapefile/Shp/Readers/ShpReader.T.cs @@ -0,0 +1,210 @@ +using NetTopologySuite.Geometries; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; + +namespace NetTopologySuite.IO.Esri.Shp.Readers +{ + + /// + /// Base class class for reading a fixed-length file header and variable-length records from a *.SHP file. + /// + public abstract class ShpReader : ShpReader where T : Geometry + { + private readonly Stream ShpStream; + private readonly int ShpEndPosition; + private readonly MemoryStream Buffer; + private readonly bool SkipFailures; + private readonly Envelope MbrEnvelope = null; + private readonly MbrFilterOption MbrFilterOption; + private readonly Geometry MbrGeometry = null; + + /// + /// Shapefile Spec:
+ /// The one-to-one relationship between geometry and attributes is based on record number. + /// Attribute records in the dBASE file must be in the same order as records in the main file. + ///
+ /// + /// DBF does not have recor number attribute. + /// + private int RecordNumber = 1; + private readonly int DbfRecordCount; + + internal GeometryFactory Factory { get; } + + private readonly Envelope _boundingBox; + + /// + public override Envelope BoundingBox => _boundingBox.Copy(); // Envelope is not immutable + + + private readonly List _errors = new List(); + /// + /// Errors which occured during reading process. Valid only if ShapefileReaderOptions.SkipFailures option is set to true. + /// + public IReadOnlyList Errors => _errors; + + /// + /// SHP geometry. + /// + public T Shape { get; private set; } + + /// + public override Geometry Geometry => Shape; + + /// + /// Initializes a new instance of the reader class. + /// + /// SHP file stream. + /// Reader options. + internal ShpReader(Stream shpStream, ShapefileReaderOptions options) + : base(Shapefile.GetShapeType(shpStream)) + { + ShpStream = shpStream ?? throw new ArgumentNullException("Uninitialized SHP stream.", nameof(shpStream)); + Factory = options?.Factory ?? Geometry.DefaultFactory; + + if (options?.MbrFilter?.IsNull == false) + { + MbrEnvelope = options.MbrFilter.Copy(); // Envelope is not immutable + } + if (MbrEnvelope != null) + { + MbrGeometry = Factory.ToGeometry(MbrEnvelope); + } + MbrFilterOption = options?.MbrFilterOption ?? MbrFilterOption.FilterByExtent; + + SkipFailures = options?.SkipFailures ?? false; + + DbfRecordCount = options?.DbfRecordCount ?? int.MaxValue; + if (DbfRecordCount < 0) + { + throw new ArgumentOutOfRangeException(nameof(DbfRecordCount)); + } + + if (ShpStream.Position != 0) + ShpStream.Seek(0, SeekOrigin.Begin); + + Buffer = new MemoryStream(); + AddManagedResource(Buffer); + + Buffer.AssignFrom(ShpStream, Shapefile.FileHeaderSize); + Buffer.ReadShpFileHeader(out _, out var fileLength, out _boundingBox); + ShpEndPosition = fileLength - 1; + } + + internal override void Restart() + { + ShpStream.Seek(Shapefile.FileHeaderSize, SeekOrigin.Begin); + RecordNumber = 1; + } + + /// + public override bool Read() + { + return Read(out var _); + } + + /// + internal bool Read(out int skippedCount) + { + skippedCount = 0; + try + { + return ReadCore(ref skippedCount); + } + catch (Exception ex) + { + if (SkipFailures) + { + _errors.Add(ex); + Shape = GetEmptyGeometry(); + return true; + } + throw; + } + } + + /// + internal bool ReadCore(ref int skippedCount) + { + if (ShpStream.Position >= ShpEndPosition) + { + Shape = null; + return false; + } + if (RecordNumber > DbfRecordCount) + { + Shape = null; + return false; + } + + (var recordNumber, var contentLength) = ShpStream.ReadShpRecordHeader(); + Debug.Assert(recordNumber == RecordNumber, "Shapefile record", $"Unexpected SHP record number: {recordNumber} (expected {RecordNumber})."); + Debug.Assert(contentLength >= sizeof(int), "Shapefile record", $"Unexpected SHP record content size: {contentLength} (expected >= {sizeof(int)})."); + + Buffer.AssignFrom(ShpStream, contentLength); + RecordNumber++; + + var type = Buffer.ReadShapeType(); + if (type == ShapeType.NullShape) + { + Shape = GetEmptyGeometry(); + return true; + } + else if (type != ShapeType) + { + ThrowInvalidRecordTypeException(type); + } + + if (!ReadGeometry(Buffer, out var geometry)) + { + skippedCount++; + return ReadCore(ref skippedCount); + } + + Shape = geometry; + return true; + } + + internal bool IsInMbr(Envelope boundingBox) + { + if (MbrEnvelope == null) + { + return true; + } + return MbrEnvelope.Intersects(boundingBox); + } + + internal bool IsInMbr(Geometry geometry) + { + if (MbrGeometry == null || MbrFilterOption != MbrFilterOption.FilterByGeometry) + { + return true; + } + return MbrGeometry.Intersects(geometry); + } + + internal abstract T GetEmptyGeometry(); + + internal abstract bool ReadGeometry(Stream stream, out T geometry); + + internal CoordinateSequence CreateCoordinateSequence(int size) + { + return Factory.CoordinateSequenceFactory.Create(size, HasZ, HasM); + } + + internal void ThrowInvalidRecordTypeException(ShapeType shapeType) + { + throw new FileLoadException($"Ivalid shapefile record type. {GetType().Name} does not support {shapeType} shape type."); + } + + internal void ThrowInvalidRecordException(string message) + { + throw new FileLoadException(message); + } + } + + +} diff --git a/src/NetTopologySuite.IO.Esri.Shapefile/Shp/Readers/ShpReader.cs b/src/NetTopologySuite.IO.Esri.Shapefile/Shp/Readers/ShpReader.cs index c2f52ca..4960ea3 100644 --- a/src/NetTopologySuite.IO.Esri.Shapefile/Shp/Readers/ShpReader.cs +++ b/src/NetTopologySuite.IO.Esri.Shapefile/Shp/Readers/ShpReader.cs @@ -1,118 +1,64 @@ using NetTopologySuite.Geometries; using System; -using System.Diagnostics; -using System.IO; +using System.Collections; +using System.Collections.Generic; +using System.Text; namespace NetTopologySuite.IO.Esri.Shp.Readers { - /// /// Base class class for reading a fixed-length file header and variable-length records from a *.SHP file. /// - public abstract class ShpReader : Shp where T : Geometry + public abstract class ShpReader : Shp, IEnumerable { - private readonly Stream ShpStream; - private readonly int ShpEndPosition; - private readonly MemoryStream Buffer; /// - /// Shapefile Spec:
- /// The one-to-one relationship between geometry and attributes is based on record number. - /// Attribute records in the dBASE file must be in the same order as records in the main file. + /// Initializes a new instance of the ShpReader class. ///
- /// - /// DBF does not have recor number attribute. - /// - private int RecordNumber = 1; - - internal GeometryFactory Factory { get; } + /// Shape type. + internal ShpReader(ShapeType shapeType) : base(shapeType) + { + } /// - /// SHP geometry. + /// The Bounding Box stored in the SHP file header representing the actual extent of the shapes in the file. + /// If the shapefile is empty (that is, has no records), the Bounding Box values are unspecified. /// - public T Geometry { get; private set; } - + public abstract Envelope BoundingBox { get; } /// - /// Initializes a new instance of the reader class. + /// Shape geometry. /// - /// SHP file stream. - /// Geometry factory. - public ShpReader(Stream shpStream, GeometryFactory factory) - : base(Shapefile.GetShapeType(shpStream)) - { - ShpStream = shpStream ?? throw new ArgumentNullException("Uninitialized SHP stream.", nameof(shpStream)); - Factory = factory ?? NtsGeometryServices.Instance.CreateGeometryFactory(); - - if (ShpStream.Position != 0) - ShpStream.Seek(0, SeekOrigin.Begin); + public abstract Geometry Geometry { get; } - Buffer = new MemoryStream(); - AddManagedResource(Buffer); - - Buffer.AssignFrom(ShpStream, Shapefile.FileHeaderSize); - Buffer.ReadShpFileHeader(out _, out var fileLength); - ShpEndPosition = fileLength - 1; - } - - internal void Restart() - { - ShpStream.Seek(Shapefile.FileHeaderSize, SeekOrigin.Begin); - } + internal abstract void Restart(); /// /// Reads content of the from the underlying stream. /// /// Value indicating if reading next record was successful. - public bool Read() - { - if (ShpStream.Position >= ShpEndPosition) - { - Geometry = null; - return false; - } + public abstract bool Read(); - (var recordNumber, var contentLength) = ShpStream.ReadShpRecordHeader(); - Debug.Assert(recordNumber == RecordNumber++, "Shapefile record", $"Unexpected SHP record number: {recordNumber} (expected {RecordNumber})."); + #region IEnumerable - Buffer.AssignFrom(ShpStream, contentLength); - - var type = Buffer.ReadShapeType(); - if (type == ShapeType.NullShape) - { - Geometry = GetEmptyGeometry(); - return true; - } - else if (type != ShapeType) + IEnumerator IEnumerable.GetEnumerator() + { + Restart(); + while (Read()) { - ThrowInvalidRecordTypeException(type); + yield return Geometry; } - - Geometry = ReadGeometry(Buffer); - return true; - } - - internal abstract T GetEmptyGeometry(); - - internal abstract T ReadGeometry(Stream shapeBinary); - - internal CoordinateSequence CreateCoordinateSequence(int size) - { - return Factory.CoordinateSequenceFactory.Create(size, HasZ, HasM); } - - internal void ThrowInvalidRecordTypeException(ShapeType shapeType) - { - throw new FileLoadException($"Ivalid shapefile record type. {GetType().Name} does not support {shapeType} shapes."); - } - - internal void ThrowInvalidRecordException(string message) + IEnumerator IEnumerable.GetEnumerator() { - throw new FileLoadException(message); + Restart(); + while (Read()) + { + yield return Geometry; + } } + #endregion } - - } diff --git a/src/NetTopologySuite.IO.Esri.Shapefile/Shp/Shp.cs b/src/NetTopologySuite.IO.Esri.Shapefile/Shp/Shp.cs index 7cce72f..f74afd8 100644 --- a/src/NetTopologySuite.IO.Esri.Shapefile/Shp/Shp.cs +++ b/src/NetTopologySuite.IO.Esri.Shapefile/Shp/Shp.cs @@ -1,4 +1,7 @@ -using System.IO; +using NetTopologySuite.Geometries; +using NetTopologySuite.IO.Esri.Shp.Readers; +using System.IO; +using System.Linq; namespace NetTopologySuite.IO.Esri.Shp { @@ -36,5 +39,41 @@ internal void ThrowUnsupportedShapeTypeException() { throw new FileLoadException(GetType().Name + $" does not support {ShapeType} shapes."); } + + #region Static Methods + + /// + /// Opens SHP reader. + /// + /// SHP file stream. + /// Reader options. + /// SHP reader. + public static ShpReader OpenRead(Stream shpStream, ShapefileReaderOptions options = null) + { + var shapeType = Shapefile.GetShapeType(shpStream); + + if (shapeType.IsPoint()) + { + return new ShpPointReader(shpStream, options); + } + else if (shapeType.IsMultiPoint()) + { + return new ShpMultiPointReader(shpStream, options); + } + else if (shapeType.IsPolyLine()) + { + return new ShpPolyLineReader(shpStream, options); + } + else if (shapeType.IsPolygon()) + { + return new ShpPolygonReader(shpStream, options); + } + else + { + throw new FileLoadException("Unsupported shapefile type: " + shapeType); + } + } + + #endregion } } diff --git a/src/NetTopologySuite.IO.Esri.Shapefile/Shp/ShpMultiPartBuilder.cs b/src/NetTopologySuite.IO.Esri.Shapefile/Shp/ShpMultiPartBuilder.cs index 0ab44e9..674bb81 100644 --- a/src/NetTopologySuite.IO.Esri.Shapefile/Shp/ShpMultiPartBuilder.cs +++ b/src/NetTopologySuite.IO.Esri.Shapefile/Shp/ShpMultiPartBuilder.cs @@ -82,7 +82,6 @@ public void WriteParts(Stream stream, bool hasZ, bool hasM) public virtual void ReadParts(Stream stream, bool hasZ, bool hasM, Func createCoordinateSequence) { Clear(); - stream.ReadXYBoundingBox(); var partCount = stream.ReadPartCount(); PointCount = stream.ReadPointCount(); stream.ReadPartOfsets(partCount, Offsets); diff --git a/src/NetTopologySuite.IO.Esri.Shapefile/Shp/ShpStreamExtensions.cs b/src/NetTopologySuite.IO.Esri.Shapefile/Shp/ShpStreamExtensions.cs index f24234f..cdd69c5 100644 --- a/src/NetTopologySuite.IO.Esri.Shapefile/Shp/ShpStreamExtensions.cs +++ b/src/NetTopologySuite.IO.Esri.Shapefile/Shp/ShpStreamExtensions.cs @@ -40,7 +40,7 @@ public static void WriteShpFileHeader(this Stream stream, ShapeType type, int fi } } - public static void ReadShpFileHeader(this Stream stream, out ShapeType type, out int fileLength) + public static void ReadShpFileHeader(this Stream stream, out ShapeType type, out int fileLength, out Envelope boundingBox) { var fileCode = stream.ReadInt32BigEndian(); if (fileCode != Shapefile.FileCode) @@ -52,7 +52,7 @@ public static void ReadShpFileHeader(this Stream stream, out ShapeType type, out var version = stream.ReadInt32LittleEndian(); type = stream.ReadShapeType(); - stream.ReadXYBoundingBox(); + boundingBox = stream.ReadXYBoundingBox(); stream.ReadZRange(); stream.ReadMRange(); @@ -209,11 +209,11 @@ public static void WriteMValues(this Stream stream, CoordinateSequence pointSequ } } - public static (double minX, double maxX, double minY, double maxY) ReadXYBoundingBox(this Stream stream) + public static Envelope ReadXYBoundingBox(this Stream stream) { var (minX, minY) = stream.ReadXYCoordinates(); var (maxX, maxY) = stream.ReadXYCoordinates(); - return (minX, maxX, minY, maxY); + return new Envelope(minX, maxX, minY, maxY); } public static void WriteXYBoundingBox(this Stream stream, ShpExtent shpExtent) { diff --git a/src/NetTopologySuite.IO.Esri.Shapefile/Shp/Writers/ShpWriter.cs b/src/NetTopologySuite.IO.Esri.Shapefile/Shp/Writers/ShpWriter.cs index c074a4f..52a2ad0 100644 --- a/src/NetTopologySuite.IO.Esri.Shapefile/Shp/Writers/ShpWriter.cs +++ b/src/NetTopologySuite.IO.Esri.Shapefile/Shp/Writers/ShpWriter.cs @@ -73,6 +73,15 @@ public void Write() RecordNumber++; } + /// + /// Writes the geometry to the underlying stream. + /// + public void Write(T geometry) + { + Geometry = geometry; + Write(); + } + internal abstract void WriteGeometry(T geometry, Stream shapeBinary); diff --git a/src/NetTopologySuite.IO.Esri.Shapefile/Shx/ShxStreamExtensions.cs b/src/NetTopologySuite.IO.Esri.Shapefile/Shx/ShxStreamExtensions.cs index 0fe152c..2b50e62 100644 --- a/src/NetTopologySuite.IO.Esri.Shapefile/Shx/ShxStreamExtensions.cs +++ b/src/NetTopologySuite.IO.Esri.Shapefile/Shx/ShxStreamExtensions.cs @@ -1,4 +1,5 @@ -using NetTopologySuite.IO.Esri.Shp; +using NetTopologySuite.Geometries; +using NetTopologySuite.IO.Esri.Shp; using System.IO; namespace NetTopologySuite.IO.Esri.Shx @@ -12,9 +13,9 @@ public static void WriteShxFileHeader(this Stream stream, ShapeType type, int fi stream.WriteShpFileHeader(type, fileLength, extent, hasZ, hasM); } - public static void ReadShxFileHeader(this Stream binary, out ShapeType type, out int fileLength) + public static void ReadShxFileHeader(this Stream binary, out ShapeType type, out int fileLength, out Envelope boundingBox) { - binary.ReadShpFileHeader(out type, out fileLength); + binary.ReadShpFileHeader(out type, out fileLength, out boundingBox); } diff --git a/test/NetTopologySuite.IO.Esri.Test/Deprecated/Attributes/AttributesTest.cs b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Attributes/AttributesTest.cs new file mode 100644 index 0000000..69bc19c --- /dev/null +++ b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Attributes/AttributesTest.cs @@ -0,0 +1,140 @@ +using NetTopologySuite.Geometries; +using NetTopologySuite.Features; +using NUnit.Framework; +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; + +namespace NetTopologySuite.IO.Esri.Test.Deprecated.Attributes +{ + public class AttributesTest + { + [Test] + public void TestShapeCreation() + { + var points = new Coordinate[3]; + points[0] = new Coordinate(0, 0); + points[1] = new Coordinate(1, 0); + points[2] = new Coordinate(1, 1); + + var line_string = new LineString(points); + + var attributes = new AttributesTable(); + attributes.Add("FOO", "FOO"); + + var factory = GeometryFactory.Default; + var feature = new Feature(factory.CreateMultiLineString(new LineString[] { line_string }), attributes); + var features = new Feature[1]; + features[0] = feature; + + Shapefile.WriteAllFeatures(features, "line_string"); + Assert.That(File.Exists("line_string.shp")); + } + + [Test] + public void TestConstructor2() + { + IAttributesTable at = null; + /* + Assert.DoesNotThrow( + () => at = new AttributesTable(new[] {new[] {"key1", new object()}, new[] {"key2", new object()}})); + Assert.That(at, Is.Not.Null); + Assert.That(at.Count, Is.EqualTo(2)); + Assert.That(at.Exists("key1"), Is.True); + Assert.That(at.Exists("key2"), Is.True); + Assert.Throws( + () => at = new AttributesTable(new[] {new[] {"key1", new object()}, new[] {(object) "key2",}})); + Assert.Throws( + () => at = new AttributesTable(new[] {new[] {"key1", new object()}, new[] {new object(), "item2",}})); + */ + Assert.DoesNotThrow(() => at = new AttributesTable { { "key1", new object() }, { "key2", new object() } }); + Assert.That(at, Is.Not.Null); + Assert.That(at.Count, Is.EqualTo(2)); + Assert.That(at.Exists("key1"), Is.True); + Assert.That(at.Exists("key2"), Is.True); + } + + [Test] + public void TestSharcDbf() + { + string filename = TestShapefiles.PathToCountriesPt(".dbf"); + if (!File.Exists(filename)) + throw new FileNotFoundException(filename + " not found at " + Environment.CurrentDirectory); + + using var reader = new Dbf.DbfReader(filename); + Console.WriteLine("RecordLength: " + reader.RecordSize); + Console.WriteLine("NumFields: " + reader.Fields.Count); + Console.WriteLine("NumRecords: " + reader.RecordCount); + Console.WriteLine("LastUpdateDate: " + reader.LastUpdateDate); + foreach (var descr in reader.Fields) + { + Console.WriteLine("FieldName: " + descr.Name); + Console.WriteLine("DBF Type: " + descr.FieldType); + Console.WriteLine("Length: " + descr.Length); + Console.WriteLine("DecimalCount: " + descr.NumericScale); + } + + foreach (var record in reader) + { + foreach (var name in record.GetNames()) + { + Console.WriteLine(name + ": " + record[name]); + } + } + Console.WriteLine(); + } + + [Test] + public void ReadFromShapeFile() + { + var featureCollection = new List(); + string filename = TestShapefiles.PathToCountriesPt(); + if (!File.Exists(filename)) + throw new FileNotFoundException(filename + " not found"); + using var dataReader = Shapefile.OpenRead(filename); + while (dataReader.Read()) + { + var feature = new Feature { Geometry = dataReader.Geometry }; + + int length = dataReader.Fields.Count; + string[] keys = new string[length]; + for (int i = 0; i < length; i++) + keys[i] = dataReader.Fields[i].Name; + + feature.Attributes = new AttributesTable(); + for (int i = 0; i < length; i++) + { + object val = dataReader.Fields[i].Value; + feature.Attributes.Add(keys[i], val); + } + + featureCollection.Add(feature); + } + + int index = 0; + Console.WriteLine("Elements = " + featureCollection.Count); + foreach (var feature in featureCollection) + { + Console.WriteLine("Feature " + index++); + var table = feature.Attributes as AttributesTable; + foreach (string name in table.GetNames()) + Console.WriteLine(name + ": " + table[name]); + } + + // Test write with stub header + string stubHeaderFile = TestShapefiles.PathTo("testWriteStubHeader"); + TestShapefiles.DeleteShp(stubHeaderFile); + Shapefile.WriteAllFeatures(featureCollection, stubHeaderFile); + + // Test write with header from a existing shapefile + string shpHeaderFile = TestShapefiles.PathTo("testWriteShapefileHeader"); + TestShapefiles.DeleteShp(shpHeaderFile); + + using var stubHeaderReader = Shapefile.OpenRead(stubHeaderFile); + var options = new ShapefileWriterOptions(stubHeaderReader); + using var dataWriter = Shapefile.OpenWrite(shpHeaderFile, options); + dataWriter.Write(featureCollection); + } + } +} diff --git a/test/NetTopologySuite.IO.Esri.Test/Deprecated/Attributes/DbfDateTest.cs b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Attributes/DbfDateTest.cs new file mode 100644 index 0000000..d3bcf02 --- /dev/null +++ b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Attributes/DbfDateTest.cs @@ -0,0 +1,51 @@ +using NUnit.Framework; +using System; +using System.Collections; +using System.IO; +using System.Linq; + +namespace NetTopologySuite.IO.Esri.Test.Deprecated.Attributes +{ + /// + /// + /// + [TestFixture] + public class DbfDateTest + { + /// + /// Initializes a new instance of the class. + /// + public DbfDateTest() + { + + } + + /// + /// + /// + [Test] + public void ReadDbfDate() + { + string file = TestShapefiles.PathTo("date.dbf"); + + if (!File.Exists(file)) + throw new FileNotFoundException("file not found at " + Path.GetDirectoryName(file)); + + using var reader = new Dbf.DbfReader(file); + var record = reader.FirstOrDefault(); + + Assert.IsNotNull(record); + Assert.AreEqual(2, record.Count); + + foreach (object value in record.GetValues()) + Assert.IsNotNull(value); + + var dateFieldName = reader.Fields[1].Name; + var date = (DateTime)record[dateFieldName]; + + Assert.AreEqual(10, date.Day); + Assert.AreEqual(3, date.Month); + Assert.AreEqual(2006, date.Year); + } + } +} diff --git a/test/NetTopologySuite.IO.Esri.Test/Deprecated/Features/UseOfIndexAndPreparedGeometry.cs b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Features/UseOfIndexAndPreparedGeometry.cs new file mode 100644 index 0000000..9eaf9cc --- /dev/null +++ b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Features/UseOfIndexAndPreparedGeometry.cs @@ -0,0 +1,248 @@ +using NetTopologySuite.Features; +using NetTopologySuite.Geometries; +using NetTopologySuite.Geometries.Utilities; +using NetTopologySuite.Index.Quadtree; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics.Contracts; + +namespace NetTopologySuite.IO.Esri.Test.Deprecated.Features +{ + + public class UseOfIndexAndPreparedGeometry + { + [TestCase(@"eurostat/countries_pt.shp")] + [TestCase(@"eurostat/countries_ln.shp")] + [TestCase(@"eurostat/countries_pg.shp")] + public void TestShapefile(string shapefile) + { + shapefile = TestShapefiles.PathTo(shapefile); + if (!System.IO.File.Exists(shapefile)) + throw new Exception("file not present: " + shapefile); + + var featureCollection = new Collection(); + Envelope bbox = GetBoundingBox(shapefile); + var options = new ShapefileReaderOptions() + { + MbrFilter = bbox + }; + using var shp = Shapefile.OpenRead(shapefile, options); + foreach (var shapefileFeature in shp) + { + featureCollection.Add(shapefileFeature); + } + + const double min1 = 0.4; + const double min2 = 0.5 - min1; + + var rnd = new Random(8888); + var queryBox = new Envelope( + bbox.MinX + (min1 + rnd.NextDouble() * min2) * bbox.Width, + bbox.MaxX - (min1 + rnd.NextDouble() * min2) * bbox.Width, + bbox.MinY + (min1 + rnd.NextDouble() * min2) * bbox.Height, + bbox.MaxY - (min1 + rnd.NextDouble() * min2) * bbox.Height); + + var shape = new SineStarFactory(GeometryFactory.Default) { Envelope = queryBox }.CreateSineStar(); + TestShapefilePlain(featureCollection, shape, "intersects"); + TestShapefilePlain(featureCollection, shape, "intersects"); + TestShapefilePrepared(featureCollection, shape, "intersects"); + TestShapefileIndexed(featureCollection, shape, "intersects"); + } + + private Envelope GetBoundingBox(string shapefile) + { + using var shp = Shapefile.OpenRead(shapefile); + return shp.BoundingBox; + } + + private void TestShapefilePrepared(ICollection features, Geometry queryGeom, string spatialPredicate) + { + System.Diagnostics.Debug.WriteLine("\nPrepared"); + var sw = new System.Diagnostics.Stopwatch(); + sw.Start(); + System.Diagnostics.Debug.WriteLine("Setup collection: {0}ms", sw.ElapsedMilliseconds); + sw.Restart(); + var prep = NetTopologySuite.Geometries.Prepared.PreparedGeometryFactory.Prepare(queryGeom); + var lst = new List(2048); + foreach (var feature in features) + { + if (prep.Intersects(feature.Geometry)) + lst.Add(feature); + } + System.Diagnostics.Debug.WriteLine("Query collection: {0}ms", sw.ElapsedMilliseconds); + System.Diagnostics.Debug.WriteLine("Queried {0} features of a set of {1}", lst.Count, features.Count); + } + + private void TestShapefilePlain(ICollection features, Geometry queryGeom, string spatialPredicate) + { + System.Diagnostics.Debug.WriteLine("\nPlain"); + var sw = new System.Diagnostics.Stopwatch(); + sw.Start(); + System.Diagnostics.Debug.WriteLine("Setup collection: {0}ms", sw.ElapsedMilliseconds); + sw.Restart(); + var lst = new List(2048); + foreach (var feature in features) + { + if (queryGeom.Intersects(feature.Geometry)) + lst.Add(feature); + } + System.Diagnostics.Debug.WriteLine("Query collection: {0}ms", sw.ElapsedMilliseconds); + System.Diagnostics.Debug.WriteLine("Queried {0} features of a set of {1}", lst.Count, features.Count); + } + + private void TestShapefileIndexed(ICollection features, Geometry queryGeom, string spatialPredicate) + { + System.Diagnostics.Debug.WriteLine("\nIndexed"); + var sw = new System.Diagnostics.Stopwatch(); + sw.Start(); + var idxFeatureCollection = new IndexedFeatureColection(); + foreach (var feature in features) + idxFeatureCollection.Add(feature); + System.Diagnostics.Debug.WriteLine("Setup collection: {0}ms", sw.ElapsedMilliseconds); + sw.Restart(); + var lst = new List(idxFeatureCollection.Query(queryGeom, spatialPredicate)); + System.Diagnostics.Debug.WriteLine("Query collection: {0}ms", sw.ElapsedMilliseconds); + System.Diagnostics.Debug.WriteLine("Queried {0} features of a set of {1}", lst.Count, features.Count); + } + + } + + public class IndexedFeatureColection : Collection, ICollection + { + private Quadtree _featuresIndex; + + private class FeatureComparer : EqualityComparer + { + public override bool Equals(IFeature x, IFeature y) + { + if (x == null || y == null) throw new ArgumentNullException(); + + if (x.Attributes == null && y.Attributes != null) + return false; + + if (x.Attributes != null && y.Attributes == null) + return false; + + if (x.Attributes.Count != y.Attributes.Count) + return false; + + string[] names = x.Attributes.GetNames(); + foreach (string name in names) + { + object v1 = x.Attributes[name]; + object v2 = y.Attributes[name]; + if (!v1.Equals(v2)) return false; + } + + if (!x.Geometry.EqualsTopologically(y.Geometry)) + return false; + + return true; + } + + public override int GetHashCode(IFeature obj) + { + int res = obj.Geometry.GetHashCode(); + if (obj.Attributes != null) + { + foreach (object value in obj.Attributes.GetValues()) + res ^= value.GetHashCode(); + } + return res; + } + } + + private readonly FeatureComparer _featureComparer = new FeatureComparer(); + + public IndexedFeatureColection() + { + _featuresIndex = new Quadtree(); + } + + protected override void ClearItems() + { + base.ClearItems(); + _featuresIndex = new Quadtree(); + } + + protected override void InsertItem(int index, IFeature item) + { + base.InsertItem(index, item); + _featuresIndex.Insert(item.BoundingBox, index); + } + + protected override void RemoveItem(int index) + { + _featuresIndex.Remove(this[index].BoundingBox, index); + base.RemoveItem(index); + } + + protected override void SetItem(int index, IFeature item) + { + var oldItem = this[index]; + _featuresIndex.Remove(oldItem.Geometry.EnvelopeInternal, index); + base.SetItem(index, item); + _featuresIndex.Insert(item.Geometry.EnvelopeInternal, index); + } + + public new bool Contains(IFeature item) + { + var indices = _featuresIndex.Query(item.BoundingBox); + foreach (int tmpItem in indices) + { + var feature = this[tmpItem]; + if (_featureComparer.Equals(item, feature)) + return true; + } + return false; + } + + public IEnumerable Query(Geometry geom, string spatialPredicate) + { + if (geom == null) + throw new ArgumentNullException("geom"); + + var prepgeom = NetTopologySuite.Geometries.Prepared.PreparedGeometryFactory.Prepare(geom); + switch (spatialPredicate.Trim().ToLowerInvariant()) + { + case "intersects": + return QueryInternal(geom.EnvelopeInternal, prepgeom.Intersects); + case "contains": + return QueryInternal(geom.EnvelopeInternal, prepgeom.Contains); + case "containsproperly": + return QueryInternal(geom.EnvelopeInternal, prepgeom.ContainsProperly); + case "coveredby": + return QueryInternal(geom.EnvelopeInternal, prepgeom.CoveredBy); + case "covers": + return QueryInternal(geom.EnvelopeInternal, prepgeom.Covers); + case "crosses": + return QueryInternal(geom.EnvelopeInternal, prepgeom.Crosses); + case "disjoint": + return QueryInternal(geom.EnvelopeInternal, prepgeom.Disjoint); + case "overlaps": + return QueryInternal(geom.EnvelopeInternal, prepgeom.Overlaps); + case "within": + return QueryInternal(geom.EnvelopeInternal, prepgeom.Within); + case "touches": + return QueryInternal(geom.EnvelopeInternal, prepgeom.Touches); + + } + return null; + } + + private IEnumerable QueryInternal(Envelope bbox, Func spatialPredicate) + { + Contract.Assert(bbox != null); + Contract.Assert(spatialPredicate != null); + + foreach (int index in _featuresIndex.Query(bbox)) + { + var feature = this[index]; + if (spatialPredicate(feature.Geometry)) + yield return feature; + } + } + } +} diff --git a/test/NetTopologySuite.IO.Esri.Test/Deprecated/Geometries/LineStringSamples.cs b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Geometries/LineStringSamples.cs new file mode 100644 index 0000000..45145c9 --- /dev/null +++ b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Geometries/LineStringSamples.cs @@ -0,0 +1,162 @@ +using NetTopologySuite.Geometries; +using System; +using NUnit.Framework; +using NetTopologySuite.Operation.Buffer; +using NetTopologySuite.Features; + +namespace NetTopologySuite.IO.Esri.Test.Deprecated.Geometries +{ + /// + /// + /// + public class LineStringSamples + { + protected GeometryFactory Factory { get; private set; } + + protected WKTReader Reader { get; private set; } + + private LineString line = null; + + /// + /// + /// + public LineStringSamples() : base() + { + this.Factory = new GeometryFactory(); + this.Reader = new WKTReader(); + + var coordinates = new Coordinate[] + { + new Coordinate(10, 10), + new Coordinate(20, 20), + new Coordinate(20, 10), + }; + line = Factory.CreateLineString(coordinates); + } + + [Test] + public void Start() + { + var pointInLine = Factory.CreatePoint(new Coordinate(20, 10)); + var pointOutLine = Factory.CreatePoint(new Coordinate(20, 31)); + var aLine = Factory.CreateLineString(new Coordinate[] { new Coordinate(23, 32.2), new Coordinate(922, 11) }); + var anotherLine = Factory.CreateLineString(new Coordinate[] { new Coordinate(0, 1), new Coordinate(30, 30) }); + + Write(line.Area); + Write(line.Boundary); + Write(line.BoundaryDimension); + Write(line.Centroid); + Write(line.Coordinate); + Write(line.Coordinates); + Write(line.CoordinateSequence); + Write(line.Dimension); + Write(line.EndPoint); + Write(line.Envelope); + Write(line.EnvelopeInternal); + Write(line.InteriorPoint); + Write(line.IsClosed); + Write(line.IsEmpty); + Write(line.IsRing); + Write(line.IsSimple); + Write(line.IsValid); + Write(line.Length); + Write(line.NumPoints); + Write(line.StartPoint); + if (line.UserData != null) + Write(line.UserData); + else Write("UserData null"); + + Write(line.Buffer(10)); + Write(line.Buffer(10, new BufferParameters { EndCapStyle = EndCapStyle.Flat })); + Write(line.Buffer(10, new BufferParameters { EndCapStyle = EndCapStyle.Square })); + Write(line.Buffer(10, 20)); + Write(line.Buffer(10, new BufferParameters(20) { EndCapStyle = EndCapStyle.Flat })); + Write(line.Buffer(10, new BufferParameters(20) { EndCapStyle = EndCapStyle.Square })); + Write(line.Contains(pointInLine)); + Write(line.Contains(pointOutLine)); + Write(line.Crosses(pointInLine)); + Write(line.Crosses(pointOutLine)); + Write(line.Difference(pointInLine)); + Write(line.Difference(pointOutLine)); + Write(line.Disjoint(pointInLine)); + Write(line.Disjoint(pointOutLine)); + Write(line.Distance(pointInLine)); + Write(line.Distance(pointOutLine)); + Write(line.EqualsTopologically(line.Copy() as LineString)); + Write(line.EqualsExact(line.Copy() as LineString)); + Write(line.ConvexHull()); + Write(line.Intersection(pointInLine)); + Write(line.Intersection(pointOutLine)); + Write(line.Intersection(aLine)); + Write(line.Intersects(pointInLine)); + Write(line.Intersects(pointOutLine)); + Write(line.Intersects(aLine)); + Write(line.IsWithinDistance(pointOutLine, 2)); + Write(line.IsWithinDistance(pointOutLine, 222)); + Write(line.Overlaps(pointInLine)); + Write(line.Overlaps(pointOutLine)); + Write(line.Overlaps(aLine)); + Write(line.Overlaps(anotherLine)); + Write(line.Relate(pointInLine)); + Write(line.Relate(pointOutLine)); + Write(line.Relate(aLine)); + Write(line.Relate(anotherLine)); + Write(line.SymmetricDifference(pointInLine)); + Write(line.SymmetricDifference(pointOutLine)); + Write(line.SymmetricDifference(aLine)); + Write(line.SymmetricDifference(anotherLine)); + Write(line.ToString()); + Write(line.AsText()); + Write(line.Touches(pointInLine)); + Write(line.Touches(pointOutLine)); + Write(line.Touches(aLine)); + Write(line.Touches(anotherLine)); + Write(line.Union(pointInLine)); + Write(line.Union(pointOutLine)); + Write(line.Union(aLine)); + Write(line.Union(anotherLine)); + Write(line.Within(pointInLine)); + Write(line.Within(pointOutLine)); + Write(line.Within(aLine)); + Write(line.Within(anotherLine)); + + string linestring = "LINESTRING (1.2 3.4, 5.6 7.8, 9.1 10.12)"; + string anotherlinestringg = "LINESTRING (12345 3654321, 685 7777.945677, 782 111.1)"; + var geom1 = Reader.Read(linestring); + Write(geom1.AsText()); + var geom2 = Reader.Read(anotherlinestringg); + Write(geom2.AsText()); + + byte[] bytes = line.AsBinary(); + var test1 = new WKBReader().Read(bytes); + Write(test1.ToString()); + + var features = new Feature[] + { + CreateFeature(line), + CreateFeature(aLine), + CreateFeature(anotherLine), + CreateFeature((LineString)geom1), + CreateFeature((LineString)geom2), + }; + Shapefile.WriteAllFeatures(features, GetType().Name); + Shapefile.ReadAllFeatures(GetType().Name); + } + + private Feature CreateFeature(LineString lineString) + { + var mls = new MultiLineString(new LineString[] { lineString }); + return new Feature(mls, null); + } + + protected void Write(object o) + { + Console.WriteLine(o.ToString()); + } + + protected void Write(string s) + { + Console.WriteLine(s); + } + } +} diff --git a/test/NetTopologySuite.IO.Esri.Test/Deprecated/Geometries/MultiPointSamples.cs b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Geometries/MultiPointSamples.cs new file mode 100644 index 0000000..20aac59 --- /dev/null +++ b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Geometries/MultiPointSamples.cs @@ -0,0 +1,96 @@ +using NetTopologySuite.Geometries; +using System; +using NUnit.Framework; +using NetTopologySuite.Operation.Buffer; +using NetTopologySuite.Features; + +namespace NetTopologySuite.IO.Esri.Test.Deprecated.Geometries +{ + /// + /// + /// + public class MultiPointSamples + { + private MultiPoint multiPoint = null; + + protected GeometryFactory Factory { get; private set; } + + protected WKTReader Reader { get; private set; } + + /// + /// + /// + public MultiPointSamples() : base() + { + this.Factory = new GeometryFactory(); + this.Reader = new WKTReader(); + + var coordinates = new Coordinate[] + { + new Coordinate(100,100), + new Coordinate(200,200), + new Coordinate(300,300), + new Coordinate(400,400), + new Coordinate(500,500), + }; + multiPoint = Factory.CreateMultiPointFromCoords(coordinates); + } + + [Test] + public void Start() + { + Write(multiPoint.Area); + Write(multiPoint.Boundary); + Write(multiPoint.BoundaryDimension); + Write(multiPoint.Centroid); + Write(multiPoint.Coordinate); + Write(multiPoint.Coordinates); + Write(multiPoint.Dimension); + Write(multiPoint.Envelope); + Write(multiPoint.EnvelopeInternal); + Write(multiPoint.Geometries.Length); + Write(multiPoint.InteriorPoint); + Write(multiPoint.IsEmpty); + Write(multiPoint.IsSimple); + Write(multiPoint.IsValid); + Write(multiPoint.Length); + Write(multiPoint.NumGeometries); + Write(multiPoint.NumPoints); + + Write(multiPoint.Buffer(10)); + Write(multiPoint.Buffer(10, new BufferParameters {EndCapStyle = EndCapStyle.Flat })); + Write(multiPoint.Buffer(10, new BufferParameters { EndCapStyle = EndCapStyle.Square })); + Write(multiPoint.Buffer(10, 20)); + Write(multiPoint.Buffer(10, new BufferParameters(20) { EndCapStyle = EndCapStyle.Flat })); + Write(multiPoint.Buffer(10, new BufferParameters(20) { EndCapStyle = EndCapStyle.Square })); + Write(multiPoint.ConvexHull()); + + byte[] bytes = multiPoint.AsBinary(); + var test1 = new WKBReader().Read(bytes); + Write(test1.ToString()); + + var features = new Feature[] + { + CreateFeature(multiPoint), + CreateFeature((MultiPoint)test1) + }; + Shapefile.WriteAllFeatures(features, GetType().Name); + Shapefile.ReadAllFeatures(GetType().Name); + } + + private Feature CreateFeature(MultiPoint multiPoint) + { + return new Feature(multiPoint, null); + } + + protected void Write(object o) + { + Console.WriteLine(o.ToString()); + } + + protected void Write(string s) + { + Console.WriteLine(s); + } + } +} diff --git a/test/NetTopologySuite.IO.Esri.Test/Deprecated/Geometries/NestedPolygons.cs b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Geometries/NestedPolygons.cs new file mode 100644 index 0000000..0758288 --- /dev/null +++ b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Geometries/NestedPolygons.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using NetTopologySuite.Geometries; +using NUnit.Framework; + +namespace NetTopologySuite.IO.Esri.Test.Deprecated.Geometries +{ + [TestFixture] + public class NestedPolygonsTest + { + public readonly string Wkt = @" + MULTIPOLYGON( + ( + (1 9, 9 9, 9 1, 1 1, 1 9), + (2 8, 2 2, 8 2, 8 8, 2 8) + ), + ( + (3 7, 7 7, 7 3, 3 3, 3 7), + (4 6, 4 4, 6 4, 6 6, 4 6) + ) + )"; + + [Test] + public void WriteNestedPolygons() + { + // This will check reading nested polygons from SHP file + var nestedPolygons = Shapefile.ReadAllFeatures(TestShapefiles.PathTo("nested_polygons.shp")); + var nestedPolygonsBytes = File.ReadAllBytes(TestShapefiles.PathTo("nested_polygons.shp")); + Assert.AreEqual(1, nestedPolygons.Length); + + var multiPolygon = nestedPolygons[0].Geometry as MultiPolygon; + Assert.AreEqual(2, multiPolygon.Count); + + var firstPolygon = multiPolygon[0] as Polygon; + Assert.AreEqual(1, firstPolygon.Holes.Length); + + var secondPolygon = multiPolygon[0] as Polygon; + Assert.AreEqual(1, secondPolygon.Holes.Length); + + Assert.IsTrue(firstPolygon.EnvelopeInternal.Contains(secondPolygon.EnvelopeInternal)); + Assert.IsTrue(firstPolygon.Shell.EnvelopeInternal.Contains(secondPolygon.EnvelopeInternal)); + + + // This will check writing nested polygons from SHP file + var tempShpPath = TestShapefiles.GetTempShpPath(); + Shapefile.WriteAllFeatures(nestedPolygons, tempShpPath); + var nestedPolygonsTest = Shapefile.ReadAllFeatures(tempShpPath); + var nestedPolygonsTestBytes = File.ReadAllBytes(tempShpPath); + + Assert.AreEqual(nestedPolygons.Length, nestedPolygonsTest.Length); + Assert.IsTrue(nestedPolygonsBytes.SequenceEqual(nestedPolygonsTestBytes)); + } + } +} diff --git a/test/NetTopologySuite.IO.Esri.Test/Deprecated/Geometries/PointSamples.cs b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Geometries/PointSamples.cs new file mode 100644 index 0000000..d11d9ff --- /dev/null +++ b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Geometries/PointSamples.cs @@ -0,0 +1,149 @@ +using NetTopologySuite.Geometries; +using System; +using NUnit.Framework; +using NetTopologySuite.Operation.Buffer; +using NetTopologySuite.Features; + +namespace NetTopologySuite.IO.Esri.Test.Deprecated.Geometries +{ + /// + /// + /// + public class PointSamples + { + protected GeometryFactory Factory { get; private set; } + + protected WKTReader Reader { get; private set; } + private Point point = null; + + /// + /// + /// + public PointSamples() + { + this.Factory = new GeometryFactory(); + this.Reader = new WKTReader(); + + point = Factory.CreatePoint(new Coordinate(100, 100)); + } + + [Test] + public void Start() + { + var pInterior = Factory.CreatePoint(new Coordinate(100, 100)); + var pExterior = Factory.CreatePoint(new Coordinate(100, 101)); + + Write(point.Area); + Write(point.Boundary); + Write(point.BoundaryDimension); + Write(point.Centroid); + Write(point.Coordinate); + Write(point.Coordinates); + Write(point.CoordinateSequence); + Write(point.Dimension); + Write(point.Envelope); + Write(point.EnvelopeInternal); + Write(point.Factory); + Write(point.InteriorPoint); + Write(point.IsEmpty); + Write(point.IsSimple); + Write(point.IsValid); + Write(point.Length); + Write(point.NumPoints); + Write(point.PrecisionModel); + Write(point.X); + Write(point.Y); + + Write(point.Contains(pInterior)); + Write(point.Contains(pExterior)); + + Write(point.Buffer(10)); + Write(point.Buffer(10, new BufferParameters { EndCapStyle = EndCapStyle.Square })); + Write(point.Buffer(10, new BufferParameters { EndCapStyle = EndCapStyle.Flat })); + Write(point.Buffer(10, 20)); + Write(point.Buffer(10, new BufferParameters(20) { EndCapStyle = EndCapStyle.Square })); + Write(point.Buffer(10, new BufferParameters(20) { EndCapStyle = EndCapStyle.Flat })); + + Write(point.Crosses(pInterior)); + Write(point.Crosses(pExterior)); + Write(point.Difference(pInterior)); + Write(point.Difference(pExterior)); + Write(point.Disjoint(pInterior)); + Write(point.Disjoint(pExterior)); + Write(point.EqualsTopologically(pInterior)); + Write(point.EqualsTopologically(pExterior)); + Write(point.EqualsExact(pInterior)); + Write(point.EqualsExact(pExterior)); + Write(point.ConvexHull()); + Write(point.Intersection(pInterior)); + Write(point.Intersection(pExterior)); + Write(point.Intersects(pInterior)); + Write(point.Intersects(pExterior)); + Write(point.IsWithinDistance(pInterior, 0.001)); + Write(point.IsWithinDistance(pExterior, 0.001)); + Write(point.Overlaps(pInterior)); + Write(point.Overlaps(pExterior)); + Write(point.SymmetricDifference(pInterior)); + Write(point.SymmetricDifference(pExterior)); + Write(point.ToString()); + Write(point.AsText()); + Write(point.Touches(pInterior)); + Write(point.Touches(pExterior)); + Write(point.Union(pInterior)); + Write(point.Union(pExterior)); + Write(point.Within(pInterior)); + Write(point.Within(pExterior)); + + string pointstring = "POINT (100.22 100.33)"; + string anotherpointstring = "POINT (12345 3654321)"; + var geom1 = Reader.Read(pointstring); + Write(geom1.AsText()); + var geom2 = Reader.Read(anotherpointstring); + Write(geom2.AsText()); + + byte[] bytes = point.AsBinary(); + var test1 = new WKBReader().Read(bytes); + Write(test1.ToString()); + + bytes = Factory.CreatePoint(new Coordinate(double.MinValue, double.MinValue)).AsBinary(); + var testempty = new WKBReader().Read(bytes); + Write(testempty); + + Write(test1.ToString()); + + // Test Empty Geometries + Write(Point.Empty); + Write(LineString.Empty); + Write(Polygon.Empty); + Write(MultiPoint.Empty); + Write(MultiLineString.Empty); + Write(MultiPolygon.Empty); + Write(GeometryCollection.Empty); + + var features = new Feature[] + { + CreateFeature(point), + CreateFeature((Point)test1), + CreateFeature((Point)testempty) + }; + + Shapefile.WriteAllFeatures(features, GetType().Name); + Shapefile.ReadAllFeatures(GetType().Name); + } + + private Feature CreateFeature(Point point) + { + return new Feature(point, null); + } + + protected void Write(object o) + { + Console.WriteLine(o.ToString()); + } + + protected void Write(string s) + { + Console.WriteLine(s); + } + } +} diff --git a/test/NetTopologySuite.IO.Esri.Test/Deprecated/Geometries/PolygonSamples.cs b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Geometries/PolygonSamples.cs new file mode 100644 index 0000000..ceb4dda --- /dev/null +++ b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Geometries/PolygonSamples.cs @@ -0,0 +1,178 @@ +using NetTopologySuite.Geometries; +using System; +using NUnit.Framework; +using NetTopologySuite.Operation.Buffer; +using NetTopologySuite.Features; + +namespace NetTopologySuite.IO.Esri.Test.Deprecated.Geometries +{ + public class PolygonSamples + { + protected GeometryFactory Factory { get; private set; } + + protected WKTReader Reader { get; private set; } + + private Polygon polygon = null; + private LinearRing shell = null; + private LinearRing hole = null; + + /// + /// + /// + public PolygonSamples() + { + this.Factory = new GeometryFactory(new PrecisionModel(PrecisionModels.Fixed)); + this.Reader = new WKTReader(); + + shell = Factory.CreateLinearRing(new Coordinate[] { new Coordinate(100,100), + new Coordinate(200,100), + new Coordinate(200,200), + new Coordinate(100,200), + new Coordinate(100,100), }); + hole = Factory.CreateLinearRing(new Coordinate[] { new Coordinate(120,120), + new Coordinate(180,120), + new Coordinate(180,180), + new Coordinate(120,180), + new Coordinate(120,120), }); + polygon = Factory.CreatePolygon(shell, new LinearRing[] { hole, }); + } + + [Test] + public void Start() + { + var interiorPoint = Factory.CreatePoint(new Coordinate(130, 150)); + var exteriorPoint = Factory.CreatePoint(new Coordinate(650, 1500)); + var aLine = Factory.CreateLineString(new Coordinate[] { new Coordinate(23, 32.2), new Coordinate(10, 222) }); + var anotherLine = Factory.CreateLineString(new Coordinate[] { new Coordinate(0, 1), new Coordinate(30, 30) }); + var intersectLine = Factory.CreateLineString(new Coordinate[] { new Coordinate(0, 1), new Coordinate(300, 300) }); + + Write(polygon.Area); + Write(polygon.Boundary); + Write(polygon.BoundaryDimension); + Write(polygon.Centroid); + Write(polygon.Coordinate); + Write(polygon.Coordinates.Length); + Write(polygon.Dimension); + Write(polygon.Envelope); + Write(polygon.EnvelopeInternal); + Write(polygon.ExteriorRing); + Write(polygon.InteriorPoint); + Write(polygon.InteriorRings.Length); + Write(polygon.IsEmpty); + Write(polygon.IsSimple); + Write(polygon.IsValid); + Write(polygon.Length); + Write(polygon.NumInteriorRings); + Write(polygon.NumPoints); + if (polygon.UserData != null) + Write(polygon.UserData); + else Write("UserData null"); + + Write(polygon.Buffer(10)); + Write(polygon.Buffer(10, new BufferParameters { EndCapStyle = EndCapStyle.Flat})); + Write(polygon.Buffer(10, new BufferParameters { EndCapStyle = EndCapStyle.Square })); + Write(polygon.Buffer(10, 20)); + Write(polygon.Buffer(10, new BufferParameters(20) { EndCapStyle = EndCapStyle.Flat })); + Write(polygon.Buffer(10, new BufferParameters(20) { EndCapStyle = EndCapStyle.Square })); + Write(polygon.Contains(interiorPoint)); + Write(polygon.Contains(exteriorPoint)); + Write(polygon.Contains(aLine)); + Write(polygon.Contains(anotherLine)); + Write(polygon.Crosses(interiorPoint)); + Write(polygon.Crosses(exteriorPoint)); + Write(polygon.Crosses(aLine)); + Write(polygon.Crosses(anotherLine)); + Write(polygon.Difference(interiorPoint)); + Write(polygon.Difference(exteriorPoint)); + Write(polygon.Difference(aLine)); + Write(polygon.Difference(anotherLine)); + Write(polygon.Disjoint(interiorPoint)); + Write(polygon.Disjoint(exteriorPoint)); + Write(polygon.Disjoint(aLine)); + Write(polygon.Disjoint(anotherLine)); + Write(polygon.Distance(interiorPoint)); + Write(polygon.Distance(exteriorPoint)); + Write(polygon.Distance(aLine)); + Write(polygon.Distance(anotherLine)); + Write(polygon.Intersection(interiorPoint)); + Write(polygon.Intersection(exteriorPoint)); + Write(polygon.Intersection(aLine)); + Write(polygon.Intersection(anotherLine)); + Write(polygon.Intersects(interiorPoint)); + Write(polygon.Intersects(exteriorPoint)); + Write(polygon.Intersects(aLine)); + Write(polygon.Intersects(anotherLine)); + Write(polygon.IsWithinDistance(interiorPoint, 300)); + Write(polygon.IsWithinDistance(exteriorPoint, 300)); + Write(polygon.IsWithinDistance(aLine, 300)); + Write(polygon.IsWithinDistance(anotherLine, 300)); + Write(polygon.Overlaps(interiorPoint)); + Write(polygon.Overlaps(exteriorPoint)); + Write(polygon.Overlaps(aLine)); + Write(polygon.Overlaps(anotherLine)); + Write(polygon.Relate(interiorPoint)); + Write(polygon.Relate(exteriorPoint)); + Write(polygon.Relate(aLine)); + Write(polygon.Relate(anotherLine)); + Write(polygon.SymmetricDifference(interiorPoint)); + Write(polygon.SymmetricDifference(exteriorPoint)); + Write(polygon.SymmetricDifference(aLine)); + Write(polygon.SymmetricDifference(anotherLine)); + Write(polygon.ToString()); + Write(polygon.AsText()); + Write(polygon.Touches(interiorPoint)); + Write(polygon.Touches(exteriorPoint)); + Write(polygon.Touches(aLine)); + Write(polygon.Touches(anotherLine)); + Write(polygon.Union(interiorPoint)); + Write(polygon.Union(exteriorPoint)); + Write(polygon.Union(aLine)); + Write(polygon.Union(anotherLine)); + + string aPoly = "POLYGON ((20 20, 100 20, 100 100, 20 100, 20 20))"; + string anotherPoly = "POLYGON ((20 20, 100 20, 100 100, 20 100, 20 20), (50 50, 60 50, 60 60, 50 60, 50 50))"; + var geom1 = Reader.Read(aPoly); + Write(geom1.AsText()); + var geom2 = Reader.Read(anotherPoly); + Write(geom2.AsText()); + + // ExpandToInclude tests + var envelope = new Envelope(0, 0, 0, 0); + envelope.ExpandToInclude(geom1.EnvelopeInternal); + envelope.ExpandToInclude(geom2.EnvelopeInternal); + Write(envelope.ToString()); + + // The polygon is not correctly ordered! Calling normalize we fix the problem... + polygon.Normalize(); + + byte[] bytes = polygon.AsBinary(); + var test1 = new WKBReader().Read(bytes); + Write(test1.ToString()); + + var features = new Feature[] + { + CreateFeature(polygon), + CreateFeature((Polygon)geom1), + CreateFeature((Polygon)geom2), + }; + Shapefile.WriteAllFeatures(features, GetType().Name); + Shapefile.ReadAllFeatures(GetType().Name); + } + + private Feature CreateFeature(Polygon polygon) + { + var mls = new MultiPolygon(new Polygon[] { polygon }); + return new Feature(mls, null); + } + + protected void Write(object o) + { + Console.WriteLine(o.ToString()); + } + + protected void Write(string s) + { + Console.WriteLine(s); + } + } +} diff --git a/test/NetTopologySuite.IO.Esri.Test/Deprecated/Headers/ShapeFileEncodingTest.cs b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Headers/ShapeFileEncodingTest.cs new file mode 100644 index 0000000..afaae25 --- /dev/null +++ b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Headers/ShapeFileEncodingTest.cs @@ -0,0 +1,51 @@ +using NetTopologySuite.Features; +using NetTopologySuite.Geometries; +using NUnit.Framework; +using System.Collections.Generic; +using System.Text; + +namespace NetTopologySuite.IO.Esri.Test.Deprecated.Headers +{ + [TestFixture] + public class ShapeFileEncodingTest + { + [SetUp] + public void Setup() + { + Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); + var options = new ShapefileWriterOptions(ShapeType.Point) + { + Encoding = CodePagesEncodingProvider.Instance.GetEncoding(1252) + }; + options.AddNumericInt32Field("id"); + options.AddCharacterField("Test", 15); + options.AddNumericInt32Field("Ålder"); + options.AddCharacterField("Ödestext"); + using var sfdr = Shapefile.OpenWrite("encoding_sample", options); + + var feats = new List(); + var at = new AttributesTable(); + at.Add("id", "0"); + at.Add("Test", "Testar"); + at.Add("Ålder", 10); + at.Add("Ödestext", "Lång text med åäö etc"); + feats.Add(new Feature(new Point(0, 0), at)); + sfdr.Write(feats); + } + + [Test] + public void TestLoadShapeFileWithEncoding() + { + var reader = Shapefile.OpenRead("encoding_sample.shp"); + Assert.AreEqual(reader.Encoding, CodePagesEncodingProvider.Instance.GetEncoding(1252), "Invalid encoding!"); + + Assert.AreEqual(reader.Fields[1].Name, "Test"); + Assert.AreEqual(reader.Fields[2].Name, "Ålder"); + Assert.AreEqual(reader.Fields[3].Name, "Ödestext"); + + Assert.IsTrue(reader.Read(), "Error reading file"); + Assert.AreEqual(reader.Fields["Test"].Value, "Testar"); + Assert.AreEqual(reader.Fields["Ödestext"].Value, "Lång text med åäö etc"); + } + } +} diff --git a/test/NetTopologySuite.IO.Esri.Test/Deprecated/Headers/ShapeFileInvalidHeaderTest.cs b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Headers/ShapeFileInvalidHeaderTest.cs new file mode 100644 index 0000000..d5541b1 --- /dev/null +++ b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Headers/ShapeFileInvalidHeaderTest.cs @@ -0,0 +1,30 @@ +using NUnit.Framework; +using System.Collections; +using System.IO; + +namespace NetTopologySuite.IO.Esri.Test.Deprecated.Headers +{ + [TestFixture] + //[Ignore("Sample file(s) not published")] + public class ShapeFileInvalidHeaderTest + { + private readonly string _invalidPath = TestShapefiles.PathToCountriesPg(); + + [Test] + public void TestInvalidShapeFile() + { + /* + var s = new NetTopologySuite.IO.ShapefileReader(_invalidPath); + var sh = s.Header; + var g = s.ReadAll(); + */ + string dbf = Path.ChangeExtension(_invalidPath, ".dbf"); + var d = new Dbf.DbfReader(dbf) as IEnumerable; + + var de = d.GetEnumerator(); + Assert.IsNull(de.Current); + de.MoveNext(); + Assert.IsNotNull(de.Current); + } + } +} diff --git a/test/NetTopologySuite.IO.Esri.Test/Deprecated/Issues/Issue123Test.cs b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Issues/Issue123Test.cs new file mode 100644 index 0000000..d33e20c --- /dev/null +++ b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Issues/Issue123Test.cs @@ -0,0 +1,58 @@ +using NetTopologySuite.Geometries; +using NetTopologySuite.Operation.Union; +using NetTopologySuite.Operation.Valid; +using NUnit.Framework; +using System.Collections.Generic; + +namespace NetTopologySuite.IO.Esri.Test.Deprecated.Issues +{ + [TestFixture] + [NtsIssueNumber(123)] + public class Issue123Test + { + [Test] + public void CascadedUnionError() + { + string[] wkt = + { + //MULTIPOLYGON (((-2.775 -37.382, -2.7694818956884695 -37.302294048833446, -4.381 -37.19, -4.379 -37.16, -2.7674053419183364 -37.272299383264858, -2.766 -37.252, -2.703 -37.257, -2.712 -37.386, -2.775 -37.382)), ((-0.558 -16.355, -0.556624473051351 -16.33528411373603, -2.168 -16.223, -2.165 -16.193, -0.55452706181921063 -16.305221219408683, -0.549 -16.226, -0.485 -16.23, -0.494 -16.36, -0.558 -16.355))) + "MULTIPOLYGON (((-2.775 -37.382, -2.7694818956884695 -37.302294048833446, -4.381 -37.19, -4.379 -37.16, -2.7674053419183364 -37.272299383264858, -2.766 -37.252, -2.703 -37.257, -2.712 -37.386, -2.775 -37.382)), ((-0.558 -16.355, -0.556624473051351 -16.33528411373603, -2.168 -16.223, -2.165 -16.193, -0.55452706181921063 -16.305221219408683, -0.549 -16.226, -0.485 -16.23, -0.494 -16.36, -0.558 -16.355)))", + //MULTIPOLYGON (((-4.218 -16.08, -4.216 -16.05, -2.924 -16.14, -2.926 -16.17, -4.218 -16.08)), ((-5.291 -18.097, -5.243 -17.415, -5.239 -17.352, -5.15929328747628 -17.357518157020873, -5.071 -16.091, -5.041 -16.093, -5.1292306097055169 -17.359599419328081, -5.109 -17.361, -5.114 -17.424, -5.161 -18.106, -5.291 -18.097))) + "MULTIPOLYGON (((-4.218 -16.08, -4.216 -16.05, -2.924 -16.14, -2.926 -16.17, -4.218 -16.08)), ((-5.291 -18.097, -5.243 -17.415, -5.239 -17.352, -5.15929328747628 -17.357518157020873, -5.071 -16.091, -5.041 -16.093, -5.1292306097055169 -17.359599419328081, -5.109 -17.361, -5.114 -17.424, -5.161 -18.106, -5.291 -18.097)))" + }; + + IList items = new List(); + var factory = GeometryFactory.Default; + var reader = new WKTReader(factory); + var geoms = reader.Read(wkt[0]); + for (int i = 0; i < geoms.NumGeometries; i++) + { + var geom = geoms.GetGeometryN(i); + items.Add(geom); + } + geoms = reader.Read(wkt[1]); + for (int i = 0; i < geoms.NumGeometries; i++) + { + var geom = geoms.GetGeometryN(i); + items.Add(geom); + } + + var op = new UnaryUnionOp(items, new GeometryFactory(new PrecisionModel(100))); + var result = op.Union(); + Assert.IsNotNull(result); + } + + [Test] + public void CascadedUnionError2() + { + var geomArr = Shapefile.ReadAllGeometries(TestShapefiles.PathTo("error_union.shp")); + var geoms = new GeometryCollection(geomArr); + + var isValidOp = new IsValidOp(geoms); + Assert.That(!isValidOp.IsValid); + Assert.That(isValidOp.ValidationError.ErrorType, Is.EqualTo(TopologyValidationErrors.RingSelfIntersection)); + + Assert.That(geoms.Union, Throws.InstanceOf()); + } + } +} diff --git a/test/NetTopologySuite.IO.Esri.Test/Deprecated/Issues/Issue161.cs b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Issues/Issue161.cs new file mode 100644 index 0000000..4c1f0b5 --- /dev/null +++ b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Issues/Issue161.cs @@ -0,0 +1,29 @@ +using NetTopologySuite.Geometries; +using NUnit.Framework; +using System.IO; + +namespace NetTopologySuite.IO.Esri.Test.Deprecated.Issues +{ + class Issue161 + { + [Test(Description = + "ShapefileDataReader error 'The output char buffer is too small to contain the decoded characters'")] + public void TestIssue161() + { + //SETUP + // https://webarchive.nationalarchives.gov.uk/ukgwa/20160110200248/http://www.ons.gov.uk/ons/guide-method/geography/products/census/spatial/2011/index.html + string filePath = TestShapefiles.PathTo("LSOA_2011_EW_BGC.shp"); + if (!File.Exists(filePath)) Assert.Ignore("File '{0}' not present", filePath); + + //ATTEMPT + using (var reader = Shapefile.OpenRead(filePath)) + { + while (reader.Read(out bool deleted))//&& count++ < 3) + { + object val; + Assert.DoesNotThrow(() => val = reader.Fields["LSOA11CD"].Value); + } + } + } + } +} diff --git a/test/NetTopologySuite.IO.Esri.Test/Deprecated/Issues/Issue173Fixture.cs b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Issues/Issue173Fixture.cs new file mode 100644 index 0000000..2675521 --- /dev/null +++ b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Issues/Issue173Fixture.cs @@ -0,0 +1,42 @@ +using NetTopologySuite.Geometries; +using NetTopologySuite.Features; +using NetTopologySuite.Geometries.Implementation; +using NUnit.Framework; +using System.Collections.Generic; +using System.IO; + +namespace NetTopologySuite.IO.Esri.Test.Deprecated.Issues +{ + [NtsIssueNumber(173)] + public class Issue173Fixture + { + [Test, Description("The NetTopologySuite.IO.GeoTools class method ShapeFile.GetGeometryType(Geometry geom) will always returns ShapeGeometryType.PointZM making all shapefile geometry GeometryZM.")] + public void Test() + { + var features = new List(); + var seq = DotSpatialAffineCoordinateSequenceFactory.Instance.Create(1, Ordinates.XY); + seq.SetOrdinate(0, Ordinate.X, -91.0454); + seq.SetOrdinate(0, Ordinate.Y, 32.5907); + var pt = new GeometryFactory(DotSpatialAffineCoordinateSequenceFactory.Instance).CreatePoint(seq); + var attr = new AttributesTable(); + attr.Add("FirstName", "John"); + attr.Add("LastName", "Doe"); + features.Add(new Feature(pt, attr)); + + string fileName = Path.GetTempFileName(); + fileName = fileName.Substring(0, fileName.Length - 4); + Shapefile.WriteAllFeatures(features, fileName); + + bool isTrue = true; + using (var reader = Shapefile.OpenRead(fileName)) + isTrue = isTrue && reader.ShapeType.ToString() == "Point"; + + foreach (string file in Directory.GetFiles(Path.GetTempPath(), Path.GetFileName(fileName) + ".*")) + { + File.Delete(file); + } + + Assert.IsTrue(@isTrue); + } + } +} diff --git a/test/NetTopologySuite.IO.Esri.Test/Deprecated/Issues/Issue174.cs b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Issues/Issue174.cs new file mode 100644 index 0000000..3929188 --- /dev/null +++ b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Issues/Issue174.cs @@ -0,0 +1,36 @@ +using NetTopologySuite.IO.Esri.Shp.Readers; +using NUnit.Framework; +using System; + +namespace NetTopologySuite.IO.Esri.Test.Deprecated.Issues +{ + [NtsIssueNumber(174)] + class Issue174 + { + [Test] + public void ensure_NetTopologySuite_IO_ShapeFile_assembly_is_strongly_named() + { + // This one is asserting the same assembly as ensure_NetTopologySuite_IO_GeoTools_assembly_is_strongly_named() + AssertStronglyNamedAssembly(typeof(ShpReader)); // TODO: Remove no longer relevant test + } + + [Test] + public void ensure_NetTopologySuite_IO_GDB_assembly_is_strongly_named() + { + //AssertStronglyNamedAssembly(typeof(GDBReader)); // TODO: Remove no longer relevant test + } + + [Test] + public void ensure_NetTopologySuite_IO_GeoTools_assembly_is_strongly_named() + { + AssertStronglyNamedAssembly(typeof(Shapefile)); + } + + private void AssertStronglyNamedAssembly(Type typeFromAssemblyToCheck) + { + Assert.IsNotNull(typeFromAssemblyToCheck, "Cannot determine assembly from null"); + var assembly = typeFromAssemblyToCheck.Assembly; + StringAssert.DoesNotContain("PublicKeyToken=null", assembly.FullName, "Strongly named assembly should have a PublicKeyToken in fully qualified name"); + } + } +} diff --git a/test/NetTopologySuite.IO.Esri.Test/Deprecated/Issues/Issue178Fixture.cs b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Issues/Issue178Fixture.cs new file mode 100644 index 0000000..d1ee4d7 --- /dev/null +++ b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Issues/Issue178Fixture.cs @@ -0,0 +1,44 @@ +using NetTopologySuite.Geometries; +using NUnit.Framework; +using System; +using System.IO; + +namespace NetTopologySuite.IO.Esri.Test.Deprecated.Issues +{ + [TestFixture] + public class Issue178Fixture + { + [SetUp] + public void SetUp() + { + // Set current dir to shapefiles dir + Environment.CurrentDirectory = TestShapefiles.Directory; + } + + [Test] + public void TestCorruptedShapeFile() + { + const string filename = "christchurch-canterbury-h.shp"; + Assert.Throws(() => + { + var reader = Shapefile.OpenRead(filename); + Assert.Fail("Invalid file: code should be unreachable"); + }); + + // ensure file isn't locked + string path = Path.Combine(Environment.CurrentDirectory, filename); + bool ok = false; + using (var file = File.OpenRead(path)) + { + using (var reader = new BinaryReader(file)) + { + // read a value + int val = reader.Read(); + Console.WriteLine("read a value: " + val); + ok = true; + } + } + Assert.That(ok, Is.True); + } + } +} diff --git a/test/NetTopologySuite.IO.Esri.Test/Deprecated/Issues/Issue27Fixture.cs b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Issues/Issue27Fixture.cs new file mode 100644 index 0000000..8696c0f --- /dev/null +++ b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Issues/Issue27Fixture.cs @@ -0,0 +1,54 @@ +using NUnit.Framework; +using System.Collections.Generic; +using System.Linq; +using System.IO; +using NetTopologySuite.Features; +using NetTopologySuite.Geometries; + +namespace NetTopologySuite.IO.Esri.Test.Deprecated.Issues +{ + [TestFixture] + [ShapeFileIssueNumber(27)] + public class Issue27Fixture + { + /// + /// + /// + [Test] + public void Data_should_be_readable_after_reader_dispose() + { + var crustal_test = TestShapefiles.PathTo("crustal_test.shp"); + Assert.True(File.Exists(crustal_test)); + + var options = new ShapefileReaderOptions(); + using (var reader = Shapefile.OpenRead(crustal_test)) + { + options.MbrFilter = reader.BoundingBox; + } + + List data = null; + using (var reader = Shapefile.OpenRead(crustal_test, options)) + { + data = reader.ToList(); + } + Assert.IsNotNull(data); + Assert.IsNotEmpty(data); + + foreach (var item in data) + { + Assert.IsNotNull(item.Geometry); + Assert.IsNotNull(item.Attributes["ID_GTR"]); + } + + + var mbr = options.MbrFilter; + options.MbrFilter = new Envelope(mbr.MinX, mbr.Centre.X, mbr.MinY, mbr.Centre.Y); + List filteredData = null; + using (var reader = Shapefile.OpenRead(crustal_test, options)) + { + filteredData = reader.ToList(); + } + Assert.IsTrue(filteredData.Count < data.Count, "Filtering by MBR failed"); + } + } +} diff --git a/test/NetTopologySuite.IO.Esri.Test/Deprecated/Issues/Issue36Tests.cs b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Issues/Issue36Tests.cs new file mode 100644 index 0000000..3fd489d --- /dev/null +++ b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Issues/Issue36Tests.cs @@ -0,0 +1,75 @@ +using NetTopologySuite.Features; +using NetTopologySuite.Geometries; +using NetTopologySuite.IO.Esri.Dbf.Fields; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.IO; + +namespace NetTopologySuite.IO.Esri.Test.Deprecated.Issues +{ + /// + /// see: https://github.com/NetTopologySuite/NetTopologySuite/issues/36 + /// + [NtsIssueNumber(27)] + [TestFixture] + public class Issue36Tests + { + private int _numPassed; + + [SetUp] + public void SetUp() + { + // Set current dir to shapefiles dir + Environment.CurrentDirectory = TestShapefiles.Directory; + + _numPassed = 0; + } + + [Test] + public void ok_when_writing_shapefile_with_features() + { + var options = new ShapefileWriterOptions(ShapeType.Point); + options.AddCharacterField("X", 10); + + using (var writer = Shapefile.OpenWrite(@"issue36", options)) + { + IAttributesTable attributesTable = new AttributesTable(); + attributesTable.Add("X", "y"); + IFeature feature = new Feature(new Point(1, 2), attributesTable); + + IList features = new List(); + features.Add(feature); + + Assert.DoesNotThrow(() => writer.Write(features)); + } + + _numPassed++; + } + + [Test] + public void ok_when_writing_shapefile_with_no_features() + { + var options = new ShapefileWriterOptions(ShapeType.Point); + options.AddCharacterField("X", 10); + using (var writer = Shapefile.OpenWrite(@"issue36", options)) + { + IList features = new List(); + Assert.DoesNotThrow(() => writer.Write(features)); + } + + _numPassed++; + } + + [TearDown] + public void TearDown() + { + if (_numPassed < 2) return; + + // Clean up! + File.Delete("issue36.dbf"); + File.Delete("issue36.shp"); + File.Delete("issue36.shx"); + } + } +} diff --git a/test/NetTopologySuite.IO.Esri.Test/Deprecated/Issues/Issue46Fixture.cs b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Issues/Issue46Fixture.cs new file mode 100644 index 0000000..cc2e6bf --- /dev/null +++ b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Issues/Issue46Fixture.cs @@ -0,0 +1,37 @@ +using NUnit.Framework; +using System.IO; +using NetTopologySuite.Geometries; + +namespace NetTopologySuite.IO.Esri.Test.Deprecated.Issues +{ + /// + /// + /// + [TestFixture] + [ShapeFileIssueNumber(46)] + public class Issue46Fixture + { + [Test] + public void Invalid_data_should_be_not_read_as_default() + { + var options = new ShapefileReaderOptions(); + options.Factory = GeometryFactory.Default; + options.SkipFailures = true; + + string shp_path = TestShapefiles.PathTo("Victoria North.shp"); + Assert.True(File.Exists(shp_path)); + + var data = Shapefile.ReadAllGeometries(shp_path, options); + Assert.IsNotNull(data); + Assert.IsNotEmpty(data); + Assert.AreEqual(2, data.Length); + + Assert.IsTrue(data[0].IsEmpty); + Assert.IsInstanceOf(data[0]); + Assert.IsTrue(options.Factory.CreateMultiPolygon().EqualsExact(data[0])); + + Assert.IsFalse(data[1].IsEmpty); + Assert.IsInstanceOf(data[1]); + } + } +} diff --git a/test/NetTopologySuite.IO.Esri.Test/Deprecated/Issues/Issue4Fixture.cs b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Issues/Issue4Fixture.cs new file mode 100644 index 0000000..6005af4 --- /dev/null +++ b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Issues/Issue4Fixture.cs @@ -0,0 +1,82 @@ +using NetTopologySuite.Geometries; +using NetTopologySuite.Features; +using NetTopologySuite.Geometries.Implementation; +using NUnit.Framework; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace NetTopologySuite.IO.Esri.Test.Deprecated.Issues +{ + [TestFixture] + public class Issue4Fixture + { + private static string CreateShapefilePath() + { + string path = Path.GetTempFileName(); + path = Path.ChangeExtension(path, string.Empty).TrimEnd('.'); + return path; + } + + private static Coordinate[] CreateCoords() + { + IList coordinates = new List(); + coordinates.Add(new Coordinate(0, 0)); + coordinates.Add(new Coordinate(1, 0)); + coordinates.Add(new Coordinate(1, 1)); + coordinates.Add(new Coordinate(0, 1)); + coordinates.Add(new Coordinate(0, 0)); + return coordinates.ToArray(); + } + + private static CoordinateSequence CopyToSequence(Coordinate[] coords, CoordinateSequence sequence) + { + for (int i = 0; i < coords.Length; i++) + { + sequence.SetOrdinate(i, Ordinate.X, coords[i].X); + sequence.SetOrdinate(i, Ordinate.Y, coords[i].Y); + } + return sequence; + } + + [Test] + public void shapefile_with_empty_attributes_table_should_not_thrown_errors() + { + IFeature feature = new Feature(new Point(0, 0), new AttributesTable()); + IList features = new List { feature }; + + string path = CreateShapefilePath(); + Shapefile.WriteAllFeatures(features, path); + + Assert.That(File.Exists(Path.ChangeExtension(path, ".shp")), Is.True); + } + + [Test] + public void create_xyonly_geom_using_sequence_and_dimension_two() + { + var coords = CreateCoords(); + + var factory = CoordinateArraySequenceFactory.Instance; + var sequence = CopyToSequence(coords, factory.Create(coords.Length, 2)); + + var polygon = GeometryFactory.Default.CreatePolygon(sequence); + Assert.That(polygon, Is.Not.Null); + Assert.That(polygon.Shell, Is.Not.Null); + Assert.That(polygon.Shell.CoordinateSequence.Dimension, Is.EqualTo(2)); + } + + [Test] + public void create_xyonly_geom_using_sequence_and_ordinates_xy() + { + var coords = CreateCoords(); + + var factory = CoordinateArraySequenceFactory.Instance; + var sequence = CopyToSequence(coords, factory.Create(coords.Length, Ordinates.XY)); + + var polygon = GeometryFactory.Default.CreatePolygon(sequence); + Assert.That(polygon, Is.Not.Null); + Assert.That(polygon.Shell, Is.Not.Null); + Assert.That(polygon.Shell.CoordinateSequence.Dimension, Is.EqualTo(2)); + } + } +} diff --git a/test/NetTopologySuite.IO.Esri.Test/Deprecated/Issues/Issue56Fixture.cs b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Issues/Issue56Fixture.cs new file mode 100644 index 0000000..db7f867 --- /dev/null +++ b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Issues/Issue56Fixture.cs @@ -0,0 +1,44 @@ +using NetTopologySuite.Features; +using NetTopologySuite.Geometries; +using NUnit.Framework; +using System.Collections.Generic; +using System.IO; + +namespace NetTopologySuite.IO.Esri.Test.Deprecated.Issues +{ + [TestFixture] + [ShapeFileIssueNumber(56)] + public class Issue56Fixture + { + /// + /// + /// + [Test] + public void Data_should_be_readable_after_reader_dispose() + { + string test56 = TestShapefiles.PathTo("test56.shp"); + var factory = new GeometryFactory(); + int intValue = 56; + string key = "id"; + var attributes = new AttributesTable(); + attributes.Add(key, intValue); + var feature = new Feature(factory.CreatePoint(new Coordinate(1, 2)), attributes); + + Shapefile.WriteAllFeatures(new[] { feature }, test56); + + using (var reader = Shapefile.OpenRead(test56)) + { + + if (reader.RecordCount > 0) + { + while (reader.Read(out var deleted)) + { + var field = reader.Fields[key]; + Assert.AreEqual(intValue.GetType(), field.Value.GetType()); + } + } + } + + } + } +} diff --git a/test/NetTopologySuite.IO.Esri.Test/Deprecated/Issues/Issue60Fixture.cs b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Issues/Issue60Fixture.cs new file mode 100644 index 0000000..7c052f3 --- /dev/null +++ b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Issues/Issue60Fixture.cs @@ -0,0 +1,48 @@ +using NetTopologySuite.Features; +using NetTopologySuite.Geometries; +using NUnit.Framework; +using System.Collections.Generic; +using System.IO; + +namespace NetTopologySuite.IO.Esri.Test.Deprecated.Issues +{ + [TestFixture] + [ShapeFileIssueNumber(60)] + public class Issue60Fixture + { + /// + /// + /// + /// without fix results into System.OverflowException + [Test] + public void Feature_without_fields_should_be_written_correctly() + { + string test56 = TestShapefiles.PathTo("test60.shp"); + var factory = new GeometryFactory(); + var attributes = new AttributesTable(); + var feature = new Feature(factory.CreatePoint(new Coordinate(1, 2)), attributes); + + Shapefile.WriteAllFeatures(new[] { feature }, test56); + + using (var reader = Shapefile.OpenRead(test56)) + { + + if (reader.RecordCount > 0) + { + while (reader.Read(out var deleted)) + { + Assert.AreEqual(feature.Geometry.AsText(), reader.Geometry.AsText()); + } + } + } + } + + [Test] + public void Header_length_should_always_be_minimal_33() + { + //var header = new DbaseFileHeader(); + //Assert.AreEqual(33, header.HeaderLength); + // TODO: Remove no longer relevant test + } + } +} diff --git a/test/NetTopologySuite.IO.Esri.Test/Deprecated/Issues/Issue64Fixture.cs b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Issues/Issue64Fixture.cs new file mode 100644 index 0000000..63317c0 --- /dev/null +++ b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Issues/Issue64Fixture.cs @@ -0,0 +1,73 @@ +using NUnit.Framework; +using System.IO; +using NetTopologySuite.IO.Esri.Dbf.Fields; +using System.Collections.Generic; +using NetTopologySuite.IO.Esri.Dbf; + +namespace NetTopologySuite.IO.Esri.Test.Deprecated.Issues +{ + /// + /// + /// + [TestFixture] + [ShapeFileIssueNumber(64)] + public class Issue64Fixture + { + public void Dbase_Read_Null(DbfField field) + { + using var s = new MemoryStream(); + + var fields = new List(); + fields.Add(field); + + var values = new Dictionary() + { + { field.Name, null } + }; + + using (var writer = new DbfWriter(s, fields)) + { + writer.Write(values); + } + + s.Position = 0; + using (var reader = new DbfReader(s)) + { + foreach (var readValues in reader) + { + Assert.AreEqual(values[field.Name], readValues[field.Name]); + } + } + } + + [Test] + public void Dbase_Read_Null_Logical() + { + Dbase_Read_Null(new DbfLogicalField("field_name")); + } + + [Test] + public void Dbase_Read_Null_Date() + { + Dbase_Read_Null(new DbfDateField("field_name")); + } + + [Test] + public void Dbase_Read_Null_Float() + { + Dbase_Read_Null(new DbfFloatField("field_name")); + } + + [Test] + public void Dbase_Read_Null_Numeric() + { + Dbase_Read_Null(new DbfNumericInt32Field("field_name")); + } + + [Test] + public void Dbase_Read_Null_Character() + { + Dbase_Read_Null(new DbfCharacterField("field_name")); + } + } +} diff --git a/test/NetTopologySuite.IO.Esri.Test/Deprecated/Issues/Issue74Fixture.cs b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Issues/Issue74Fixture.cs new file mode 100644 index 0000000..60e931a --- /dev/null +++ b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Issues/Issue74Fixture.cs @@ -0,0 +1,56 @@ +using NetTopologySuite.Geometries; +using NetTopologySuite.IO.Esri.Shp.Readers; +using NetTopologySuite.IO.Esri.Shp.Writers; +using NUnit.Framework; +using System.IO; +using System.Linq; + +namespace NetTopologySuite.IO.Esri.Test.Deprecated.Issues +{ + [NtsIssueNumber(74)] + public class Issue74Fixture + { + [Test, Description("Max 'M' value is always set to double.PositiveInfinity")] + public void ShapeHandler_correctly_writes_BBOX_info() + { + var factory = GeometryFactory.Default; + var pMin = factory.CreatePoint(new CoordinateZM(-1, -1, -1, -1)); + var pMax = factory.CreatePoint(new CoordinateZM(2, 2, 2, 2)); + var coll = factory.CreateMultiPoint(new[] { pMin, pMax }); + byte[] bytes; + + using (var shpStream = new MemoryStream()) + using (var shxStream = new MemoryStream()) + { + using (var writer = new ShpMultiPointWriter(shpStream, shxStream, ShapeType.MultiPointZM)) + { + writer.Write(coll); + } + // Dispose the writer to save the header + bytes = shpStream.ToArray(); + } + + using (var reader = new ShpMultiPointReader(new MemoryStream(bytes))) + { + var collRead = reader.First(); + var bbox = collRead.EnvelopeInternal; + var pMinRead = collRead.GetGeometryN(0) as Point; + var pMaxRead = collRead.GetGeometryN(1) as Point; + + Assert.AreEqual(ShapeType.MultiPointZM, reader.ShapeType); + Assert.AreEqual(pMin.X, bbox.MinX); // MinX + Assert.AreEqual(pMin.Y, bbox.MinY); // MinY + Assert.AreEqual(pMax.X, bbox.MaxX); // MaxX + Assert.AreEqual(pMax.Y, bbox.MaxY); // MaxY + Assert.AreEqual(coll.NumGeometries, collRead.NumGeometries); + + Assert.AreEqual(pMin.Z, pMinRead.Z); // MinZ + Assert.AreEqual(pMax.Z, pMaxRead.Z); // MaxZ + + Assert.AreEqual(pMin.M, pMinRead.M); // MinM + Assert.AreEqual(pMin.M, pMinRead.M); // MaxM + } + } + } +} + diff --git a/test/NetTopologySuite.IO.Esri.Test/Deprecated/Issues/Issue79Fixture.cs b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Issues/Issue79Fixture.cs new file mode 100644 index 0000000..ee9f42c --- /dev/null +++ b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Issues/Issue79Fixture.cs @@ -0,0 +1,36 @@ +using NUnit.Framework; +using System.IO; +using NetTopologySuite.Geometries; + +namespace NetTopologySuite.IO.Esri.Test.Deprecated.Issues +{ + [TestFixture] + [ShapeFileIssueNumber(79)] + public class Issue79Fixture + { + /// + /// + /// + [Test] + public void TestReadEmptyShapefile() + { + string filePath = TestShapefiles.PathTo("__emptyShapefile.shp"); + Assert.That(File.Exists(filePath), Is.True); + + Assert.Throws(typeof(ShapefileException), () => { + using var shpReader = Shapefile.OpenRead(filePath); + + // TODO: Changed original test logic. + + // This will not be executed. + // ShpNullReader and ShpNullWriter can be implemented + // but what's the point for having Shapefile without geometries at all? + // QGIS reads this Shapefile as a table(!). + bool success = shpReader.Read(out var deleted); + Assert.That(success, Is.False); + + // Empty Shapefiles with specified ShapeType (Point, Polygon, ...) can be read without any problem. + }); + } + } +} diff --git a/test/NetTopologySuite.IO.Esri.Test/Deprecated/NtsIssueNumberAttribute.cs b/test/NetTopologySuite.IO.Esri.Test/Deprecated/NtsIssueNumberAttribute.cs new file mode 100644 index 0000000..e5c406c --- /dev/null +++ b/test/NetTopologySuite.IO.Esri.Test/Deprecated/NtsIssueNumberAttribute.cs @@ -0,0 +1,19 @@ +using System; +using NUnit.Framework; + +namespace NetTopologySuite.IO.Esri.Test.Deprecated +{ + /// + /// The issue number used in this test (or fixture) actually refers to an + /// issue on https://github.com/NetTopologySuite/NetTopologySuite, back + /// before this project was split out on its own. + /// + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)] + public sealed class NtsIssueNumberAttribute : PropertyAttribute + { + public NtsIssueNumberAttribute(int issueNumber) + : base("NetTopologySuite issue", issueNumber) + { + } + } +} diff --git a/test/NetTopologySuite.IO.Esri.Test/Deprecated/README.md b/test/NetTopologySuite.IO.Esri.Test/Deprecated/README.md new file mode 100644 index 0000000..8df0708 --- /dev/null +++ b/test/NetTopologySuite.IO.Esri.Test/Deprecated/README.md @@ -0,0 +1,12 @@ +# Deprecated tests + +Here you can find test ported from [NetTopologySuite.IO.ShapeFile +repository](https://github.com/NetTopologySuite/NetTopologySuite.IO.ShapeFile/tree/e5cea1013adc51232a2124eecc903efaa23e192d/test). +Most of those tests refer to an issue on [NetTopologySuite.IO.ShapeFile](https://github.com/NetTopologySuite/NetTopologySuite.IO.ShapeFile) repository. +There are also tests created before the *ShapeFile* project was split out on its own from the +[NetTopologySuite](https://github.com/NetTopologySuite/NetTopologySuite/commit/540a32566f1e57a611e12b44d4162fdb1515aa04) +repository. Tests related to *NetTopologySuite.IO.ShapeFile* repository have an issue number marked with `ShapeFileIssueNumberAttribute` +and tests related to *NetTopologySuite* repository have an issue number marked with `NtsIssueNumberAttribute`. + + +All new tests should have an issue number marked with `EsriIssueNumberAttribute`. diff --git a/test/NetTopologySuite.IO.Esri.Test/Deprecated/Readers/ShapeFileDataReaderTest.cs b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Readers/ShapeFileDataReaderTest.cs new file mode 100644 index 0000000..6f93db9 --- /dev/null +++ b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Readers/ShapeFileDataReaderTest.cs @@ -0,0 +1,296 @@ +using NetTopologySuite.Features; +using NetTopologySuite.Geometries; +using NetTopologySuite.Geometries.Implementation; +using NetTopologySuite.IO.Esri; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Data; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; + +namespace NetTopologySuite.IO.Esri.Test.Deprecated.Readers +{ + [TestFixture] + public class ShapeFileDataReaderTest + { + protected GeometryFactory Factory { get; private set; } + + protected WKTReader Reader { get; private set; } + + [SetUp] + public void SetUp() + { + // Set current dir to shapefiles dir + Environment.CurrentDirectory = TestShapefiles.Directory; + + Factory = new GeometryFactory(); + Reader = new WKTReader(); + } + + // see https://code.google.com/p/nettopologysuite/issues/detail?id=175 + [Test] + public void TestIssue175_ReadingShapeFileUsingShpExtension() + { + using (var reader = Shapefile.OpenRead("crustal_test.shp")) + { + int length = reader.Fields.Count; + while (reader.Read()) + { + Debug.WriteLine(reader.Fields[length - 1].Value); + } + } + } + + [Test] + public void TestReadingCrustalTestShapeFile() + { + // Original file with characters '°' in NAME field. + using (var reader = Shapefile.OpenRead("crustal_test_bugged")) + { + int length = reader.Fields.Count; + while (reader.Read()) + { + Debug.WriteLine(reader.Fields[length - 1].Value); + } + } + + // Removed NAME field characters + using (var reader = Shapefile.OpenRead("crustal_test")) + { + int length = reader.Fields.Count; + while (reader.Read()) + { + Debug.WriteLine(reader.Fields[length - 1].Value); + } + } + } + + [Test] + public void TestReadingAaaShapeFile() + { + Assert.Catch(() => + { + using (var reader = Shapefile.OpenRead("aaa")) + { + int length = reader.Fields.Count; + while (reader.Read()) + { + Debug.WriteLine(reader.Fields[length - 1].Value); + } + } + Assert.Fail(); + }); + } + + [Test] + public void TestReadingShapeFileWithNulls() + { + using (var reader = Shapefile.OpenRead("AllNulls")) + { + while (reader.Read()) + { + var geom = reader.Geometry; + Assert.IsNotNull(geom); + + object[] values = reader.Fields.GetValues(); + Assert.IsNotNull(values); + } + } + } + [Test] + public void TestReadingShapeFileZ() + { + //Use a factory with a coordinate sequence factor that can handle measure values + var factory = new GeometryFactory(DotSpatialAffineCoordinateSequenceFactory.Instance); + var options = new ShapefileReaderOptions() { Factory = factory }; + + const int distance = 500; + using (var reader = Shapefile.OpenRead("with_M", options)) + { // "" + int index = 0; + + reader.Read(); + var geom = reader.Geometry; + double firstM = geom.GetOrdinates(Ordinate.M).First(); + Assert.AreEqual(400, firstM); + + while (reader.Read()) + { + geom = reader.Geometry; + Assert.IsNotNull(geom); + Assert.IsTrue(geom.IsValid); + Debug.WriteLine(string.Format("Geom {0}: {1}", index++, geom)); + + var buff = geom.Buffer(distance); + Assert.IsNotNull(buff); + + foreach (double m in geom.GetOrdinates(Ordinate.M)) + { + Assert.IsFalse(double.IsNaN(m)); + } + } + } + } + + [Test] + public void TestReadingShapeFileAfvalbakken() + { + var factory = GeometryFactory.Default; + var options = new ShapefileReaderOptions() { Factory = factory }; + var polys = new List(); + const int distance = 500; + using (var reader = Shapefile.OpenRead("afvalbakken", options)) + { + int index = 0; + while (reader.Read()) + { + var geom = reader.Geometry as MultiPolygon; + Assert.IsNotNull(geom); + Assert.IsTrue(geom.IsValid); + Debug.WriteLine(string.Format("Geom {0}: {1}", index++, geom)); + + var buff = geom.Buffer(distance); + Assert.IsNotNull(buff); + + foreach (var pg in geom.Geometries) + { + polys.Add((Polygon)pg); + } + } + } + + var multiPolygon = factory.CreateMultiPolygon(polys.ToArray()); + Assert.IsNotNull(multiPolygon); + Assert.IsTrue(multiPolygon.IsValid); + + var multiBuffer = (MultiPolygon)multiPolygon.Buffer(distance); + Assert.IsNotNull(multiBuffer); + Assert.IsTrue(multiBuffer.IsValid); + + var attributes = new AttributesTable(); + var feature = new Feature(multiBuffer, attributes); + var features = new Feature[] { feature }; + Shapefile.WriteAllFeatures(features, @"test_buffer"); + } + + [Test] + public void TestSeptPolygones() + { + const string wktGeom9 = + "MULTIPOLYGON ( " + + "((-73.8706030450129 45.425307895968558, -73.8691180248536 45.425712901466682, -73.862907940551338 45.425949154673731, -73.862739188260548 45.423181617104319, -73.864662964375952 45.423384119853267, -73.8654729753718 45.42220285381751, -73.865979232244342 45.421730347403241, -73.866822993698463 45.42088658594912, -73.866485489116826 45.420481580450996, -73.865202971706537 45.42041407953468, -73.864629213917681 45.421325341905117, -73.864156707503412 45.422236604275611, -73.863481698340081 45.422405356566514, -73.863414197423765 45.421899099693974, -73.863414197423765 45.421190340072485, -73.8635491992564 45.4200765749531, -73.864122957045254 45.419165312582606, -73.864797966208585 45.419064061208076, -73.866316736825922 45.419030310749974, -73.867092997363727 45.419266563957194, -73.867295500112789 45.419536567622515, -73.867396751487263 45.420751584116317, -73.867092997363727 45.421527844654122, -73.866384237742238 45.422506607941045, -73.866046733160658 45.423215367562364, -73.8669579955311 45.423721624434904, -73.868881771646556 45.423485371227684, -73.8694555294353 45.423417870311312, -73.8700630376822 45.423991628100168, -73.870434292722109 45.424497884972709, -73.8706030450129 45.425307895968558), " + + "(-73.86921927622808 45.425139143677825, -73.868983023020974 45.424464134514437, -73.868544267064863 45.423991628100168, -73.86813926156691 45.423991628100168, -73.867092997363727 45.423991628100168, -73.86533797353917 45.423620373060317, -73.864966718499375 45.424059129016484, -73.864966718499375 45.424497884972709, -73.865304223081068 45.42534164642683, -73.866451738658668 45.425409147343146, -73.86756550377811 45.425274145510514, -73.86921927622808 45.425139143677825), " + + "(-73.865937695291677 45.419884197388171, -73.865599517078863 45.419585804847259, -73.86432637557175 45.4198046260438, -73.864167232883347 45.4205605538138, -73.864565089604355 45.420500875305606, -73.865937695291677 45.419884197388171)), " + + "((-73.868038010192436 45.424869140012561, -73.866620490949458 45.424869140012561, -73.865844230411653 45.424970391386921, -73.865742979037179 45.42436288314002, -73.865979232244342 45.42402537855844, -73.866687991865717 45.424295382223704, -73.867869257901532 45.424396633598121, -73.868038010192436 45.424869140012561), " + + "(-73.86744733356926 45.424703767127937, -73.867371896498639 45.42446991220919, -73.867002254852821 45.424454824795021, -73.866232796733016 45.424432193673908, -73.866345952338861 45.4246509611786, -73.86744733356926 45.424703767127937)), " + + "((-73.86512208901371 45.419923983060187, -73.864604875276427 45.420301946945074, -73.8644059469159 45.420043340076518, -73.86512208901371 45.419923983060187)))"; + + var factory = GeometryFactory.Default; //new GeometryFactory(new PrecisionModel(Math.Pow(10, 13))); + var options = new ShapefileReaderOptions() { Factory = factory }; + var wktReader = new WKTReader(factory); + var polys = new List(); + using (var reader = Shapefile.OpenRead("Sept_polygones", options)) + { + int index = 0; + while (reader.Read()) + { + var geom = reader.Geometry; + Assert.IsNotNull(geom); + Assert.IsTrue(geom.IsValid); + geom.Normalize(); + Debug.WriteLine(string.Format("Geom {0}: {1}", ++index, geom)); + polys.Add(geom); + } + } + + var expected = wktReader.Read(wktGeom9); + expected.Normalize(); + + var e1 = expected.EnvelopeInternal; + var e2 = polys[8].EnvelopeInternal; + Assert.IsTrue(e1.Equals(e2), string.Format("{0}\ndoes not match\n{1}", e1, e2)); + Assert.IsTrue(expected.EqualsTopologically(polys[8]), string.Format("{0}\ndoes not match\n{1}", expected, polys[8])); + } + + [Test] + // see https://code.google.com/p/nettopologysuite/issues/detail?id=167 + public void Issue167_EnsureAllBinaryContentIsReaded() + { + int i = 0; + foreach (Geometry geom in Shapefile.ReadAllGeometries("Issue167.shp")) + { + Assert.That(geom, Is.Not.Null, "geom null"); + Console.WriteLine("geom {0}: {1}", ++i, geom); + } + Assert.That(i, Is.EqualTo(201)); + } + + [Test()] + // see https://github.com/NetTopologySuite/NetTopologySuite/issues/112 + public void Issue_GH_112_ReadingShapeFiles() + { + // TODO: Changed original test logic. + + // The Volume2.shp file is broken. Second record has RecordNumber=-252173723 + // and ContentLength=1870401424 (where ShpStream.Length=91332). + // The original test was passed only because of every-exception-catching code + // in the ShapefileEnumerator class: + // https://github.com/NetTopologySuite/NetTopologySuite.IO.ShapeFile/blob/75b6bcdde2f3aa947bf731903f58b1b6b4a80e01/src/NetTopologySuite.IO.ShapeFile/ShapefileReader.cs#L134-L139 + + int i = 0; + using var stream = File.OpenRead("Volume2.shp"); + using var reader = Shp.Shp.OpenRead(stream, new ShapefileReaderOptions()); + var geom = reader.First(); + Assert.That(geom, Is.Not.Null, "geom null"); + Console.WriteLine("geom {0}: {1}", ++i, geom); + + } + + [Test()] + // see https://github.com/NetTopologySuite/NetTopologySuite/issues/115 + public void Issue_GH_115_CreateDataTable() + { + // TODO: Remove no longer relevant test (Creating System.Data.DataTable is not implemented) + + //if (!File.Exists("Matt.shp")) + // throw new IgnoreException("File not found"); + + //DataTable dt = null; + //Assert.DoesNotThrow(() => dt = + //Shapefile.CreateDataTable("Volume2", "Matt", GeometryFactory.Default)); + + } + + [Test] + [NtsIssueNumber(39)] // see also #52 for input data + public void EnsureSpecifiedEncodingIsUsed() + { + /* + * https://docs.microsoft.com/en-us/dotnet/api/system.text.encoding?redirectedfrom=MSDN&view=net-5.0 + * 936 gb2312 Chinese Simplified (GB2312) + */ + + var encoding = CodePagesEncodingProvider.Instance.GetEncoding(936); // DbaseEncodingUtility.GetEncodingForCodePageIdentifier(936); + Assert.IsNotNull(encoding); + + var options = new ShapefileReaderOptions() + { + Factory = Factory, + Encoding = encoding + }; + using var reader = Shapefile.OpenRead("chinese_encoding.shp", options); + Assert.AreEqual(encoding, reader.Encoding); + + Console.WriteLine(string.Join(",", reader.Fields.Select(f => f.Name))); + int length = reader.Fields.Count; + while (reader.Read()) + { + Console.WriteLine(string.Join(",", reader.Fields.GetValues())); + Assert.AreEqual("安徽", reader.Fields[1].Value?.ToString()); + } + } + } +} diff --git a/test/NetTopologySuite.IO.Esri.Test/Deprecated/Readers/ShapeRead.cs b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Readers/ShapeRead.cs new file mode 100644 index 0000000..16a4dc2 --- /dev/null +++ b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Readers/ShapeRead.cs @@ -0,0 +1,155 @@ +using NetTopologySuite.Features; +using NetTopologySuite.Geometries; +using System; +using System.IO; +using System.Linq; + +namespace NetTopologySuite.IO.Esri.Test.Deprecated.Readers +{ + /// + /// + /// + public class ShapeRead + { + protected GeometryFactory Factory { get; private set; } + + protected WKTReader Reader { get; private set; } + + /// + /// + /// + public ShapeRead() + { + // Set current dir to shapefiles dir + Environment.CurrentDirectory = TestShapefiles.Directory; + + Factory = new GeometryFactory(); + Reader = new WKTReader(); + } + + /// + /// + /// + public void Start() + { + //TestBugMultipolygonHShuntao(); + //TestBugCimino(); + + //// Bug with a.shp and b.shp and intersection + //GeometryCollection aColl = ReadShape("a.shp"); + //GeometryCollection bColl = ReadShape("b.shp"); + //Geometry result = aColl.Intersection(bColl); + + //// Point shapefile + //TestShapeReadWrite("tnp_pts.shp", "Test_tnp_pts.shp"); + + //// Arc shapefile + TestShapeReadWrite("tnp_arc.shp", "arc.shp"); + TestShapeReadWrite("Stato_Fatto.shp", "Test_Stato_Fatto.shp"); + TestShapeReadWrite("Stato_Progetto.shp", "Test_Stato_Progetto.shp"); + TestShapeReadWrite("Zone_ISTAT.shp", "Test_Zone_ISTAT.shp"); + TestShapeReadWrite("Strade.shp", "Test_Strade.shp"); + + //// Polygon shapefile + //TestShapeReadWrite("tnp_pol.shp", "Test_tnp_pol.shp"); + + //// MultiPoint shapefile + //TestShapeReadWrite("tnp_multiPoint.shp", "Test_tnp_multiPoint.shp"); + + // TestShapeReadWrite("a.shp", "Test_a.shp"); + // TestShapeReadWrite("b.shp", "Test_b.shp"); + } + + //private void TestBugMultipolygonHShuntao() + //{ + // GeometryCollection gc1 = null; + // GeometryCollection gc2 = null; + // string file = "BJmultipolygon.shp"; + // if (!File.Exists(file)) + // throw new FileNotFoundException(); + + // // Test with Default ShapefileReader + // try + // { + // var sfr = new ShapefileReader(file); + // gc1 = sfr.ReadAll(); + // } + // catch (Exception ex) + // { + // throw ex; + // } + + // //// Test with MyShapefileReader (only for debug purpose!) + // //try + // //{ + // // MyShapeFileReader reader = new MyShapeFileReader(); + // // gc2 = reader.Read(file); + // //} + // //catch (Exception ex) + // //{ + // // throw ex; + // //} + + // //// Check for equality + // //if (!gc1.EqualsExact(gc2)) + // // throw new TopologyException("Both geometries must be equals!"); + //} + + //private void TestBugCimino() + //{ + // try + // { + // string file = "countryCopy.shp"; + // if (!File.Exists(file)) + // throw new FileNotFoundException(); + + // var sfr = new ShapefileReader(file); + + // var gc = sfr.ReadAll(); + // for (int i = 0; i < gc.NumGeometries; i++) + // Console.WriteLine(i + " " + gc.Geometries[i].Envelope); + + // // IsValidOp.CheckShellsNotNested molto lento nell'analisi di J == 7 (Poligono con 11600 punti) + // string write = Path.Combine(Path.GetTempPath(), "copy_countryCopy"); + // var sfw = new ShapefileWriter(gc.Factory); + // sfw.Write(write, gc); + // Console.WriteLine("Write Complete!"); + // } + // catch (Exception ex) + // { + // throw ex; + // } + //} + + private static GeometryCollection ReadShape(string shapepath) + { + if (!File.Exists(shapepath)) + throw new ArgumentException("File " + shapepath + " not found!"); + + var geometries = Shapefile.ReadAllGeometries(shapepath); + var factory = GeometryFactory.Default; + return factory.CreateGeometryCollection(geometries); + } + + private static void WriteShape(GeometryCollection geometries, string shapepath) + { + if (File.Exists(shapepath)) + File.Delete(shapepath); + var attributes = new AttributesTable(); + attributes.Add("attr_name", "attr_value"); + var features = geometries.Select(g => new Feature(g, attributes)); + Shapefile.WriteAllFeatures(features, shapepath); + } + + private static void TestShapeReadWrite(string shapepath, string outputpath) + { + var collection = ReadShape(shapepath); + WriteShape(collection, outputpath); + var testcollection = ReadShape(outputpath); + + if (!collection.EqualsExact(testcollection)) + throw new ArgumentException("Geometries are not equals"); + Console.WriteLine("TEST OK!"); + } + } +} diff --git a/test/NetTopologySuite.IO.Esri.Test/Deprecated/ShapeFile.Extended/DbaseReaderTests.cs b/test/NetTopologySuite.IO.Esri.Test/Deprecated/ShapeFile.Extended/DbaseReaderTests.cs new file mode 100644 index 0000000..f6bd045 --- /dev/null +++ b/test/NetTopologySuite.IO.Esri.Test/Deprecated/ShapeFile.Extended/DbaseReaderTests.cs @@ -0,0 +1,292 @@ +using System; +using System.IO; +using System.Linq; +using NetTopologySuite.Features; +using NetTopologySuite.IO.Esri.Dbf; +using NUnit.Framework; + +namespace NetTopologySuite.IO.Esri.Test.Deprecated.ShapeFile.Extended +{ + /// + /// Summary description for DbfFileReaderTests + /// + [TestFixture] + public class DbfReaderTests + { + private static readonly DateTime DATE_SAVED_IN_DBF = new DateTime(1990, 1, 1); + + private DbfReader m_Reader; + private TempFileWriter m_TmpFile; + + [Test] + public void Ctor_SendNullPath_ShouldThrowException() + { + // Act. + Assert.Catch(() => + { + m_Reader = new DbfReader((string)null); + }); + } + + [Test] + public void Ctor_SendEmptyString_ShouldThrowException() + { + // Act. + Assert.Catch(() => + { + m_Reader = new DbfReader(string.Empty); + }); + } + + [Test] + public void Ctor_SendWhitespaceString_ShouldThrowException() + { + // Act. + Assert.Catch(() => + { + m_Reader = new DbfReader(" \t "); + }); + } + + [Test] + public void Ctor_SendNonExistantPath_ShouldThrowException() + { + // Act. + Assert.Catch(() => + { + m_Reader = new DbfReader(@"C:\this\is\sheker\path\should\never\exist\on\ur\pc"); + }); + } + + [Test] + public void Ctor_SendValidParameters_ShouldReturnNotNull() + { + // Arrange + m_TmpFile = new TempFileWriter(".dbf", DbfFiles.Read("line_ed50_geo")); + + // Act. + m_Reader = new DbfReader(m_TmpFile.Path); + + // Assert. + Assert.IsNotNull(m_Reader); + } + + [Test] + public void ReadEntry_SendNegativeIndex_ShouldThrowException() + { + // Arrange + m_TmpFile = new TempFileWriter(".dbf", DbfFiles.Read("point_ed50_geo")); + m_Reader = new DbfReader(m_TmpFile.Path); + + // Act. + Assert.Catch(() => + { + m_Reader.ReadEntry(-1); + }); + } + + [Test] + public void ReadEntry_SendOutOfBoundIndex_ShouldThrowException() + { + // Arrange + m_TmpFile = new TempFileWriter(".dbf", DbfFiles.Read("point_ed50_geo")); + m_Reader = new DbfReader(m_TmpFile.Path); + + // Act. + Assert.Catch(() => + { + m_Reader.ReadEntry(3); + }); + } + + [Test] + public void ReadEntry_TryReadAfterDisposed_ShouldThrowException() + { + // Arrange + m_TmpFile = new TempFileWriter(".dbf", DbfFiles.Read("point_ed50_geo")); + m_Reader = new DbfReader(m_TmpFile.Path); + + m_Reader.Dispose(); + + // Act. + Assert.Catch(() => + { + m_Reader.ReadEntry(1); + }); + } + + [Test] + public void ReadEntry_ReadEntryValues_ShoudReturnCorrectValues() + { + // Arrange + m_TmpFile = new TempFileWriter(".dbf", DbfFiles.Read("point_ed50_geo")); + m_Reader = new DbfReader(m_TmpFile.Path); + + var expectedTable = new + { + Ids = new int[] + { + 3, 2, 1 + }, + Strings = new string[] + { + "str3", "str2", "str1" + }, + WholeNums = new int[] + { + 3, 2, 1 + }, + DecNums = new int[] + { + 3, 2, 1 + }, + }; + + // Act. + var results = new IAttributesTable[] + { + m_Reader.ReadEntry(0), + m_Reader.ReadEntry(1), + m_Reader.ReadEntry(2) + }; + + // Assert. + int currResIndex = 0; + foreach (var res in results) + { + object id = res["id"]; + object str = res["str"]; + object wholeNum = res["wholeNum"]; + object decNum = res["decNum"]; + object date = res["dt"]; + + Assert.IsNotNull(id); + Assert.IsNotNull(str); + Assert.IsNotNull(wholeNum); + Assert.IsNotNull(decNum); + Assert.IsNotNull(date); + + Assert.IsInstanceOf(id); + Assert.IsInstanceOf(str); + Assert.IsInstanceOf(wholeNum); + Assert.IsInstanceOf(decNum); + Assert.IsInstanceOf(date); + + Assert.AreEqual(id, expectedTable.Ids[currResIndex]); + Assert.AreEqual(str, expectedTable.Strings[currResIndex]); + Assert.AreEqual(wholeNum, expectedTable.WholeNums[currResIndex]); + Assert.AreEqual(decNum, expectedTable.DecNums[currResIndex]); + Assert.AreEqual(date, DATE_SAVED_IN_DBF); + + currResIndex++; + } + } + + [Test] + public void ReadEntry_ReadNonExistantKeyFromEntry_ShoudReturnCorrectValues() + { + // Arrange + m_TmpFile = new TempFileWriter(".dbf", DbfFiles.Read("point_ed50_geo")); + m_Reader = new DbfReader(m_TmpFile.Path); + + var results = m_Reader.ReadEntry(0); + + // Act. + Assert.Catch(() => + { + object a = results["a"]; + }); + } + + [Test] + public void ForEachIteration_ReadEntryValues_ShoudReturnCorrectValues() + { + // Arrange + m_TmpFile = new TempFileWriter(".dbf", DbfFiles.Read("point_ed50_geo")); + m_Reader = new DbfReader(m_TmpFile.Path); + + var expectedTable = new + { + Ids = new int[] + { + 3, 2, 1 + }, + Strings = new string[] + { + "str3", "str2", "str1" + }, + WholeNums = new int[] + { + 3, 2, 1 + }, + DecNums = new int[] + { + 3, 2, 1 + }, + }; + + // Act. + var results = m_Reader.ToArray(); + + Assert.AreEqual(results.Length, 3); + + // Assert. + int currResIndex = 0; + foreach (var res in results) + { + object id = res["id"]; + object str = res["str"]; + object wholeNum = res["wholeNum"]; + object decNum = res["decNum"]; + object date = res["dt"]; + + Assert.IsNotNull(id); + Assert.IsNotNull(str); + Assert.IsNotNull(wholeNum); + Assert.IsNotNull(decNum); + Assert.IsNotNull(date); + + Assert.IsInstanceOf(id); + Assert.IsInstanceOf(str); + Assert.IsInstanceOf(wholeNum); + Assert.IsInstanceOf(decNum); + Assert.IsInstanceOf(date); + + Assert.AreEqual(id, expectedTable.Ids[currResIndex]); + Assert.AreEqual(str, expectedTable.Strings[currResIndex]); + Assert.AreEqual(wholeNum, expectedTable.WholeNums[currResIndex]); + Assert.AreEqual(decNum, expectedTable.DecNums[currResIndex]); + Assert.AreEqual(date, DATE_SAVED_IN_DBF); + + currResIndex++; + } + } + + [TearDown] + public void TestCleanup() + { + if (m_Reader != null) + { + m_Reader.Dispose(); + m_Reader = null; + } + + if (m_TmpFile != null) + { + m_TmpFile.Dispose(); + m_TmpFile = null; + } + } + } + + static class DbfFiles + { + public static byte[] Read(string filename) + { + string file = Path.ChangeExtension(filename, ".dbf"); + string path = TestShapefiles.PathTo(file); + Assert.That(File.Exists(path), Is.True); + return File.ReadAllBytes(path); + } + } +} diff --git a/test/NetTopologySuite.IO.Esri.Test/Deprecated/ShapeFile.Extended/HelperMethods.cs b/test/NetTopologySuite.IO.Esri.Test/Deprecated/ShapeFile.Extended/HelperMethods.cs new file mode 100644 index 0000000..0aedaaf --- /dev/null +++ b/test/NetTopologySuite.IO.Esri.Test/Deprecated/ShapeFile.Extended/HelperMethods.cs @@ -0,0 +1,76 @@ +using System; +using NetTopologySuite.Geometries; +using Assert = NUnit.Framework.Assert; + +namespace NetTopologySuite.IO.Esri.Test.Deprecated.ShapeFile.Extended +{ + public static class HelperMethods + { + private static readonly double REQUIRED_PRECISION = Math.Pow(10, -9); + + public static void AssertEnvelopesEqual(Envelope env1, Envelope env2) + { + AssertEnvelopesEqual(env1, env2, REQUIRED_PRECISION); + } + + public static void AssertEnvelopesEqual(Envelope env1, Envelope env2, double requiredPrecision, string errorMessage = "") + { + AssertDoubleValuesEqual(env1.MaxX, env2.MaxX, requiredPrecision, errorMessage); + AssertDoubleValuesEqual(env1.MaxY, env2.MaxY, requiredPrecision, errorMessage); + AssertDoubleValuesEqual(env1.MinX, env2.MinX, requiredPrecision, errorMessage); + AssertDoubleValuesEqual(env1.MinY, env2.MinY, requiredPrecision, errorMessage); + } + + public static void AssertPolygonsEqual(Polygon poly1, Polygon poly2) + { + Assert.IsNotNull(poly1); + Assert.IsNotNull(poly2); + + LineString line1 = poly1.Shell; + LineString line2 = poly2.Shell; + + Assert.AreEqual(line1.Coordinates.Length, line2.Coordinates.Length, "Number of coordinates between polygons doesn't match"); + + for (int i = 0; i < line2.Coordinates.Length; i++) + { + AssertCoordinatesEqual(line2.Coordinates[i], line1.Coordinates[i]); + } + } + + public static void AssertPolygonsEqual(MultiPolygon multiPoly1, Polygon poly2) + { + if (multiPoly1.IsEmpty && poly2.IsEmpty) + { + return; + } + + Assert.AreEqual(multiPoly1.NumGeometries, 1); + var poly1 = multiPoly1.GetGeometryN(0) as Polygon; + Assert.IsNotNull(poly1); + AssertPolygonsEqual(poly1, poly2); + } + + public static void AssertCoordinatesEqual(Coordinate coord1, Coordinate coord2) + { + AssertDoubleValuesEqual(coord1.X, coord2.X); + AssertDoubleValuesEqual(coord1.Y, coord2.Y); + } + + public static void AssertDoubleValuesEqual(double num1, double num2) + { + AssertDoubleValuesEqual(num1, num2, REQUIRED_PRECISION); + } + + public static void AssertDoubleValuesEqual(double num1, double num2, double requiredPrecision, string errorMessage = "") + { + if (string.IsNullOrWhiteSpace(errorMessage)) + { + Assert.AreEqual(num1, num2, requiredPrecision); + } + else + { + Assert.AreEqual(num1, num2, requiredPrecision, errorMessage); + } + } + } +} diff --git a/test/NetTopologySuite.IO.Esri.Test/Deprecated/ShapeFile.Extended/ShapeDataReaderTests.cs b/test/NetTopologySuite.IO.Esri.Test/Deprecated/ShapeFile.Extended/ShapeDataReaderTests.cs new file mode 100644 index 0000000..1ff0f00 --- /dev/null +++ b/test/NetTopologySuite.IO.Esri.Test/Deprecated/ShapeFile.Extended/ShapeDataReaderTests.cs @@ -0,0 +1,620 @@ +using System; +using System.IO; +using System.Linq; +using NetTopologySuite.Geometries; +using NetTopologySuite.Index.Strtree; +using NetTopologySuite.IO.Esri.Shapefiles.Readers; +using NetTopologySuite.IO.Esri; +using NetTopologySuite.IO.Esri.Test; +using NUnit.Framework; +using NetTopologySuite.Features; + +namespace NetTopologySuite.IO.Esri.Test.Deprecated.ShapeFile.Extended +{ + [TestFixture] + public class ShapeDataReaderTests + { + [Test] + public void Ctor_SendNullPath_ShouldThrowException() + { + // Act. + Assert.Catch(() => + { + using var shapefileReader = Shapefile.OpenRead((string)null); + }); + } + + [Test] + public void Ctor_SendEmptyPath_ShouldThrowException() + { + // Act. + Assert.Catch(() => + { + using var shapefileReader = Shapefile.OpenRead(string.Empty); + }); + } + + [Test] + public void Ctor_SendWhitespacePath_ShouldThrowException() + { + // Act. + Assert.Catch(() => + { + using var shapefileReader = Shapefile.OpenRead(" \t "); + }); + } + + [Test] + public void Ctor_SendNonExistantFilePath_ShouldThrowException() + { + // Act. + Assert.Catch(() => + { + using var shapefileReader = Shapefile.OpenRead(@"C:\this\is\sheker\path\should\never\exist\on\ur\pc"); + }); + } + + [Test] + public void Ctor_SendShpWithNoDbf_ShouldThrowException() + { + // Arrange. + using var tempShp = new TempFileWriter(".shp", "UnifiedChecksMaterial"); + + // Act. + Assert.Catch(() => + { + using var shapefileReader = Shapefile.OpenRead(tempShp.Path); + }); + } + + [Test] + public void Ctor_SendNullSpatialIndex_ShouldNotThrowException() + { + // TODO: Remove no longer relevant test + // IDX file is not used during reading at all. + // It is stored during writing for compability reasons. + /* + // Arrange. + m_TempFiles = new TempFileWriter[] + { + new TempFileWriter(".shp", ShpFiles.Read("UnifiedChecksMaterial")), + new TempFileWriter(".dbf", DbfFiles.Read("UnifiedChecksMaterial")), + }; + + // Act. + Assert.Catch(() => + { + m_shapeDataReader = new ShapeDataReader(m_TempFiles[0].Path, null); + }); + */ + } + + [Test] + public void Ctor_SendNullGeometryFactory_ShouldThrowException() + { + // TODO: Remove no longer relevant test + // Geometry.DefaultFactory is used when provided factory is null. + /* + // Arrange. + m_TempFiles = new TempFileWriter[] + { + new TempFileWriter(".shp", ShpFiles.Read("UnifiedChecksMaterial")), + new TempFileWriter(".dbf", DbfFiles.Read("UnifiedChecksMaterial")), + }; + + // Act. + Assert.Catch(() => + { + m_shapeDataReader = new ShapeDataReader(m_TempFiles[0].Path, new STRtree(), null); + }); + */ + } + + [Test] + public void Ctor_SendShpWithNoPrj_ShouldReturnNotNull() + { + // Arrange. + using var tempShp = new TempFileWriter(".shp", "UnifiedChecksMaterial"); + using var tempDbf = new TempFileWriter(".dbf", "UnifiedChecksMaterial"); + + // Act. + using var shapefileReader = Shapefile.OpenRead(tempShp.Path); + Assert.IsNotNull(shapefileReader); + } + + [Test] + public void Ctor_SetAsyncIndexToTrue_ShouldReturnNotNull() + { + // TODO: Remove no longer relevant test + // STRtree is not supported + /* + // Arrange. + m_TempFiles = new TempFileWriter[] + { + new TempFileWriter(".shp", ShpFiles.Read("UnifiedChecksMaterial")), + new TempFileWriter(".dbf", DbfFiles.Read("UnifiedChecksMaterial")) + }; + + // Act. + m_shapeDataReader = new ShapeDataReader(m_TempFiles[0].Path, new STRtree(), new GeometryFactory(), true); + + // Assert. + Assert.IsNotNull(m_shapeDataReader); + */ + } + + [Test] + public void Ctor_SetAsyncIndexToFalse_ShouldReturnNotNull() + { + // TODO: Remove no longer relevant test + // STRtree is not supported + /* + // Arrange. + m_TempFiles = new TempFileWriter[] + { + new TempFileWriter(".shp", ShpFiles.Read("UnifiedChecksMaterial")), + new TempFileWriter(".dbf", DbfFiles.Read("UnifiedChecksMaterial")), + }; + + // Act. + m_shapeDataReader = new ShapeDataReader(m_TempFiles[0].Path, new STRtree(), new GeometryFactory(), false); + + // Assert. + Assert.IsNotNull(m_shapeDataReader); + */ + } + + [Test] + public void ShapeFileBounds_ReadPointED50Geo_ShouldReturnCorrectEnvelope() + { + // Arrange. + var expectedMBR = new Envelope(34.14526022208882, 34.28293070132935, 31.85116738930965, 31.92063218020455); + + // Act. + using var shapefileReader = Shapefile.OpenRead(TestShapefiles.PathTo("point_ed50_geo")); + + // Assert. + HelperMethods.AssertEnvelopesEqual(expectedMBR, shapefileReader.BoundingBox); + } + + [Test] + public void ReadByGeoFilter_ReadAllInBounds_ShouldReturnAllShapesAndCorrectDbfData() + { + // Arrange. + using var tempShp = new TempFileWriter(".shp", "UnifiedChecksMaterial"); + using var tempDbf = new TempFileWriter(".dbf", "UnifiedChecksMaterial"); + + Polygon[] expectedResult = new Polygon[] + { + new Polygon(new LinearRing(new Coordinate[] + { + new Coordinate(-0.815656565656566, -0.439393939393939), + new Coordinate(-0.353535353535354, -0.795454545454545), + new Coordinate(-0.888888888888889,-0.929292929292929), + new Coordinate(-1.151515151515152, -0.419191919191919), + new Coordinate(-0.815656565656566,-0.439393939393939), + })), + new Polygon(new LinearRing(new Coordinate[] + { + new Coordinate(0.068181818181818,0.578282828282829), + new Coordinate(0.421717171717172,0.070707070707071), + new Coordinate(-0.457070707070707,0.080808080808081), + new Coordinate(0.068181818181818,0.578282828282829), + })) + }; + + string[] expectedShapeMetadata = new string[] { "Rectangle", "Triangle" }; + + using var boundingBoxReader = Shapefile.OpenRead(tempShp.Path); + var options = new ShapefileReaderOptions() + { + MbrFilter = boundingBoxReader.BoundingBox + }; + + // Act. + var results = Shapefile.ReadAllFeatures(tempShp.Path, options); + + // Assert. + Assert.IsNotNull(results); + + int currIndex = 0; + foreach (var result in results) + { + Assert.IsNotNull(result); + Assert.IsNotNull(result.Attributes); + + Polygon resultPolygon = GetPolygon(result.Geometry); + HelperMethods.AssertPolygonsEqual(resultPolygon, expectedResult[currIndex]); + + object shapeNameData = result.Attributes["ShapeName"]; + Assert.IsInstanceOf(shapeNameData); + + Assert.AreEqual((string)shapeNameData, expectedShapeMetadata[currIndex]); + currIndex++; + } + } + + [Test] + public void ReadByGeoFilter_ReadWithWholeTriangleInBounds_ShouldReturnTriangle() + { + var boundsWithWholeTriangle = new Envelope(-0.62331, 0.63774, -0.02304, 0.76942); + + // Arrange. + using var tempShp = new TempFileWriter(".shp", "UnifiedChecksMaterial"); + using var tempDbf = new TempFileWriter(".dbf", "UnifiedChecksMaterial"); + + var expectedTriangle = new Polygon(new LinearRing(new Coordinate[] + { + new Coordinate(0.068181818181818,0.578282828282829), + new Coordinate(0.421717171717172,0.070707070707071), + new Coordinate(-0.457070707070707,0.080808080808081), + new Coordinate(0.068181818181818,0.578282828282829), + })); + + string expectedShapeMetadata = "Triangle"; + + var options = new ShapefileReaderOptions() + { + MbrFilter = boundsWithWholeTriangle + }; + + // Act. + using var shapefileReader = Shapefile.OpenRead(tempShp.Path, options); + var result = shapefileReader.Single(); + + // Assert. + Assert.IsNotNull(result); + Assert.IsNotNull(result.Attributes); + + Polygon resultPolygon = GetPolygon(result.Geometry); + HelperMethods.AssertPolygonsEqual(resultPolygon, expectedTriangle); + + object shapeNameData = result.Attributes["ShapeName"]; + Assert.IsInstanceOf(shapeNameData); + + Assert.AreEqual((string)shapeNameData, expectedShapeMetadata); + } + + [Test] + public void ReadByGeoFilter_ReadWithWholeRectangleInBounds_ShouldReturnRectangle() + { + var boundsWithWholeTriangle = new Envelope(-1.39510, -0.12716, -1.13938, -0.22977); + + // Arrange. + using var tempShp = new TempFileWriter(".shp", "UnifiedChecksMaterial"); + using var tempDbf = new TempFileWriter(".dbf", "UnifiedChecksMaterial"); + + var expectedTriangle = new Polygon(new LinearRing(new Coordinate[] + { + new Coordinate(-0.815656565656566, -0.439393939393939), + new Coordinate(-0.353535353535354, -0.795454545454545), + new Coordinate(-0.888888888888889,-0.929292929292929), + new Coordinate(-1.151515151515152, -0.419191919191919), + new Coordinate(-0.815656565656566,-0.439393939393939), + })); + + string expectedShapeMetadata = "Rectangle"; + + var options = new ShapefileReaderOptions() + { + MbrFilter = boundsWithWholeTriangle + }; + + // Act. + using var shapefileReader = Shapefile.OpenRead(tempShp.Path, options); + var result = shapefileReader.Single(); + + // Assert. + Assert.IsNotNull(result); + Assert.IsNotNull(result.Attributes); + + Polygon resultPolygon = GetPolygon(result.Geometry); + HelperMethods.AssertPolygonsEqual(resultPolygon, expectedTriangle); + + object shapeNameData = result.Attributes["ShapeName"]; + Assert.IsInstanceOf(shapeNameData); + + Assert.AreEqual((string)shapeNameData, expectedShapeMetadata); + } + + [Test] + public void ReadByGeoFilter_ReadWithWholeRectangleInBoundsAndFilterByGeometryOption_ShouldReturnRectangle() + { + var boundsWithWholeTriangle = new Envelope(-1.39510, -0.12716, -1.13938, -0.22977); + + // Arrange. + using var tempShp = new TempFileWriter(".shp", "UnifiedChecksMaterial"); + using var tempDbf = new TempFileWriter(".dbf", "UnifiedChecksMaterial"); + + var expectedTriangle = new Polygon(new LinearRing(new Coordinate[] + { + new Coordinate(-0.815656565656566, -0.439393939393939), + new Coordinate(-0.353535353535354, -0.795454545454545), + new Coordinate(-0.888888888888889,-0.929292929292929), + new Coordinate(-1.151515151515152, -0.419191919191919), + new Coordinate(-0.815656565656566,-0.439393939393939), + })); + + string expectedShapeMetadata = "Rectangle"; + + var options = new ShapefileReaderOptions() + { + MbrFilter = boundsWithWholeTriangle, + MbrFilterOption = MbrFilterOption.FilterByGeometry + }; + + // Act. + using var shapefileReader = Shapefile.OpenRead(tempShp.Path, options); + var result = shapefileReader.Single(); + + // Assert. + Assert.IsNotNull(result); + Assert.IsNotNull(result.Attributes); + + Polygon resultPolygon = GetPolygon(result.Geometry); + HelperMethods.AssertPolygonsEqual(resultPolygon, expectedTriangle); + + object shapeNameData = result.Attributes["ShapeName"]; + Assert.IsInstanceOf(shapeNameData); + + Assert.AreEqual((string)shapeNameData, expectedShapeMetadata); + } + + [Test] + public void ReadByGeoFilter_ReadWithRectanglePartiallyInBounds_ShouldReturnRectangle() + { + var boundsWithWholeTriangle = new Envelope(-0.93340, -0.38902, -0.73281, -0.29179); + + // Arrange. + using var tempShp = new TempFileWriter(".shp", "UnifiedChecksMaterial"); + using var tempDbf = new TempFileWriter(".dbf", "UnifiedChecksMaterial"); + + var expectedTriangle = new Polygon(new LinearRing(new Coordinate[] + { + new Coordinate(-0.815656565656566, -0.439393939393939), + new Coordinate(-0.353535353535354, -0.795454545454545), + new Coordinate(-0.888888888888889,-0.929292929292929), + new Coordinate(-1.151515151515152, -0.419191919191919), + new Coordinate(-0.815656565656566,-0.439393939393939), + })); + + string expectedShapeMetadata = "Rectangle"; + + var options = new ShapefileReaderOptions() + { + MbrFilter = boundsWithWholeTriangle + }; + + // Act. + using var shapefileReader = Shapefile.OpenRead(tempShp.Path, options); + var result = shapefileReader.Single(); + + // Assert. + Assert.IsNotNull(result); + Assert.IsNotNull(result.Attributes); + + Polygon resultPolygon = GetPolygon(result.Geometry); + HelperMethods.AssertPolygonsEqual(resultPolygon, expectedTriangle); + + object shapeNameData = result.Attributes["ShapeName"]; + Assert.IsInstanceOf(shapeNameData); + + Assert.AreEqual((string)shapeNameData, expectedShapeMetadata); + } + + [Test] + public void ReadByGeoFilter_ReadWithRectanglePartiallyInBoundsAndFilterByGeometryOption_ShouldReturnRectangle() + { + var boundsWithWholeTriangle = new Envelope(-0.93340, -0.38902, -0.73281, -0.29179); + + // Arrange. + using var tempShp = new TempFileWriter(".shp", "UnifiedChecksMaterial"); + using var tempDbf = new TempFileWriter(".dbf", "UnifiedChecksMaterial"); + + var expectedTriangle = new Polygon(new LinearRing(new Coordinate[] + { + new Coordinate(-0.815656565656566, -0.439393939393939), + new Coordinate(-0.353535353535354, -0.795454545454545), + new Coordinate(-0.888888888888889,-0.929292929292929), + new Coordinate(-1.151515151515152, -0.419191919191919), + new Coordinate(-0.815656565656566,-0.439393939393939), + })); + + string expectedShapeMetadata = "Rectangle"; + + var options = new ShapefileReaderOptions() + { + MbrFilter = boundsWithWholeTriangle, + MbrFilterOption = MbrFilterOption.FilterByGeometry + }; + + // Act. + using var shapefileReader = Shapefile.OpenRead(tempShp.Path, options); + var result = shapefileReader.Single(); + + // Assert. + Assert.IsNotNull(result); + Assert.IsNotNull(result.Attributes); + + Polygon resultPolygon = GetPolygon(result.Geometry); + HelperMethods.AssertPolygonsEqual(resultPolygon, expectedTriangle); + + object shapeNameData = result.Attributes["ShapeName"]; + Assert.IsInstanceOf(shapeNameData); + + Assert.AreEqual((string)shapeNameData, expectedShapeMetadata); + } + + [Test] + public void ReadByGeoFilter_ReadWithRectangleMBRPartiallyInBoundsAndFilterByGeometryOption_ShouldReturnNoGeometries() + { + var boundsWithWholeTriangle = new Envelope(-1.17459, -1.00231, -1.09803, -0.80861); + + // Arrange. + using var tempShp = new TempFileWriter(".shp", "UnifiedChecksMaterial"); + using var tempDbf = new TempFileWriter(".dbf", "UnifiedChecksMaterial"); + + var options = new ShapefileReaderOptions() + { + MbrFilter = boundsWithWholeTriangle, + MbrFilterOption = MbrFilterOption.FilterByGeometry + }; + + // Act. + var results = Shapefile.ReadAllFeatures(tempShp.Path, options); + + // Assert. + Assert.IsNotNull(results); + Assert.IsFalse(results.Any()); + } + + // I give it as a parameter a rectangle that partially intersects only with the MBR of the + // shape, and doesn't intersect with the shape itself at all. + [Test] + public void ReadByGeoFilter_ReadWithRectangleMBRPartiallyInBounds_ShouldReturnRectangle() + { + var boundsWithWholeTriangle = new Envelope(-1.17459, -1.00231, -1.09803, -0.80861); + + // Arrange. + using var tempShp = new TempFileWriter(".shp", "UnifiedChecksMaterial"); + using var tempDbf = new TempFileWriter(".dbf", "UnifiedChecksMaterial"); + + var expectedTriangle = new Polygon(new LinearRing(new Coordinate[] + { + new Coordinate(-0.815656565656566, -0.439393939393939), + new Coordinate(-0.353535353535354, -0.795454545454545), + new Coordinate(-0.888888888888889,-0.929292929292929), + new Coordinate(-1.151515151515152, -0.419191919191919), + new Coordinate(-0.815656565656566,-0.439393939393939), + })); + + string expectedShapeMetadata = "Rectangle"; + + var options = new ShapefileReaderOptions() + { + MbrFilter = boundsWithWholeTriangle, + MbrFilterOption = MbrFilterOption.FilterByExtent + }; + + // Act. + using var shapefileReader = Shapefile.OpenRead(tempShp.Path, options); + var result = shapefileReader.Single(); + + // Assert. + Assert.IsNotNull(result); + Assert.IsNotNull(result.Attributes); + + Polygon resultPolygon = GetPolygon(result.Geometry); + HelperMethods.AssertPolygonsEqual(resultPolygon, expectedTriangle); + + object shapeNameData = result.Attributes["ShapeName"]; + Assert.IsInstanceOf(shapeNameData); + + Assert.AreEqual((string)shapeNameData, expectedShapeMetadata); + } + + [Test] + public void ReadByGeoFilter_ReadWithNoShapeInBounds_ShouldReturnEmptyEnumerable() + { + var boundsWithWholeTriangle = new Envelope(-1.17459, -1.00231, -1.09803, -1.5); + + // Arrange. + using var tempShp = new TempFileWriter(".shp", "UnifiedChecksMaterial"); + using var tempDbf = new TempFileWriter(".dbf", "UnifiedChecksMaterial"); + + var options = new ShapefileReaderOptions() + { + MbrFilter = boundsWithWholeTriangle + }; + + // Act. + var results = Shapefile.ReadAllFeatures(tempShp.Path, options); + + // Assert. + Assert.IsNotNull(results); + Assert.IsFalse(results.Any()); + } + + [Test] + public void ReadByGeoFilter_ReadWithNoShapeInBoundsAndFilterByGeometryOption_ShouldReturnEmptyEnumerable() + { + var boundsWithWholeTriangle = new Envelope(-1.17459, -1.00231, -1.09803, -1.5); + + // Arrange. + using var tempShp = new TempFileWriter(".shp", "UnifiedChecksMaterial"); + using var tempDbf = new TempFileWriter(".dbf", "UnifiedChecksMaterial"); + + var options = new ShapefileReaderOptions() + { + MbrFilter = boundsWithWholeTriangle, + MbrFilterOption = MbrFilterOption.FilterByGeometry + }; + + // Act. + var results = Shapefile.ReadAllFeatures(tempShp.Path, options); + + // Assert. + Assert.IsNotNull(results); + Assert.IsFalse(results.Any()); + } + + [Test, ShapeFileIssueNumber(27)] + public void ReadByGeoFilter_ReadDbfDataAfterReaderObjectDisposed_ShouldNotThrowException() + { + var boundsWithWholeTriangle = new Envelope(-1.17459, -1.00231, -1.09803, -0.80861); + + // Arrange. + using var tempShp = new TempFileWriter(".shp", "UnifiedChecksMaterial"); + using var tempDbf = new TempFileWriter(".dbf", "UnifiedChecksMaterial"); + + var options = new ShapefileReaderOptions() + { + MbrFilter = boundsWithWholeTriangle + }; + + // Act. + using var shapefileReader = Shapefile.OpenRead(tempShp.Path, options); + var result = shapefileReader.Single(); + + // Dispose of the reader object. + shapefileReader.Dispose(); + + // Assert. + Assert.IsNotNull(result.Attributes); + } + + [Test, ShapeFileIssueNumber(27)] + public void ReadByGeoFilter_ReadShapeDataAfterReaderObjectDisposed_ShouldNotThrowException() + { + var boundsWithWholeTriangle = new Envelope(-1.17459, -1.00231, -1.09803, -0.80861); + + // Arrange. + using var tempShp = new TempFileWriter(".shp", "UnifiedChecksMaterial"); + using var tempDbf = new TempFileWriter(".dbf", "UnifiedChecksMaterial"); + + var options = new ShapefileReaderOptions() + { + MbrFilter = boundsWithWholeTriangle + }; + + // Act. + using var shapefileReader = Shapefile.OpenRead(tempShp.Path, options); + var result = shapefileReader.Single(); + shapefileReader.Dispose(); + + // Assert. + Assert.IsNotNull(result.Geometry); + } + + private Polygon GetPolygon(Geometry geometry) + { + var multiPolygon = geometry as MultiPolygon; + Assert.IsNotNull(multiPolygon); + Assert.AreEqual(geometry.NumGeometries, 1); + + var polygon = multiPolygon.GetGeometryN(0) as Polygon; + Assert.IsNotNull(polygon); + return polygon; + } + } +} diff --git a/test/NetTopologySuite.IO.Esri.Test/Deprecated/ShapeFile.Extended/ShapeReaderTests.cs b/test/NetTopologySuite.IO.Esri.Test/Deprecated/ShapeFile.Extended/ShapeReaderTests.cs new file mode 100644 index 0000000..652ebcd --- /dev/null +++ b/test/NetTopologySuite.IO.Esri.Test/Deprecated/ShapeFile.Extended/ShapeReaderTests.cs @@ -0,0 +1,1213 @@ +using System; +using System.IO; +using System.Linq; +using NetTopologySuite.Geometries; +using NUnit.Framework; + +namespace NetTopologySuite.IO.Esri.Test.Deprecated.ShapeFile.Extended +{ + [TestFixture] + public class ShapeReaderTests + { + + [Test] + public void Ctor_SendNullPath_ShouldThrowException() + { + // TODO: Remove no longer relevant test + // ShpReader constructors support only a Stream as input data parameter. + /* + // Act. + Assert.Catch(() => + { + using var shpReader = Shp.OpenRead((string)null); + }); + */ + } + + [Test] + public void Ctor_SendEmptyPath_ShouldThrowException() + { + // TODO: Remove no longer relevant test + // ShpReader constructors support only a Stream as input data parameter. + /* + // Act. + Assert.Catch(() => + { + new IO.ShapeFile.Extended.ShapeReader(string.Empty); + }); + */ + } + + [Test] + public void Ctor_SendWhitespacePath_ShouldThrowException() + { + // TODO: Remove no longer relevant test + // ShpReader constructors support only a Stream as input data parameter. + /* + // Act. + Assert.Catch(() => + { + new IO.ShapeFile.Extended.ShapeReader(" \t "); + }); + */ + } + + [Test] + public void Ctor_SendNonExistantFilePath_ShouldThrowException() + { + // TODO: Remove no longer relevant test + // ShpReader constructors support only a Stream as input data parameter. + /* + // Act. + Assert.Catch(() => + { + new IO.ShapeFile.Extended.ShapeReader(@"C:\this\is\sheker\path\should\never\exist\on\ur\pc"); + }); + */ + } + + [Test] + public void Ctor_SendValidParameters_ShouldReturnNotNull() + { + // Arrange + using var tempShp = new TempFileWriter(".shp", "line_ed50_geo"); + + // Act. + var shp = Shp.Shp.OpenRead(tempShp.OpenRead()); + + // Assert. + Assert.IsNotNull(shp); + Assert.IsTrue(shp.Any()); + } + + [Test] + public void FileHeader_ReadPoint_ShouldReturnCorrectValues() + { + // Arrange. + var expectedMBR = new Envelope(34.14526022208882, 34.28293070132935, 31.85116738930965, 31.92063218020455); + + using var tempShp = new TempFileWriter(".shp", "point_ed50_geo"); + + // Act. + var shp = Shp.Shp.OpenRead(tempShp.OpenRead()); + + // Assert. + Assert.IsNotNull(shp); + Assert.AreEqual(shp.ShapeType, ShapeType.Point); + HelperMethods.AssertEnvelopesEqual(shp.BoundingBox, expectedMBR); + } + + [Test] + public void FileHeader_ReadLine_ShouldReturnCorrectValues() + { + // Arrange. + var expectedMBR = new Envelope(639384.5630270261, 662946.9241196744, 3505730.839052265, 3515879.236960234); + + using var tempShp = new TempFileWriter(".shp", "line_ed50_utm36"); + + // Act. + var shp = Shp.Shp.OpenRead(tempShp.OpenRead()); + + // Assert. + Assert.IsNotNull(shp); + Assert.AreEqual(shp.ShapeType, ShapeType.PolyLine); + HelperMethods.AssertEnvelopesEqual(shp.BoundingBox, expectedMBR); + } + + [Test] + public void FileHeader_ReadPolygon_ShouldReturnCorrectValues() + { + // Arrange. + var expectedMBR = new Envelope(33.47383821246188, 33.75452922072821, 32.0295864794076, 32.1886342399706); + + using var tempShp = new TempFileWriter(".shp", "polygon_wgs84_geo"); + + // Act. + var shp = Shp.Shp.OpenRead(tempShp.OpenRead()); + + // Assert. + Assert.IsNotNull(shp); + Assert.AreEqual(shp.ShapeType, ShapeType.Polygon); + HelperMethods.AssertEnvelopesEqual(shp.BoundingBox, expectedMBR); + } + + [Test] + public void ReadMBRs_ReadPoint_ShouldReturnCorrectValues() + { + // Arrange. + + var expectedInfos = new[] + { + new Envelope(new Coordinate(34.282930701329349, 31.851167389309651)), + new Envelope(new Coordinate(34.145260222088822, 31.864369159253059)), + new Envelope(new Coordinate(34.181721116813314, 31.920632180204553)) + }; + + using var tempShp = new TempFileWriter(".shp", "point_ed50_geo"); + + // Act. + var shp = Shp.Shp.OpenRead(tempShp.OpenRead()); + var infos = shp.Select(g => g.EnvelopeInternal).ToArray(); + + // Assert. + Assert.IsNotNull(infos); + Assert.AreEqual(3, infos.Length); + + int currIndex = 0; + + foreach (var expectedInfo in expectedInfos) + { + HelperMethods.AssertEnvelopesEqual(expectedInfo, infos[currIndex++]); + } + } + + [Test] + public void ReadMBRs_ReadUnifiedWithNullAtStart_ShouldReturnCorrectValues() + { + var expectedInfos = new[] + { + new Envelope(), + new Envelope(-1.151515151515152, -0.353535353535354, -0.929292929292929, -0.419191919191919), + new Envelope(-0.457070707070707, 0.421717171717172, 0.070707070707071, 0.578282828282829), + }; + + using var tempShp = new TempFileWriter(".shp", "UnifiedChecksMaterialNullAtStart"); + + // Act. + var shp = Shp.Shp.OpenRead(tempShp.OpenRead()); + var infos = shp.Select(g => g.EnvelopeInternal).ToArray(); + + // Assert. + Assert.IsNotNull(infos); + Assert.AreEqual(expectedInfos.Length, infos.Length); + + int currIndex = 0; + + foreach (var expectedInfo in expectedInfos) + { + HelperMethods.AssertEnvelopesEqual(expectedInfo, infos[currIndex++]); + } + } + + [Test] + public void ReadMBRs_ReadUnifiedWithNullInMiddle_ShouldReturnCorrectValues() + { + // Arrange. + var expectedInfos = new[] + { + new Envelope(-1.151515151515152, -0.353535353535354, -0.929292929292929, -0.419191919191919), + new Envelope(), + new Envelope(-0.457070707070707, 0.421717171717172, 0.070707070707071, 0.578282828282829) + }; + + using var tempShp = new TempFileWriter(".shp", "UnifiedChecksMaterialNullInMiddle"); + + // Act. + var shp = Shp.Shp.OpenRead(tempShp.OpenRead()); + var infos = shp.Select(g => g.EnvelopeInternal).ToArray(); + + // Assert. + Assert.IsNotNull(infos); + Assert.AreEqual(expectedInfos.Length, infos.Length); + + int currIndex = 0; + + foreach (var expectedInfo in expectedInfos) + { + HelperMethods.AssertEnvelopesEqual(expectedInfo, infos[currIndex++]); + } + } + + [Test] + public void ReadMBRs_ReadUnifiedWithNullAtEnd_ShouldReturnCorrectValues() + { + // Arrange. + var expectedInfos = new[] + { + new Envelope(-1.151515151515152, -0.353535353535354, -0.929292929292929, -0.419191919191919), + new Envelope(-0.457070707070707, 0.421717171717172, 0.070707070707071, 0.578282828282829), + new Envelope() + }; + + using var tempShp = new TempFileWriter(".shp", "UnifiedChecksMaterialNullAtEnd"); + + // Act. + var shp = Shp.Shp.OpenRead(tempShp.OpenRead()); + var infos = shp.Select(g => g.EnvelopeInternal).ToArray(); + + // Assert. + Assert.IsNotNull(infos); + Assert.AreEqual(expectedInfos.Length, infos.Length); + + int currIndex = 0; + + foreach (var expectedInfo in expectedInfos) + { + HelperMethods.AssertEnvelopesEqual(expectedInfo, infos[currIndex++]); + } + } + + [Test] + public void ReadMBRs_ReadLine_ShouldReturnCorrectValues() + { + // Arrange. + var expectedInfos = new[] + { + new Envelope(34.573027972716453, 34.628034609274806, 31.803273460424684, 31.895998933480186), + new Envelope(34.396692412092257, 34.518021336158107, 31.778756216701534, 31.864880893370035), + }; + + using var tempShp = new TempFileWriter(".shp", "line_wgs84_geo"); + + // Act. + var shp = Shp.Shp.OpenRead(tempShp.OpenRead()); + var infos = shp.Select(g => g.EnvelopeInternal).ToArray(); + + // Assert. + Assert.IsNotNull(infos); + Assert.AreEqual(2, infos.Length); + + int currIndex = 0; + + foreach (var expectedInfo in expectedInfos) + { + HelperMethods.AssertEnvelopesEqual(expectedInfo, infos[currIndex++]); + } + } + + [Test] + public void ReadMBRs_ReadPolygon_ShouldReturnCorrectValues() + { + // Arrange. + var expectedInfos = new[] + { + new Envelope(33.719047819505683, 33.78096814177016, 31.928805665809271, 32.025301664150398), + new Envelope(33.819000337359398, 33.929011051318348, 31.97406740944362, 32.072449163771559) + }; + + using var tempShp = new TempFileWriter(".shp", "polygon_ed50_geo"); + + // Act. + var shp = Shp.Shp.OpenRead(tempShp.OpenRead()); + var infos = shp.Select(g => g.EnvelopeInternal).ToArray(); + + // Assert. + Assert.IsNotNull(infos); + Assert.AreEqual(2, infos.Length); + + int currIndex = 0; + + foreach (var expectedInfo in expectedInfos) + { + HelperMethods.AssertEnvelopesEqual(expectedInfo, infos[currIndex++]); + } + } + + [Test] + public void ReadShapeAtOffset_SendNegativeOffset_shouldThrowException() + { + // TODO: Remove no longer relevant test + // Current implementation supports forward only shape reading. + /* + // Arrange. + GeometryFactory factory = new GeometryFactory(); + m_TmpFile = new TempFileWriter(".shp", ShpFiles.Read("polygon intersecting line")); + m_Reader = new IO.ShapeFile.Extended.ShapeReader(m_TmpFile.Path); + + // Act. + Assert.Catch(() => + { + m_Reader.ReadShapeAtOffset(-1, factory); + }); + */ + } + + [Test] + public void ReadShapeAtOffset_SendOffsetAtEndOfFile_shouldThrowException() + { + // TODO: Remove no longer relevant test + // Current implementation supports forward only shape reading. + /* + // Arrange. + GeometryFactory factory = new GeometryFactory(); + m_TmpFile = new TempFileWriter(".shp", ShpFiles.Read("polygon intersecting line")); + m_Reader = new IO.ShapeFile.Extended.ShapeReader(m_TmpFile.Path); + + // Act. + Assert.Catch(() => + { + m_Reader.ReadShapeAtOffset(ShpFiles.Read("polygon intersecting line").Length, factory); + }); + */ + } + + [Test] + public void ReadShapeAtOffset_ReadPolygonWithIntersectingLine_shouldReturnInvalidGeo() + { + // Arrange. + using var tempShp = new TempFileWriter(".shp", "polygon intersecting line"); + var shp = Shp.Shp.OpenRead(tempShp.OpenRead()); + + bool[] expectedValidityResults = new bool[] { false, true }; + + var firstGeo = shp.First(); + Assert.IsNotNull(firstGeo); + Assert.AreEqual(firstGeo.IsValid, false); + + Assert.Catch(() => + { + var secondGeo = shp.Skip(1).First(); + }); + } + + [Test] + public void ReadShapeAtOffset_ReadPoint_shouldReturnCorrectValue() + { + // Arrange. + using var tempShp = new TempFileWriter(".shp", "point_ed50_geo"); + var shp = Shp.Shp.OpenRead(tempShp.OpenRead()); + + double[,] expectedCoordinates = {{ 34.282930701329349, 31.851167389309651 }, + { 34.145260222088822, 31.864369159253059 }, + { 34.181721116813314, 31.920632180204553 }}; + + // Act. + int i = 0; + foreach(var geo in shp) + { + // Assert. + Assert.IsNotNull(geo); + Assert.IsTrue(geo.IsValid); + Assert.IsInstanceOf(geo); + var givenPoint = geo as Point; + + HelperMethods.AssertDoubleValuesEqual(givenPoint.X, expectedCoordinates[i, 0]); + HelperMethods.AssertDoubleValuesEqual(givenPoint.Y, expectedCoordinates[i, 1]); + i++; + } + } + + [Test] + public void ReadShapeAtOffset_ReadLines_shouldReturnCorrectValue() + { + // Arrange. + using var tempShp = new TempFileWriter(".shp", "line_wgs84_geo"); + var shp = Shp.Shp.OpenRead(tempShp.OpenRead()); + + var expectedLines = new Coordinate[,] + { + { + new Coordinate(34.574599590903837, 31.884368958893564), + new Coordinate(34.57648553272869, 31.803273460424684), + new Coordinate(34.628034609274806, 31.875882220681703), + new Coordinate(34.573027972716453, 31.895998933480186), + new Coordinate(34.582143358203268, 31.886883547993374) + }, + { + new Coordinate(34.448555812275849, 31.864880893370035), + new Coordinate(34.396692412092257, 31.778756216701534), + new Coordinate(34.468672525074325, 31.794158074937872), + new Coordinate(34.484703030585621, 31.844135533296601), + new Coordinate(34.518021336158107, 31.838163384184551) + } + }; + + // Act. + int i = 0; + foreach (var geo in shp) + { + // Assert. + Assert.IsNotNull(geo); + Assert.IsTrue(geo.IsValid); + Assert.IsInstanceOf(geo); + var givenLine = geo.IsEmpty ? LineString.Empty : geo.GetGeometryN(0) as LineString; + + for (int j = 0; j < givenLine.Coordinates.Length; j++) + { + var currPoint = givenLine.Coordinates[j]; + + HelperMethods.AssertDoubleValuesEqual(currPoint.X, expectedLines[i, j].X); + HelperMethods.AssertDoubleValuesEqual(currPoint.Y, expectedLines[i, j].Y); + } + i++; + } + } + + [Test] + public void ReadShapeAtOffset_ReadPolygon_shouldReturnCorrectValue() + { + // Arrange. + using var tempShp = new TempFileWriter(".shp", "polygon_ed50_geo"); + var shp = Shp.Shp.OpenRead(tempShp.OpenRead()); + + var expectedLines = new Coordinate[,] + { + { + new Coordinate(33.719047819505683, 31.989469320254013), + new Coordinate(33.730049025918099, 32.025301664150398), + new Coordinate(33.771538712027194, 32.008956957757299), + new Coordinate(33.78096814177016, 31.993555297099103), + new Coordinate(33.744507207486457, 31.928805665809271), + new Coordinate(33.719047819505683, 31.989469320254013) + }, + { + new Coordinate(33.821829475819285, 32.051075573685317), + new Coordinate(33.860176141775888, 32.072449163771559), + new Coordinate(33.927125440097875, 32.054847113210094), + new Coordinate(33.929011051318348, 31.97878189417845), + new Coordinate(33.819000337359398, 31.97406740944362), + new Coordinate(33.821829475819285, 32.051075573685317) + } + }; + + // Act. + int i = 0; + foreach (var geo in shp) + { + // Assert. + Assert.IsNotNull(geo); + Assert.IsTrue(geo.IsValid); + Assert.IsInstanceOf(geo); + var givenPoly = geo.IsEmpty ? Polygon.Empty : geo.GetGeometryN(0) as Polygon; + + Assert.IsNotNull(givenPoly.ExteriorRing); + Assert.AreSame(givenPoly.ExteriorRing, givenPoly.Shell); + Assert.AreEqual(givenPoly.Shell.Coordinates.Length, expectedLines.GetLength(1)); + + LineString givenLine = givenPoly.Shell; + + for (int j = 0; j < givenLine.Coordinates.Length; j++) + { + var currPoint = givenLine.Coordinates[j]; + + HelperMethods.AssertDoubleValuesEqual(currPoint.X, expectedLines[i, j].X); + HelperMethods.AssertDoubleValuesEqual(currPoint.Y, expectedLines[i, j].Y); + } + i++; + } + } + + [Test] + public void ReadShapeAtOffset_ReadAllPolygonsFromUnifiedWithNullAtStart_ShouldReturnCorrectValues() + { + // Arrange. + using var tempShp = new TempFileWriter(".shp", "UnifiedChecksMaterialNullAtStart"); + var shp = Shp.Shp.OpenRead(tempShp.OpenRead()); + + var expectedResult = new Coordinate[][] + { + Array.Empty(), + new Coordinate[] + { + new Coordinate(-0.815656565656566, -0.439393939393939), + new Coordinate(-0.353535353535354, -0.795454545454545), + new Coordinate(-0.888888888888889,-0.929292929292929), + new Coordinate(-1.151515151515152, -0.419191919191919), + new Coordinate(-0.815656565656566,-0.439393939393939), + }, + new Coordinate[] + { + new Coordinate(0.068181818181818,0.578282828282829), + new Coordinate(0.421717171717172,0.070707070707071), + new Coordinate(-0.457070707070707,0.080808080808081), + new Coordinate(0.068181818181818,0.578282828282829), + } + }; + long[] offsets = { 112, 248 }; + + // Act. + for (int i = 0; i < offsets.Length; i++) + { + shp.Read(); + var geo = shp.Geometry; + + // Assert. + Assert.IsNotNull(geo); + Assert.IsTrue(geo.IsValid); + Assert.IsInstanceOf(geo); + var givenPoly = geo.IsEmpty ? Polygon.Empty : geo.GetGeometryN(0) as Polygon; + + Assert.IsNotNull(givenPoly.ExteriorRing); + Assert.AreSame(givenPoly.ExteriorRing, givenPoly.Shell); + Assert.AreEqual(givenPoly.Shell.Coordinates.Length, expectedResult[i].Length); + + LineString givenLine = givenPoly.Shell; + + for (int j = 0; j < givenLine.Coordinates.Length; j++) + { + var currPoint = givenLine.Coordinates[j]; + + HelperMethods.AssertDoubleValuesEqual(currPoint.X, expectedResult[i][j].X); + HelperMethods.AssertDoubleValuesEqual(currPoint.Y, expectedResult[i][j].Y); + } + } + } + + [Test] + public void ReadShapeAtOffset_ReadAllPolygonsFromUnifiedWithNullInMiddle_ShouldReturnCorrectValues() + { + // Arrange. + using var tempShp = new TempFileWriter(".shp", "UnifiedChecksMaterialNullInMiddle"); + var shp = Shp.Shp.OpenRead(tempShp.OpenRead()); + + var expectedResult = new Coordinate[][] + { + new Coordinate[] + { + new Coordinate(-0.815656565656566, -0.439393939393939), + new Coordinate(-0.353535353535354, -0.795454545454545), + new Coordinate(-0.888888888888889,-0.929292929292929), + new Coordinate(-1.151515151515152, -0.419191919191919), + new Coordinate(-0.815656565656566,-0.439393939393939), + }, + Array.Empty(), + new Coordinate[] + { + new Coordinate(0.068181818181818,0.578282828282829), + new Coordinate(0.421717171717172,0.070707070707071), + new Coordinate(-0.457070707070707,0.080808080808081), + new Coordinate(0.068181818181818,0.578282828282829), + } + }; + long[] offsets = { 100, 248 }; + + // Act. + for (int i = 0; i < offsets.Length; i++) + { + shp.Read(); + var geo = shp.Geometry; + + // Assert. + Assert.IsNotNull(geo); + Assert.IsTrue(geo.IsValid); + Assert.IsInstanceOf(geo); + var givenPoly = geo.IsEmpty ? Polygon.Empty : geo.GetGeometryN(0) as Polygon; + + Assert.IsNotNull(givenPoly.ExteriorRing); + Assert.AreSame(givenPoly.ExteriorRing, givenPoly.Shell); + Assert.AreEqual(givenPoly.Shell.Coordinates.Length, expectedResult[i].Length); + + LineString givenLine = givenPoly.Shell; + + for (int j = 0; j < givenLine.Coordinates.Length; j++) + { + var currPoint = givenLine.Coordinates[j]; + + HelperMethods.AssertDoubleValuesEqual(currPoint.X, expectedResult[i][j].X); + HelperMethods.AssertDoubleValuesEqual(currPoint.Y, expectedResult[i][j].Y); + } + } + } + + [Test] + public void ReadShapeAtOffset_ReadAllPolygonsFromUnifiedWithNullAtEnd_ShouldReturnCorrectValues() + { + // Arrange. + using var tempShp = new TempFileWriter(".shp", "UnifiedChecksMaterialNullAtEnd"); + var shp = Shp.Shp.OpenRead(tempShp.OpenRead()); + + var expectedResult = new Coordinate[][] + { + new Coordinate[] + { + new Coordinate(-0.815656565656566, -0.439393939393939), + new Coordinate(-0.353535353535354, -0.795454545454545), + new Coordinate(-0.888888888888889,-0.929292929292929), + new Coordinate(-1.151515151515152, -0.419191919191919), + new Coordinate(-0.815656565656566,-0.439393939393939), + }, + new Coordinate[] + { + new Coordinate(0.068181818181818,0.578282828282829), + new Coordinate(0.421717171717172,0.070707070707071), + new Coordinate(-0.457070707070707,0.080808080808081), + new Coordinate(0.068181818181818,0.578282828282829), + } + }; + long[] offsets = { 100, 236 }; + + // Act. + for (int i = 0; i < offsets.Length; i++) + { + shp.Read(); + var geo = shp.Geometry; + + // Assert. + Assert.IsNotNull(geo); + Assert.IsTrue(geo.IsValid); + Assert.IsInstanceOf(geo); + var givenPoly = geo.GetGeometryN(0) as Polygon; + + Assert.IsNotNull(givenPoly.ExteriorRing); + Assert.AreSame(givenPoly.ExteriorRing, givenPoly.Shell); + Assert.AreEqual(givenPoly.Shell.Coordinates.Length, expectedResult[i].Length); + + LineString givenLine = givenPoly.Shell; + + for (int j = 0; j < givenLine.Coordinates.Length; j++) + { + var currPoint = givenLine.Coordinates[j]; + + HelperMethods.AssertDoubleValuesEqual(currPoint.X, expectedResult[i][j].X); + HelperMethods.AssertDoubleValuesEqual(currPoint.Y, expectedResult[i][j].Y); + } + } + } + + [Test] + public void ReadShapeAtOffset_TryReadAfterDisposed_shouldThrowException() + { + // Arrange. + using var tempShp = new TempFileWriter(".shp", "line_wgs84_geo"); + var shp = Shp.Shp.OpenRead(tempShp.OpenRead()); + + shp.Dispose(); + Assert.Catch(() => + { + shp.First(); + }); + } + + [Test] + public void ReadAllShapes_SendNullFactory_ShouldThrowException() + { + // Arrange. + using var tempShp = new TempFileWriter(".shp", "UnifiedChecksMaterial"); + var shp = Shp.Shp.OpenRead(tempShp.OpenRead()); + + // TODO: Changed original test logic. + // Geometry.DefaultFactory is used when provided factory is null. + + // Act. + var geos = shp.ToList(); + + // Assert. + Assert.IsNotNull(geos); + Assert.IsTrue(shp.Any()); + } + + [Test] + public void ReadAllShapes_ReadEmptyShapeFile_ShouldReturnEmptyEnumerable() + { + // Arrange. + using var tempShp = new TempFileWriter(".shp", "EmptyShapeFile"); + var shp = Shp.Shp.OpenRead(tempShp.OpenRead()); + + // Act. + var geos = shp.ToList(); + + // Assert. + Assert.IsNotNull(geos); + Assert.IsFalse(geos.Any()); + } + + [Test] + public void ReadAllShapes_ReadPointZM_ShouldReturnCorrectValues() + { + // Arrange. + using var tempShp = new TempFileWriter(".shp", "shape_PointZM"); + var shp = Shp.Shp.OpenRead(tempShp.OpenRead()); + double errorMargin = Math.Pow(10, -6); + + double[,] expectedValues = {{-11348202.6085706, 4503476.68482375}, + {-601708.888562033, 3537065.37906758}, + {-7366588.02885523, -637831.461799072}}; + + // Act. + var shapes = shp.ToList(); + + // Assert. + Assert.IsNotNull(shapes); + var shapesArr = shapes.ToArray(); + Assert.AreEqual(shapesArr.Length, 3); + + for (int i = 0; i < shapesArr.Length; i++) + { + Assert.IsInstanceOf(shapesArr[i]); + var currPoint = shapesArr[i] as Point; + HelperMethods.AssertDoubleValuesEqual(currPoint.X, expectedValues[i, 0], errorMargin); + HelperMethods.AssertDoubleValuesEqual(currPoint.Y, expectedValues[i, 1], errorMargin); + HelperMethods.AssertDoubleValuesEqual(currPoint.Z, 0); + HelperMethods.AssertDoubleValuesEqual(currPoint.M, double.NaN); + } + } + + [Test] + public void ReadAllShapes_ReadPointZMWithMissingMValues_ShouldReturnCorrectValues() + { + // TODO: Remove no longer relevant test + // This is not a valid SHP file. Record number 4 has ContentLength=-2. + /* + // Arrange. + using var tempShp = new TempFileWriter(".shp", "shape_pointZM_MissingM values"); + var shp = Shp.OpenRead(tempShp.OpenRead()); + double errorMargin = Math.Pow(10, -6); + + double[,] expectedValues = {{-11348202.6085706, 4503476.68482375}, + {-601708.888562033, 3537065.37906758}, + {-7366588.02885523, -637831.461799072}}; + + // Act. + var shapes = shp.ToList(); + + // Assert. + Assert.IsNotNull(shapes); + var shapesArr = shapes.ToArray(); + Assert.AreEqual(shapesArr.Length, 3); + + for (int i = 0; i < shapesArr.Length; i++) + { + Assert.IsInstanceOf(shapesArr[i]); + var currPoint = shapesArr[i] as Point; + HelperMethods.AssertDoubleValuesEqual(currPoint.X, expectedValues[i, 0], errorMargin); + HelperMethods.AssertDoubleValuesEqual(currPoint.Y, expectedValues[i, 1], errorMargin); + HelperMethods.AssertDoubleValuesEqual(currPoint.Z, 0); + HelperMethods.AssertDoubleValuesEqual(currPoint.M, double.NaN); + } + */ + } + + [Test] + public void ReadAllShapes_ReadPointM_ShouldReturnCorrectValues() + { + // Arrange. + using var tempShp = new TempFileWriter(".shp", "shape_pointM"); + var shp = Shp.Shp.OpenRead(tempShp.OpenRead()); + + double[,] expectedValues = {{-133.606621226874, 66.8997078870497}, + {-68.0564751703992, 56.4888023369036}, + {-143.246348588121, 40.6796494644596}, + {-82.3232716650438, -21.014605647517}}; + + // Act. + var shapes = shp.ToList(); + + // Assert. + Assert.IsNotNull(shapes); + var shapesArr = shapes.ToArray(); + Assert.AreEqual(shapesArr.Length, 4); + + for (int i = 0; i < shapesArr.Length; i++) + { + Assert.IsInstanceOf(shapesArr[i]); + var currPoint = shapesArr[i] as Point; + HelperMethods.AssertDoubleValuesEqual(currPoint.X, expectedValues[i, 0]); + HelperMethods.AssertDoubleValuesEqual(currPoint.Y, expectedValues[i, 1]); + HelperMethods.AssertDoubleValuesEqual(currPoint.Z, double.NaN); + HelperMethods.AssertDoubleValuesEqual(currPoint.M, double.NaN); + } + } + + [Test] + public void ReadAllShapes_ReadUnifiedChecksMaterial_ShouldRead2ShapesAndCorrectValues() + { + // Arrange. + using var tempShp = new TempFileWriter(".shp", "UnifiedChecksMaterial"); + var shp = Shp.Shp.OpenRead(tempShp.OpenRead()); + + Polygon[] expectedResult = new Polygon[] + { + new Polygon(new LinearRing(new Coordinate[] + { + new Coordinate(-0.815656565656566, -0.439393939393939), + new Coordinate(-0.353535353535354, -0.795454545454545), + new Coordinate(-0.888888888888889,-0.929292929292929), + new Coordinate(-1.151515151515152, -0.419191919191919), + new Coordinate(-0.815656565656566,-0.439393939393939), + })), + new Polygon(new LinearRing(new Coordinate[] + { + new Coordinate(0.068181818181818,0.578282828282829), + new Coordinate(0.421717171717172,0.070707070707071), + new Coordinate(-0.457070707070707,0.080808080808081), + new Coordinate(0.068181818181818,0.578282828282829), + })) + }; + + // Act. + var shapes = shp.ToArray(); + + Assert.IsNotNull(shapes); + Assert.AreEqual(shapes.Length, 2); + + for (int i = 0; i < shapes.Length; i++) + { + Assert.IsInstanceOf(shapes[i]); + HelperMethods.AssertPolygonsEqual(shapes[i] as MultiPolygon, expectedResult[i]); + } + } + + [Test] + public void ReadAllShapes_ReadAllPolygonsFromUnifiedWithNullAtStart_ShouldReturnCorrectValues() + { + // Arrange. + using var tempShp = new TempFileWriter(".shp", "UnifiedChecksMaterialNullAtStart"); + var shp = Shp.Shp.OpenRead(tempShp.OpenRead()); + + Polygon[] expectedResult = new Polygon[] + { + Polygon.Empty, + new Polygon(new LinearRing(new Coordinate[] + { + new Coordinate(-0.815656565656566, -0.439393939393939), + new Coordinate(-0.353535353535354, -0.795454545454545), + new Coordinate(-0.888888888888889,-0.929292929292929), + new Coordinate(-1.151515151515152, -0.419191919191919), + new Coordinate(-0.815656565656566,-0.439393939393939), + })), + new Polygon(new LinearRing(new Coordinate[] + { + new Coordinate(0.068181818181818,0.578282828282829), + new Coordinate(0.421717171717172,0.070707070707071), + new Coordinate(-0.457070707070707,0.080808080808081), + new Coordinate(0.068181818181818,0.578282828282829), + })) + }; + + // Act. + var shapes = shp.ToArray(); + + Assert.IsNotNull(shapes); + Assert.AreEqual(shapes.Length, 3); + + for (int i = 0; i < shapes.Length; i++) + { + Assert.IsInstanceOf(shapes[i]); + HelperMethods.AssertPolygonsEqual(shapes[i] as MultiPolygon, expectedResult[i]); + } + } + + [Test] + public void ReadAllShapes_ReadAllPolygonsFromUnifiedWithNullInMiddle_ShouldReturnCorrectValues() + { + // Arrange. + using var tempShp = new TempFileWriter(".shp", "UnifiedChecksMaterialNullInMiddle"); + var shp = Shp.Shp.OpenRead(tempShp.OpenRead()); + + Polygon[] expectedResult = new Polygon[] + { + new Polygon(new LinearRing(new Coordinate[] + { + new Coordinate(-0.815656565656566, -0.439393939393939), + new Coordinate(-0.353535353535354, -0.795454545454545), + new Coordinate(-0.888888888888889,-0.929292929292929), + new Coordinate(-1.151515151515152, -0.419191919191919), + new Coordinate(-0.815656565656566,-0.439393939393939), + })), + Polygon.Empty, + new Polygon(new LinearRing(new Coordinate[] + { + new Coordinate(0.068181818181818,0.578282828282829), + new Coordinate(0.421717171717172,0.070707070707071), + new Coordinate(-0.457070707070707,0.080808080808081), + new Coordinate(0.068181818181818,0.578282828282829), + })) + }; + + // Act. + var shapes = shp.ToArray(); + + Assert.IsNotNull(shapes); + Assert.AreEqual(shapes.Length, 3); + + for (int i = 0; i < shapes.Length; i++) + { + Assert.IsInstanceOf(shapes[i]); + HelperMethods.AssertPolygonsEqual(shapes[i] as MultiPolygon, expectedResult[i]); + } + } + + [Test] + public void ReadAllShapes_ReadAllPolygonsFromUnifiedWithNullAtEnd_ShouldReturnCorrectValues() + { + // Arrange. + using var tempShp = new TempFileWriter(".shp", "UnifiedChecksMaterialNullAtEnd"); + var shp = Shp.Shp.OpenRead(tempShp.OpenRead()); + + Polygon[] expectedResult = new Polygon[] + { + new Polygon(new LinearRing(new Coordinate[] + { + new Coordinate(-0.815656565656566, -0.439393939393939), + new Coordinate(-0.353535353535354, -0.795454545454545), + new Coordinate(-0.888888888888889,-0.929292929292929), + new Coordinate(-1.151515151515152, -0.419191919191919), + new Coordinate(-0.815656565656566,-0.439393939393939), + })), + new Polygon(new LinearRing(new Coordinate[] + { + new Coordinate(0.068181818181818,0.578282828282829), + new Coordinate(0.421717171717172,0.070707070707071), + new Coordinate(-0.457070707070707,0.080808080808081), + new Coordinate(0.068181818181818,0.578282828282829), + })), + Polygon.Empty + }; + + // Act. + var shapes = shp.ToArray(); + + Assert.IsNotNull(shapes); + Assert.AreEqual(shapes.Length, 3); + + for (int i = 0; i < shapes.Length; i++) + { + Assert.IsInstanceOf(shapes[i]); + HelperMethods.AssertPolygonsEqual(shapes[i] as MultiPolygon, expectedResult[i]); + } + } + + [Test] + public void ReadAllShapes_TryReadAfterDisposed_ShouldThrowException() + { + // Arrange. + using var tempShp = new TempFileWriter(".shp", "UnifiedChecksMaterial"); + var shp = Shp.Shp.OpenRead(tempShp.OpenRead()); + + // Act. + shp.Dispose(); + Assert.Catch(() => + { + shp.ToList(); + }); + } + + [Test] + public void ReadShapeAtIndex_SendNullFactory_ShouldThrowException() + { + // TODO: Remove no longer relevant test + // Current implementation supports forward only shape reading. + // Geometry.DefaultFactory is used when provided factory is null. + /* + // Arrange. + m_TmpFile = new TempFileWriter(".shp", ShpFiles.Read("UnifiedChecksMaterial")); + m_Reader = new IO.ShapeFile.Extended.ShapeReader(m_TmpFile.Path); + + // Act. + Assert.Catch(() => + { + m_Reader.ReadShapeAtIndex(0, null); + }); + */ + } + + [Test] + public void ReadShapeAtIndex_SendNegativeIndex_ShouldThrowException() + { + // TODO: Remove no longer relevant test + // Current implementation supports forward only shape reading. + /* + // Arrange. + m_TmpFile = new TempFileWriter(".shp", ShpFiles.Read("UnifiedChecksMaterial")); + m_Reader = new IO.ShapeFile.Extended.ShapeReader(m_TmpFile.Path); + GeometryFactory factory = new GeometryFactory(); + + // Act. + Assert.Catch(() => + { + m_Reader.ReadShapeAtIndex(-1, factory); + }); + */ + } + + [Test] + public void ReadShapeAtIndex_SendOutOfBoundIndex_ShouldThrowException() + { + // TODO: Remove no longer relevant test + // Current implementation supports forward only shape reading. + /* + // Arrange. + m_TmpFile = new TempFileWriter(".shp", ShpFiles.Read("UnifiedChecksMaterial")); + m_Reader = new IO.ShapeFile.Extended.ShapeReader(m_TmpFile.Path); + GeometryFactory factory = new GeometryFactory(); + + // Act. + Assert.Catch(() => + { + m_Reader.ReadShapeAtIndex(2, factory); + }); + */ + } + + [Test] + public void ReadShapeAtIndex_ReadFirstUnifiedCheckMaterialShape_ShouldReturnRectangle() + { + // Arrange. + using var tempShp = new TempFileWriter(".shp", "UnifiedChecksMaterial"); + var shp = Shp.Shp.OpenRead(tempShp.OpenRead()); + + var expectedPolygon = new Polygon(new LinearRing(new Coordinate[] + { + new Coordinate(-0.815656565656566, -0.439393939393939), + new Coordinate(-0.353535353535354, -0.795454545454545), + new Coordinate(-0.888888888888889,-0.929292929292929), + new Coordinate(-1.151515151515152, -0.419191919191919), + new Coordinate(-0.815656565656566,-0.439393939393939), + })); + + // Act. + var polygon = shp.First(); + + Assert.IsNotNull(polygon); + Assert.IsInstanceOf(polygon); + HelperMethods.AssertPolygonsEqual(polygon as MultiPolygon, expectedPolygon); + } + + [Test] + public void ReadShapeAtIndex_ReadSecondUnifiedCheckMaterialShape_ShouldReturnTriangle() + { + // Arrange. + using var tempShp = new TempFileWriter(".shp", "UnifiedChecksMaterial"); + var shp = Shp.Shp.OpenRead(tempShp.OpenRead()); + + var expectedPolygon = new Polygon(new LinearRing(new Coordinate[] + { + new Coordinate(0.068181818181818,0.578282828282829), + new Coordinate(0.421717171717172,0.070707070707071), + new Coordinate(-0.457070707070707,0.080808080808081), + new Coordinate(0.068181818181818,0.578282828282829), + })); + + // Act. + var polygon = shp.Skip(1).First(); + + Assert.IsNotNull(polygon); + Assert.IsInstanceOf(polygon); + HelperMethods.AssertPolygonsEqual(polygon as MultiPolygon, expectedPolygon); + } + + [Test] + public void ReadShapeAtIndex_ReadUnifiedCheckMaterialWithNullAtStart_ShouldReturnBothShapesCorrectly() + { + // Arrange. + using var tempShp = new TempFileWriter(".shp", "UnifiedChecksMaterialNullAtStart"); + var shp = Shp.Shp.OpenRead(tempShp.OpenRead()); + + Polygon[] expectedResult = new Polygon[] + { + Polygon.Empty, + new Polygon(new LinearRing(new Coordinate[] + { + new Coordinate(-0.815656565656566, -0.439393939393939), + new Coordinate(-0.353535353535354, -0.795454545454545), + new Coordinate(-0.888888888888889,-0.929292929292929), + new Coordinate(-1.151515151515152, -0.419191919191919), + new Coordinate(-0.815656565656566,-0.439393939393939), + })), + new Polygon(new LinearRing(new Coordinate[] + { + new Coordinate(0.068181818181818,0.578282828282829), + new Coordinate(0.421717171717172,0.070707070707071), + new Coordinate(-0.457070707070707,0.080808080808081), + new Coordinate(0.068181818181818,0.578282828282829), + })) + }; + + // Act. + for (int i = 0; i < expectedResult.Length; i++) + { + shp.Read(); + var result = shp.Geometry; + + Assert.IsNotNull(result); + Assert.IsInstanceOf(result); + + HelperMethods.AssertPolygonsEqual(result as MultiPolygon, expectedResult[i]); + } + } + + [Test] + public void ReadShapeAtIndex_ReadUnifiedCheckMaterialWithNullAtEnd_ShouldReturnBothShapesCorrectly() + { + // Arrange. + using var tempShp = new TempFileWriter(".shp", "UnifiedChecksMaterialNullAtEnd"); + var shp = Shp.Shp.OpenRead(tempShp.OpenRead()); + + Polygon[] expectedResult = new Polygon[] + { + new Polygon(new LinearRing(new Coordinate[] + { + new Coordinate(-0.815656565656566, -0.439393939393939), + new Coordinate(-0.353535353535354, -0.795454545454545), + new Coordinate(-0.888888888888889,-0.929292929292929), + new Coordinate(-1.151515151515152, -0.419191919191919), + new Coordinate(-0.815656565656566,-0.439393939393939), + })), + new Polygon(new LinearRing(new Coordinate[] + { + new Coordinate(0.068181818181818,0.578282828282829), + new Coordinate(0.421717171717172,0.070707070707071), + new Coordinate(-0.457070707070707,0.080808080808081), + new Coordinate(0.068181818181818,0.578282828282829), + })) + }; + + // Act. + for (int i = 0; i < expectedResult.Length; i++) + { + shp.Read(); + var result = shp.Geometry; + + Assert.IsNotNull(result); + Assert.IsInstanceOf(result); + + HelperMethods.AssertPolygonsEqual(result as MultiPolygon, expectedResult[i]); + } + } + + [Test] + public void ReadShapeAtIndex_ReadUnifiedCheckMaterialWithNulLInMiddle_ShouldReturnBothShapesCorrectly() + { + // Arrange. + using var tempShp = new TempFileWriter(".shp", "UnifiedChecksMaterialNullInMiddle"); + var shp = Shp.Shp.OpenRead(tempShp.OpenRead()); + + Polygon[] expectedResult = new Polygon[] + { + new Polygon(new LinearRing(new Coordinate[] + { + new Coordinate(-0.815656565656566, -0.439393939393939), + new Coordinate(-0.353535353535354, -0.795454545454545), + new Coordinate(-0.888888888888889,-0.929292929292929), + new Coordinate(-1.151515151515152, -0.419191919191919), + new Coordinate(-0.815656565656566,-0.439393939393939), + })), + Polygon.Empty, + new Polygon(new LinearRing(new Coordinate[] + { + new Coordinate(0.068181818181818,0.578282828282829), + new Coordinate(0.421717171717172,0.070707070707071), + new Coordinate(-0.457070707070707,0.080808080808081), + new Coordinate(0.068181818181818,0.578282828282829), + })) + }; + + // Act. + for (int i = 0; i < expectedResult.Length; i++) + { + shp.Read(); + var result = shp.Geometry; + + Assert.IsNotNull(result); + Assert.IsInstanceOf(result); + + HelperMethods.AssertPolygonsEqual(result as MultiPolygon, expectedResult[i]); + } + } + + [Test] + public void ReadShapeAtIndex_TryReadAfterDisposed_ShouldThrowException() + { + // Arrange. + using var tempShp = new TempFileWriter(".shp", "UnifiedChecksMaterial"); + var shp = Shp.Shp.OpenRead(tempShp.OpenRead()); + + // Act. + shp.Dispose(); + Assert.Catch(() => + { + shp.First(); + }); + } + } +} diff --git a/test/NetTopologySuite.IO.Esri.Test/Deprecated/ShapeFile.Extended/ShapefileDataWriterTests.cs b/test/NetTopologySuite.IO.Esri.Test/Deprecated/ShapeFile.Extended/ShapefileDataWriterTests.cs new file mode 100644 index 0000000..2b121fe --- /dev/null +++ b/test/NetTopologySuite.IO.Esri.Test/Deprecated/ShapeFile.Extended/ShapefileDataWriterTests.cs @@ -0,0 +1,105 @@ +using NetTopologySuite.Features; +using NetTopologySuite.Geometries; +using NetTopologySuite.IO.Esri; +using NetTopologySuite.IO.Esri.Dbf; +using NUnit.Framework; +using System; +using System.Linq; + +namespace NetTopologySuite.IO.Esri.Test.Deprecated.Deprecated.Extended +{ + /// + /// Contains tests for the shapefile data writer. + /// + [TestFixture] + public class ShapefileDataWriterTests + { + /// + /// Tests creating a header from a feature. + /// + [Test] + public void TestGetHeaderFromFeature() + { + var feature = new Feature(new Point(0, 0), new AttributesTable()); + feature.Attributes.Add("c_long", (long)12345678900000000); + feature.Attributes.Add("c_ulong", (ulong)12345678900000000); + feature.Attributes.Add("c_int", int.MaxValue); + feature.Attributes.Add("c_uint", uint.MinValue); + feature.Attributes.Add("c_short", short.MaxValue); + feature.Attributes.Add("c_ushort", ushort.MaxValue); + feature.Attributes.Add("c_string", string.Empty); + feature.Attributes.Add("c_double", double.MinValue); + feature.Attributes.Add("c_bool", false); + feature.Attributes.Add("c_datetime", new DateTime(1999, 01, 01)); + + var shpPath = TestShapefiles.GetTempShpPath(); + Shapefile.WriteAllFeatures(Enumerable.Repeat(feature, 1), shpPath); + using var shapefile = Shapefile.OpenRead(shpPath); + var fields = shapefile.Fields; + + Assert.IsNotNull(fields); + Assert.AreEqual(10, fields.Count); + + var field = fields["c_long"]; + Assert.IsNotNull(field); + Assert.AreEqual(DbfType.Numeric, field.FieldType); + Assert.AreEqual(0, field.NumericScale); + Assert.AreEqual(19, field.Length); + + field = fields["c_ulong"]; + Assert.IsNotNull(field); + Assert.AreEqual(DbfType.Numeric, field.FieldType); + Assert.AreEqual(0, field.NumericScale); + Assert.AreEqual(19, field.Length); + + field = fields["c_int"]; + Assert.IsNotNull(field); + Assert.AreEqual(DbfType.Numeric, field.FieldType); + Assert.AreEqual(0, field.NumericScale); + Assert.AreEqual(10, field.Length); + + field = fields["c_uint"]; + Assert.IsNotNull(field); + Assert.AreEqual(DbfType.Numeric, field.FieldType); + Assert.AreEqual(0, field.NumericScale); + Assert.AreEqual(10, field.Length); + + field = fields["c_short"]; + Assert.IsNotNull(field); + Assert.AreEqual(DbfType.Numeric, field.FieldType); + Assert.AreEqual(0, field.NumericScale); + Assert.AreEqual(10, field.Length); + + field = fields["c_ushort"]; + Assert.IsNotNull(field); + Assert.AreEqual(DbfType.Numeric, field.FieldType); + Assert.AreEqual(0, field.NumericScale); + Assert.AreEqual(10, field.Length); + + field = fields["c_string"]; + Assert.IsNotNull(field); + Assert.AreEqual(DbfType.Character, field.FieldType); + Assert.AreEqual(0, field.NumericScale); + Assert.AreEqual(254, field.Length); + + field = fields["c_double"]; + Assert.IsNotNull(field); + Assert.AreEqual(DbfType.Float, field.FieldType); + Assert.AreEqual(11, field.NumericScale); + Assert.AreEqual(19, field.Length); + + field = fields["c_bool"]; + Assert.IsNotNull(field); + Assert.AreEqual(DbfType.Logical, field.FieldType); + Assert.AreEqual(0, field.NumericScale); + Assert.AreEqual(1, field.Length); + + field = fields["c_datetime"]; + Assert.IsNotNull(field); + Assert.AreEqual(DbfType.Date, field.FieldType); + Assert.AreEqual(0, field.NumericScale); + Assert.AreEqual(8, field.Length); + + } + } +} diff --git a/test/NetTopologySuite.IO.Esri.Test/Deprecated/ShapeFile.Extended/TempFileWriter.cs b/test/NetTopologySuite.IO.Esri.Test/Deprecated/ShapeFile.Extended/TempFileWriter.cs new file mode 100644 index 0000000..cb50560 --- /dev/null +++ b/test/NetTopologySuite.IO.Esri.Test/Deprecated/ShapeFile.Extended/TempFileWriter.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; +using System.IO; + +using NUnit.Framework; + +namespace NetTopologySuite.IO.Esri.Test.Deprecated.ShapeFile.Extended +{ + internal sealed class TempFileWriter : IDisposable + { + private List OpenedStreams = new List(); + private bool IsDisposed; + + public TempFileWriter(string ext, byte[] data) + { + this.Path = System.IO.Path.GetFullPath(System.IO.Path.ChangeExtension(TestContext.CurrentContext.Test.ID, ext)); + File.WriteAllBytes(this.Path, data); + } + + public TempFileWriter(string ext, string testFile) + { + string file = System.IO.Path.ChangeExtension(testFile, ext); + string path = TestShapefiles.PathTo(file); + Assert.That(File.Exists(path), Is.True); + + this.Path = System.IO.Path.GetFullPath(System.IO.Path.ChangeExtension(TestContext.CurrentContext.Test.ID, ext)); + byte[] data = File.ReadAllBytes(path); + File.WriteAllBytes(this.Path, data); + } + + public string Path { get; } + + public Stream OpenRead() + { + var stream = File.OpenRead(Path); + OpenedStreams.Add(stream); + return stream; + } + + private void Dispose(bool disposing) + { + if (IsDisposed) + { + return; + } + + if (disposing) + { + // TODO: dispose managed state (managed objects) + } + + foreach (var stream in OpenedStreams) + { + stream.Dispose(); + } + + try + { + File.Delete(this.Path); + } + catch + { + } + + IsDisposed = true; + } + + public void Dispose() + { + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + + ~TempFileWriter() + { + Dispose(disposing: false); + } + } +} diff --git a/test/NetTopologySuite.IO.Esri.Test/Deprecated/ShapeFileIssueNumberAttribute.cs b/test/NetTopologySuite.IO.Esri.Test/Deprecated/ShapeFileIssueNumberAttribute.cs new file mode 100644 index 0000000..ab59773 --- /dev/null +++ b/test/NetTopologySuite.IO.Esri.Test/Deprecated/ShapeFileIssueNumberAttribute.cs @@ -0,0 +1,20 @@ +using System; +using NUnit.Framework; + +namespace NetTopologySuite.IO.Esri.Test.Deprecated +{ + /// + /// The issue number used in this test (or fixture) refers to an issue on + /// https://github.com/NetTopologySuite/NetTopologySuite.IO.ShapeFile, created + /// after this project was split out on its own (and thus, it got its own + /// set of issue numbers). + /// + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)] + public sealed class ShapeFileIssueNumberAttribute : PropertyAttribute + { + public ShapeFileIssueNumberAttribute(int issueNumber) + : base("NetTopologySuite.IO.ShapeFile issue", issueNumber) + { + } + } +} diff --git a/test/NetTopologySuite.IO.Esri.Test/Deprecated/Streams/ByteStreamProviderFixture.cs b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Streams/ByteStreamProviderFixture.cs new file mode 100644 index 0000000..e4a7043 --- /dev/null +++ b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Streams/ByteStreamProviderFixture.cs @@ -0,0 +1,94 @@ +using System; +using System.IO; +using System.Text; +using NUnit.Framework; + +namespace NetTopologySuite.IO.Esri.Test.Deprecated.Streams +{ + [TestFixture] + public class ByteStreamProviderFixture + { + [TestCase("This is sample text", 1252)] + [TestCase("Dies sind deutsche Umlaute: Ää. Öö, Üü, ß", 1252)] + [TestCase("Dies sind deutsche Umlaute: Ää. Öö, Üü, ß", 850)] + [TestCase("Dies sind deutsche Umlaute: Ää. Öö, Üü, ß", 437)] + public void TestConstructorText(string constructorText, int codepage) + { + var encoding = CodePagesEncodingProvider.Instance.GetEncoding(codepage); + + var textField = new Dbf.Fields.DbfCharacterField("test"); + var fields = new Dbf.Fields.DbfField[] { textField }; + using var ms = new MemoryStream(); + + using var dbfWriter = new Dbf.DbfWriter(ms, fields, encoding); + textField.StringValue = constructorText; + dbfWriter.Write(); + + using var dbfReader = new Dbf.DbfReader(ms, encoding); + dbfReader.Read(); + var readText = dbfReader.Fields[textField.Name].Value; + Assert.That(readText, Is.EqualTo(constructorText)); + + } + + [TestCase(50, 50, true)] + [TestCase(50, 100, true)] + [TestCase(50, 50, false)] + [TestCase(50, 100, false)] + public void TestConstructor(int length, int maxLength, bool @readonly) + { + // TODO: Remove no longer relevant test + /* + var bsp = new ByteStreamProvider("Test", CreateData(length), maxLength, @readonly); + Assert.That(bsp.UnderlyingStreamIsReadonly, Is.EqualTo(@readonly)); + Assert.That(bsp.Length, Is.EqualTo(length)); + Assert.That(bsp.MaxLength, Is.EqualTo(maxLength)); + + using (var ms = (MemoryStream)bsp.OpenRead()) + { + byte[] data = ms.ToArray(); + Assert.That(data, Is.Not.Null); + Assert.That(data.Length, Is.EqualTo(length)); + for (int i = 0; i < length; i++) + Assert.That(data[i], Is.EqualTo(bsp.Buffer[i])); + } + + try + { + using (var ms = (MemoryStream)bsp.OpenWrite(false)) + { + var sw = new BinaryWriter(ms); + sw.BaseStream.Position = 50; + for (int i = 0; i < 10; i++) + sw.Write((byte)i); + sw.Flush(); + Assert.That(ms.Length, Is.EqualTo(length+10)); + Assert.That(bsp.Length, Is.EqualTo(length+10)); + Assert.That(bsp.Buffer[59], Is.EqualTo(9)); + } + } + catch (Exception ex) + { + if (ex is AssertionException) + throw; + + if (!@readonly) + { + Assert.That(ex, Is.TypeOf(typeof(NotSupportedException))); + Assert.That(length, Is.EqualTo(maxLength)); + } + } + */ + } + + private static byte[] CreateData(int length) + { + var rnd = new Random(); + + byte[] res = new byte[length]; + for (int i = 0; i < length; i++) + res[i] = (byte) rnd.Next(0, 255); + return res; + } + } +} diff --git a/test/NetTopologySuite.IO.Esri.Test/Deprecated/Streams/ManagedStreamProviderFixture.cs b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Streams/ManagedStreamProviderFixture.cs new file mode 100644 index 0000000..86cb351 --- /dev/null +++ b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Streams/ManagedStreamProviderFixture.cs @@ -0,0 +1,141 @@ +using System; +using System.IO; +using System.Text; +using NUnit.Framework; + +namespace NetTopologySuite.IO.Esri.Test.Deprecated.Streams +{ + [TestFixture] + public class ManagedStreamProviderFixture + { + [TestCase("This is sample text", 1252)] + [TestCase("Dies sind deutsche Umlaute: Ää. Öö, Üü, ß", 1252)] + [TestCase("Dies sind deutsche Umlaute: Ää. Öö, Üü, ß", 850)] + [TestCase("Dies sind deutsche Umlaute: Ää. Öö, Üü, ß", 437)] + public void TestConstructorText(string constructorText, int codepage) + { + var encoding = CodePagesEncodingProvider.Instance.GetEncoding(codepage); + + var textField = new Dbf.Fields.DbfCharacterField("test"); + var fields = new Dbf.Fields.DbfField[] { textField }; + using var ms = new MemoryStream(); + + using var dbfWriter = new Dbf.DbfWriter(ms, fields, encoding); + textField.Value = constructorText; + dbfWriter.Write(); + + using var dbfReader = new Dbf.DbfReader(ms, encoding); + dbfReader.Read(); + var readText = dbfReader.Fields[textField.Name].Value; + Assert.That(readText, Is.EqualTo(constructorText)); + } + + [Test] + public void TestWithReadonlyStream() + { + byte[] sourceData = CreateDbfData(50); + using var sourceStream = new MemoryStream(sourceData, 0, sourceData.Length, false); + Assert.That(!sourceStream.CanWrite); + + using var dbfReader = new Dbf.DbfReader(sourceStream); + TestReading(dbfReader, sourceData); + + Assert.That(() => new Dbf.DbfWriter(sourceStream, dbfReader.Fields), Throws.InstanceOf()); + } + + [Test] + public void TestWithWritableStream() + { + byte[] sourceData = CreateDbfData(50); + using var sourceStream = new MemoryStream(); + sourceStream.Write(sourceData); + sourceStream.Position = 0; + Assert.That(sourceStream.CanWrite); + + using var dbfReader = new Dbf.DbfReader(sourceStream); + TestReading(dbfReader, sourceData); + } + + [Test] + public void TestTruncate() { + // TODO: Remove no longer relevant test + /* + string test = "truncate string"; + + using (var memoryStream = new MemoryStream()) + { + memoryStream.Write(Encoding.ASCII.GetBytes(test)); + memoryStream.Position = 0; + var bsp = new ExternallyManagedStreamProvider("Test", memoryStream); + var stream = bsp.OpenWrite(true); + + Assert.That(stream.Length, Is.EqualTo(0)); + } + */ + } + + [Test] + public void TestTruncateNonSeekableStream() + { + // TODO: Remove no longer relevant test + /* + string test = "truncate string"; + + using (var memoryStream = new NonSeekableStream()) + { + try + { + memoryStream.Write(Encoding.ASCII.GetBytes(test)); + memoryStream.Position = 0; + var bsp = new ExternallyManagedStreamProvider("Test", memoryStream); + var stream = bsp.OpenWrite(true); + } + catch (InvalidOperationException ex) + { + Assert.AreEqual(ex.Message, "The underlying stream doesn't support seeking! You are unable to truncate the data."); + } + + } + */ + } + + private static void TestReading(Dbf.DbfReader dbfReader, byte[] sourceData) + { + using var ms = new MemoryStream(); + using var dbfWriter = new Dbf.DbfWriter(ms, dbfReader.Fields, dbfReader.Encoding); + while (dbfReader.Read()) + { + // We are reusing the fields from dbfReader. + // DbfField.Value is Read() from dbfReader and then the same value + // from the same field is written to dbfWriter. + // So bellow assignment is unnecessary: + // dbfWriter.Fields[0].Value = dbfReader.Fields[0].Value. + dbfWriter.Write(); + } + dbfWriter.Dispose(); // Flush changes. + Assert.That(ms.ToArray(), Is.EqualTo(sourceData)); + } + + private static byte[] CreateDbfData(int length) + { + var textField = new Dbf.Fields.DbfCharacterField("field_name"); + var fields = new Dbf.Fields.DbfField[] { textField }; + using var ms = new MemoryStream(); + + using var dbfWriter = new Dbf.DbfWriter(ms, fields); + for (int i = 0; i < length; i++) + { + textField.Value = "field_value_" + i; + dbfWriter.Write(); + } + dbfWriter.Dispose(); // Write header to underlying stream + + return ms.ToArray(); + } + + private class NonSeekableStream : MemoryStream + { + public override bool CanSeek => false; + } + } +} diff --git a/test/NetTopologySuite.IO.Esri.Test/Deprecated/Various/CascadedPolygonUnionFixture.cs b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Various/CascadedPolygonUnionFixture.cs new file mode 100644 index 0000000..4b05738 --- /dev/null +++ b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Various/CascadedPolygonUnionFixture.cs @@ -0,0 +1,45 @@ +using NetTopologySuite.Geometries; +using NetTopologySuite.Operation.Overlay; +using NetTopologySuite.Operation.Overlay.Snap; +using NetTopologySuite.Operation.Union; +using NUnit.Framework; +using System; +using System.IO; +using System.Linq; + +namespace NetTopologySuite.IO.Esri.Test.Deprecated.Various +{ + [TestFixture] + public class CascadedPolygonUnionFixture + { + protected GeometryFactory Factory { get; private set; } + + protected WKTReader Reader { get; private set; } + + public CascadedPolygonUnionFixture() + { + // Set current dir to shapefiles dir + Environment.CurrentDirectory = TestShapefiles.Directory; + + this.Factory = new GeometryFactory(); + this.Reader = new WKTReader(); + } + + [Test] + public void PerformCascadedPolygonUnion() + { + using var shpStream = File.OpenRead(TestShapefiles.PathTo("tnp_pol.shp")); + var shpReader = Shp.Shp.OpenRead(shpStream); + var collection = shpReader.ToList(); + + var u1 = collection[0]; + for (int i = 1; i < collection.Count; i++) + { + u1 = SnapIfNeededOverlayOp.Overlay(u1, collection[i], SpatialFunction.Union); + } + + var u2 = CascadedPolygonUnion.Union(collection); + Assert.IsTrue(u1.EqualsTopologically(u2)); + } + } +} diff --git a/test/NetTopologySuite.IO.Esri.Test/Deprecated/Various/GraphBuilder2.cs b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Various/GraphBuilder2.cs new file mode 100644 index 0000000..ef8675c --- /dev/null +++ b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Various/GraphBuilder2.cs @@ -0,0 +1,304 @@ +using NetTopologySuite.Geometries; +using QuickGraph; +using QuickGraph.Algorithms.Observers; +using QuickGraph.Algorithms.ShortestPath; +using System; +using System.Collections.Generic; + +namespace NetTopologySuite.IO.Esri.Test.Deprecated.Various +{ + + /// + /// A class that manages shortest path computation. + /// + public class GraphBuilder2 + { + #region Delegates + + /// + /// A delegate that defines how to calculate the weight + /// of a line. + /// + /// A line. + /// The weight of the line. + public delegate double ComputeWeightDelegate(LineString line); + + #endregion + + private static readonly ComputeWeightDelegate DefaultComputer = line => line.Length; + + private readonly bool bidirectional; + + private readonly AdjacencyGraph> graph; + private readonly IList strings; + private IDictionary, double> consts; + private GeometryFactory factory; + + /// + /// Initializes a new instance of the class. + /// + /// + /// Specify if the graph must be build using both edges directions. + /// + public GraphBuilder2(bool bidirectional) + { + this.bidirectional = bidirectional; + + factory = null; + strings = new List(); + graph = new AdjacencyGraph>(true); + } + + /// + /// Initializes a new instance of the class, + /// using a directed graph. + /// + public GraphBuilder2() : this(false) + { + } // TODO: maybe the default value must be true... + + /// + /// Adds each line to the graph structure. + /// + /// + /// + /// true if all lines + /// are added, false otherwise. + /// + /// + /// If geometries don't have the same factory. + /// + public bool Add(params LineString[] lines) + { + bool result = true; + foreach (var line in lines) + { + var newfactory = line.Factory; + if (factory == null) + factory = newfactory; + else if (!newfactory.PrecisionModel.Equals(factory.PrecisionModel)) + throw new TopologyException("all geometries must have the same precision model"); + + bool lineFound = strings.Contains(line); + result &= !lineFound; + if (!lineFound) + strings.Add(line); + else continue; // Skip vertex check because line is already present + + foreach (var coord in line.Coordinates) + { + if (!graph.ContainsVertex(coord)) + graph.AddVertex(coord); + } + } + return result; + } + + /// + /// Initialize the algorithm using the default + /// weight computer, + /// that uses string length + /// as weight value. + /// + /// + /// If you've don't added two or more geometries to the builder. + /// + /// + /// If builder is already initialized. + /// + public void Initialize() + { + BuildEdges(DefaultComputer); + } + + /// + /// Initialize the algorithm using the specified + /// weight computer + /// + /// + /// A function that computes the weight + /// of any edge of the graph. + /// + /// + /// If you've don't added two or more geometries to the builder. + /// + /// + /// If builder is already initialized. + /// + public void Initialize(ComputeWeightDelegate computer) + { + BuildEdges(computer); + } + + /// + /// + /// + /// + private void BuildEdges(ComputeWeightDelegate computer) + { + if (strings.Count < 2) + throw new TopologyException("you must specify two or more geometries to build a graph"); + + // Counts the number of edges in the set we pass to this method. + int numberOfEdgesInLines = 0; + foreach (var str in strings) + { + int edges = str.Coordinates.GetUpperBound(0); + numberOfEdgesInLines += edges; + } + + // Double values because we use also reversed edges... + if (bidirectional) + numberOfEdgesInLines *= 2; + + consts = new Dictionary, double>(numberOfEdgesInLines); + + foreach (var line in strings) + { + // A line has to have at least two dimensions + int bound = line.Coordinates.GetUpperBound(0); + if (bound > 0) + { + for (int counter = 0; counter < bound; counter++) + { + // Prepare a segment + var src = line.Coordinates[counter]; + var dst = line.Coordinates[counter + 1]; + + // Here we calculate the weight of the edge + var lineString = factory.CreateLineString( + new[] { src, dst, }); + double weight = computer(lineString); + + // Add the edge + IEdge localEdge = new Edge(src, dst); + graph.AddEdge(localEdge); + consts.Add(localEdge, weight); + + if (!bidirectional) + continue; + + // Add the reversed edge + IEdge localEdgeRev = new Edge(dst, src); + graph.AddEdge(localEdgeRev); + consts.Add(localEdgeRev, weight); + } + } + } + } + + /// + /// Carries out the shortest path anlayis between the two + /// nodes + /// passed as variables and returns an + /// giveing the shortest path. + /// + /// The source geom + /// The destination geom + /// A line string geometric shape of the path + public LineString Perform(Geometry source, Geometry destination) + { + return Perform(source.Coordinate, destination.Coordinate); + } + + /// + /// Carries out the shortest path between the two nodes + /// ids passed as variables and returns an + /// giveing the shortest path. + /// + /// The source node + /// The destination node + /// A line string geometric shape of the path + public LineString Perform(Coordinate source, Coordinate destination) + { + if (!graph.ContainsVertex(source)) + throw new ArgumentException("key not found in the graph", "source"); + if (!graph.ContainsVertex(destination)) + throw new ArgumentException("key not found in the graph", "destination"); + + // Build algorithm + var dijkstra = + new DijkstraShortestPathAlgorithm>(graph, edge => consts[edge]); + + // Attach a Distance observer to give us the distances between edges + var distanceObserver = + new VertexDistanceRecorderObserver>(edge => consts[edge]); + distanceObserver.Attach(dijkstra); + + // Attach a Vertex Predecessor Recorder Observer to give us the paths + var predecessorObserver = + new VertexPredecessorRecorderObserver>(); + predecessorObserver.Attach(dijkstra); + + // Run the algorithm with A set to be the source + dijkstra.Compute(source); + + // Get the path computed to the destination. + IEnumerable> path; + bool result = predecessorObserver.TryGetPath(destination, out path); + + // Then we need to turn that into a geomery. + return result ? BuildString(new List>(path)) : null; + + // if the count is greater than one then a + // path could not be found, so we return null + } + + /// + /// Takes the path returned from QuickGraph library and uses the + /// list of coordinates to reconstruct the path into a geometric + /// "shape" + /// + /// Shortest path from the QucikGraph Library + /// + private LineString BuildString(IList> path) + { + // if the path has no links then return a null reference + if (path.Count < 1) + return null; + + // if we get here then we now that there is at least one + // edge in the path. + var links = new Coordinate[path.Count + 1]; + + // Add each node to the list of coordinates in to the array. + int i; + for (i = 0; i < path.Count; i++) + links[i] = path[i].Source; + + // Add the target node to the last loction in the list + links[i] = path[i - 1].Target; + + // Turn the list of coordinates into a geometry. + var thePath = factory.CreateLineString(links); + return thePath; + } + + /// + /// Outputs the graph as a DIMACS Graph + /// + /// The name of the output graph + /// Indicates if the method was worked. + public bool WriteAsDIMACS(string fileName) + { + // The DIMACS format is a reasonabley standard method + // of preparing graphs for analys in SP algortihm. + // This method *could* be used to prepare the graph beforehand + // so the turning from a GIS layer into a graph is not so + // intensive. + // + // NOTE: Follows the 9th DIMACS format: http://www.dis.uniroma1.it/~challenge9/format.shtml + return false; + } + + /// + /// + /// + /// + /// + public bool ReadFromDIMACS(string fileName) + { + return false; + } + } +} diff --git a/test/NetTopologySuite.IO.Esri.Test/Deprecated/Various/GraphBuilder2Test.cs b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Various/GraphBuilder2Test.cs new file mode 100644 index 0000000..f54e161 --- /dev/null +++ b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Various/GraphBuilder2Test.cs @@ -0,0 +1,410 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using NetTopologySuite.Features; +using NetTopologySuite.Geometries; +using NUnit.Framework; + +namespace NetTopologySuite.IO.Esri.Test.Deprecated.Various +{ + [TestFixture] + public class GraphBuilder2Test + { + #region Setup/Teardown + + [SetUp] + public void Setup() + { + factory = GeometryFactory.Fixed; + + a = factory.CreateLineString(new Coordinate[] + { + new Coordinate(0, 0), + new Coordinate(100, 0), + new Coordinate(200, 100), + new Coordinate(200, 200), + }); + b = factory.CreateLineString(new Coordinate[] + { + new Coordinate(0, 0), + new Coordinate(100, 100), + new Coordinate(200, 200), + }); + c = factory.CreateLineString(new Coordinate[] + { + new Coordinate(0, 0), + new Coordinate(0, 100), + new Coordinate(100, 200), + new Coordinate(200, 200), + }); + d = factory.CreateLineString(new Coordinate[] + { + new Coordinate(0, 0), + new Coordinate(300, 0), + new Coordinate(300, 200), + new Coordinate(150, 200), + new Coordinate(150, 300), + }); + e = factory.CreateLineString(new Coordinate[] + { + new Coordinate(100, 300), + new Coordinate(150, 300), + new Coordinate(200, 300), + }); + + result = factory.CreateLineString(new Coordinate[] + { + new Coordinate(0, 0), + new Coordinate(300, 0), + new Coordinate(300, 200), + new Coordinate(150, 200), + new Coordinate(150, 300), + }); + revresult = (LineString)result.Reverse(); + + start = a.StartPoint; + end = d.EndPoint; + } + + #endregion + + private const string shp = ".shp"; + private const string shx = ".shx"; + private const string dbf = ".dbf"; + + private GeometryFactory factory; + private LineString a, b, c, d, e; + private LineString result, revresult; + private Point start, end; + + /// + /// Loads the shapefile as a graph allowing SP analysis to be carried out + /// + /// The name of the shape file we want to load + /// + /// + public static LineString TestGraphBuilder2WithSampleGeometries(string fileName, Coordinate src, Coordinate dst) + { + var geometries = Shapefile.ReadAllGeometries(fileName); + var edges = new GeometryCollection(geometries); + return TestGraphBuilder2WithSampleGeometries(edges, src, dst); + } + + /// + /// Uses the passed geometry collection to generate a QuickGraph. + /// + /// + /// + /// + public static LineString TestGraphBuilder2WithSampleGeometries(GeometryCollection edges, Coordinate src, Coordinate dst) + { + var builder = new GraphBuilder2(true); + foreach (var edge in edges.Geometries.Cast()) + { + foreach (var line in edge.Geometries.Cast()) + { + builder.Add(line); + } + } + builder.Initialize(); + + return builder.Perform(src, dst); + } + + [SetUp] + public void FixtureSetup() + { + Environment.CurrentDirectory = TestShapefiles.Directory; + } + + private static void SaveGraphResult(Geometry path) + { + if (path == null) + throw new ArgumentNullException(nameof(path)); + + const string shapepath = "graphresult"; + TestShapefiles.DeleteShp(shapepath); + Assert.IsFalse(File.Exists(shapepath + shp)); + Assert.IsFalse(File.Exists(shapepath + shx)); + Assert.IsFalse(File.Exists(shapepath + dbf)); + + const string field1 = "OBJECTID"; + var feature = new Feature(path, new AttributesTable()); + feature.Attributes.Add(field1, 0); + + Shapefile.WriteAllFeatures(new[] { feature }, shapepath); + + Assert.IsTrue(File.Exists(shapepath + shp)); + Assert.IsTrue(File.Exists(shapepath + shx)); + Assert.IsTrue(File.Exists(shapepath + dbf)); + } + + [Test] + [Ignore("graph.shp not present")] + public void BuildGraphFromCompleteGraphShapefile() + { + const string shapepath = "graph.shp"; + const int count = 1179; + + Assert.IsTrue(File.Exists(shapepath)); + var geometries = Shapefile.ReadAllGeometries(shapepath); + var edges = new GeometryCollection(geometries); + Assert.IsNotNull(edges); + Assert.IsInstanceOf(typeof(GeometryCollection), edges); + Assert.AreEqual(count, edges.NumGeometries); + + var startls = edges.GetGeometryN(515).GetGeometryN(0) as LineString; + Assert.IsNotNull(startls); + var startPoint = startls.EndPoint; + Assert.AreEqual(2317300d, startPoint.X); + Assert.AreEqual(4843961d, startPoint.Y); + + var endls = edges.GetGeometryN(141).GetGeometryN(0) as LineString; + ; + Assert.IsNotNull(endls); + var endPoint = endls.StartPoint; + Assert.AreEqual(2322739d, endPoint.X); + Assert.AreEqual(4844539d, endPoint.Y); + + var builder = new GraphBuilder2(true); + foreach (MultiLineString mlstr in edges.Geometries.Cast()) + { + Assert.AreEqual(1, mlstr.NumGeometries); + var str = mlstr.GetGeometryN(0) as LineString; + Assert.IsNotNull(str); + Assert.IsTrue(builder.Add(str)); + } + builder.Initialize(); + + var path = builder.Perform(startPoint, endPoint); + Assert.IsNotNull(path); + SaveGraphResult(path); + + var reverse = builder.Perform(endPoint, startPoint); + Assert.IsNotNull(reverse); + Assert.AreEqual(path, reverse.Reverse()); + } + + [Test] + [Ignore("minimalgraph.shp not present")] + public void BuildGraphFromMinimalGraphShapefile() + { + const string shapepath = "minimalgraph.shp"; + const int count = 15; + + Assert.IsTrue(File.Exists(shapepath)); + var geometries = Shapefile.ReadAllGeometries(shapepath); + var edges = new GeometryCollection(geometries); + Assert.IsNotNull(edges); + Assert.IsInstanceOf(typeof(GeometryCollection), edges); + Assert.AreEqual(count, edges.NumGeometries); + + var startls = edges.GetGeometryN(0).GetGeometryN(0) as LineString; + Assert.IsNotNull(startls); + var endls = edges.GetGeometryN(5).GetGeometryN(0) as LineString; + ; + Assert.IsNotNull(endls); + + var builder = new GraphBuilder2(true); + foreach (var mlstr in edges.Geometries.Cast()) + { + Assert.AreEqual(1, mlstr.NumGeometries); + var str = mlstr.GetGeometryN(0) as LineString; + Assert.IsNotNull(str); + Assert.IsTrue(builder.Add(str)); + } + builder.Initialize(); + + var path = builder.Perform(startls.StartPoint, endls.EndPoint); + Assert.IsNotNull(path); + } + + [Test] + [Ignore("strade_fixed.shp not present")] + public void BuildGraphFromStradeShapefile() + { + string shapepath = "strade_fixed.shp"; + int count = 703; + + Assert.IsTrue(File.Exists(shapepath)); + var geometries = Shapefile.ReadAllGeometries(shapepath); + var edges = new GeometryCollection(geometries); + Assert.IsNotNull(edges); + Assert.IsInstanceOf(typeof(GeometryCollection), edges); + Assert.AreEqual(count, edges.NumGeometries); + + var startCoord = new Coordinate(2317300d, 4843961d); + var endCoord = new Coordinate(2322739d, 4844539d); + + bool startFound = false; + bool endFound = false; + var builder = new GraphBuilder2(true); + foreach (var mlstr in edges.Geometries.Cast()) + { + Assert.AreEqual(1, mlstr.NumGeometries); + var str = mlstr.GetGeometryN(0) as LineString; + Assert.IsNotNull(str); + Assert.IsTrue(builder.Add(str)); + + if (!startFound) + { + var coords = new List(str.Coordinates); + if (coords.Contains(startCoord)) + startFound = true; + } + + if (!endFound) + { + var coords = new List(str.Coordinates); + if (coords.Contains(endCoord)) + endFound = true; + } + } + builder.Initialize(); + Assert.IsTrue(startFound); + Assert.IsTrue(endFound); + + var path = builder.Perform(startCoord, endCoord); + Assert.IsNotNull(path); + SaveGraphResult(path); + + var reverse = builder.Perform(startCoord, endCoord); + Assert.IsNotNull(reverse); + Assert.AreEqual(path, reverse.Reverse()); + } + + [Ignore("strade.shp not present")] + [Test] + public void BuildStradeFixed() + { + string path = "strade" + shp; + Assert.IsTrue(File.Exists(path)); + + using var reader = Shapefile.OpenRead(path); + var features = new List(reader.RecordCount); + while (reader.Read()) + { + var feature = new Feature(reader.Geometry, new AttributesTable()); + object[] values = reader.Fields.GetValues(); + for (int i = 0; i < values.Length; i++) + { + string name = reader.Fields[i].Name; + object value = values[i]; + feature.Attributes.Add(name, value); + } + features.Add(feature); + } + Assert.AreEqual(703, features.Count); + + string shapepath = "strade_fixed"; + TestShapefiles.DeleteShp(shapepath); + Assert.IsFalse(File.Exists(shapepath + shp)); + Assert.IsFalse(File.Exists(shapepath + shx)); + Assert.IsFalse(File.Exists(shapepath + dbf)); + + Shapefile.WriteAllFeatures(features, shapepath); + + Assert.IsTrue(File.Exists(shapepath + shp)); + Assert.IsTrue(File.Exists(shapepath + shx)); + Assert.IsTrue(File.Exists(shapepath + dbf)); + } + + [Test] + public void CheckGraphBuilder2ExceptionUsingARepeatedGeometry() + { + Assert.Catch(() => + { + var builder = new GraphBuilder2(); + Assert.IsTrue(builder.Add(a)); + Assert.IsFalse(builder.Add(a)); + Assert.IsFalse(builder.Add(a, a)); + builder.Initialize(); + }); + } + + [Test] + public void CheckGraphBuilder2ExceptionUsingDifferentFactories() + { + Assert.Catch(() => + { + var builder = new GraphBuilder2(); + Assert.IsTrue(builder.Add(a)); + Assert.IsTrue(builder.Add(b, c)); + Assert.IsTrue(builder.Add(d)); + builder.Add(GeometryFactory.Default.CreateLineString(new Coordinate[] + { + new Coordinate(0, 0), + new Coordinate(50, 50), + })); + }); + } + + [Test] + public void CheckGraphBuilder2ExceptionUsingDoubleInitialization() + { + var builder = new GraphBuilder2(); + builder.Add(a); + builder.Add(b, c); + builder.Add(d); + builder.Add(e); + builder.Initialize(); + builder.Initialize(); + } + + [Test] + public void CheckGraphBuilder2ExceptionUsingNoGeometries() + { + Assert.Catch(() => + { + var builder = new GraphBuilder2(); + builder.Initialize(); + }); + } + + [Test] + public void CheckGraphBuilder2ExceptionUsingOneGeometry() + { + Assert.Catch(() => + { + var builder = new GraphBuilder2(); + Assert.IsTrue(builder.Add(a)); + builder.Initialize(); + }); + } + + [Test] + public void TestBidirectionalGraphBuilder2WithSampleGeometries() + { + var builder = new GraphBuilder2(true); + builder.Add(a); + builder.Add(b, c); + builder.Add(d); + builder.Add(e); + builder.Initialize(); + + var path = builder.Perform(start.Coordinate, end.Coordinate); + Assert.IsNotNull(path); + Assert.AreEqual(result, path); + + var revpath = builder.Perform(end, start); + Assert.IsNotNull(revpath); + Assert.AreEqual(revresult, revpath); + } + + [Test] + public void TestGraphBuilder2WithSampleGeometries() + { + var builder = new GraphBuilder2(); + builder.Add(a); + builder.Add(b, c); + builder.Add(d); + builder.Add(e); + builder.Initialize(); + + var path = builder.Perform(start, end); + Assert.IsNotNull(path); + Assert.AreEqual(result, path); + } + } +} diff --git a/test/NetTopologySuite.IO.Esri.Test/Deprecated/Various/NormalizeTest.cs b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Various/NormalizeTest.cs new file mode 100644 index 0000000..e5421de --- /dev/null +++ b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Various/NormalizeTest.cs @@ -0,0 +1,95 @@ +using System.Linq; +using NetTopologySuite.Geometries; +using NUnit.Framework; + +namespace NetTopologySuite.IO.Esri.Test.Deprecated.Various +{ + /// + /// + /// + [TestFixture] + public class NormalizeTest + { + protected GeometryFactory Factory { get; private set; } + + protected WKTReader Reader { get; private set; } + + private Polygon _polygon = null; + private LinearRing _shell = null; + private LinearRing _hole = null; + + /// + /// Initializes a new instance of the class. + /// + public NormalizeTest() : base() { } + + /// + /// Method called prior to every test in this fixture + /// + [SetUp] + public void Init() + { + this.Factory = new GeometryFactory(); + this.Reader = new WKTReader(); + + // NOTE: Shell is created with not correct order of coordinates (should be clockwise order) + _shell = Factory.CreateLinearRing(new Coordinate[] { new Coordinate(100,100), + new Coordinate(200,100), + new Coordinate(200,200), + new Coordinate(100,200), + new Coordinate(100,100), }); + + _hole = Factory.CreateLinearRing(new Coordinate[] { new Coordinate(120,120), + new Coordinate(180,120), + new Coordinate(180,180), + new Coordinate(120,180), + new Coordinate(120,120), }); + _polygon = Factory.CreatePolygon(_shell, new LinearRing[] { _hole, }); + } + + /// + /// + /// + [Test] + public void NotNormalizedGDBOperation() + { + var shpPath = TestShapefiles.GetTempShpPath(); + using (var shpWriter = Shapefile.OpenWrite(shpPath, new(ShapeType.Polygon))) + { + shpWriter.Geometry = new MultiPolygon(new[] { _polygon }); + shpWriter.Write(); + } + var firstGeometry = Shapefile.ReadAllGeometries(shpPath).First(); + var test = firstGeometry.GetGeometryN(0) as Polygon; + Assert.IsNotNull(test); + + Assert.IsTrue(test is IPolygonal); + Assert.IsFalse(_polygon.EqualsExact(test)); // SHP shells and holes are always written with correct coordinate order + + + } + + /// + /// + /// + [Test] + public void NormalizedGDBOperation() + { + _polygon.Normalize(); + + var shpPath = TestShapefiles.GetTempShpPath(); + using (var shpWriter = Shapefile.OpenWrite(shpPath, new(ShapeType.Polygon))) + { + shpWriter.Geometry = new MultiPolygon(new[] { _polygon }); + shpWriter.Write(); + } + var firstGeometry = Shapefile.ReadAllGeometries(shpPath).First(); + var test = firstGeometry.GetGeometryN(0) as Polygon; + Assert.IsNotNull(test); + + Assert.IsNotNull(test); + Assert.IsTrue(test is IPolygonal); + Assert.IsTrue(_polygon.EqualsExact(test)); + } + } +} diff --git a/test/NetTopologySuite.IO.Esri.Test/Deprecated/Various/PathFinder.cs b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Various/PathFinder.cs new file mode 100644 index 0000000..6ce3621 --- /dev/null +++ b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Various/PathFinder.cs @@ -0,0 +1,291 @@ +using NetTopologySuite.Geometries; +using NetTopologySuite.Operation.Linemerge; +using QuickGraph; +using QuickGraph.Algorithms.Observers; +using QuickGraph.Algorithms.ShortestPath; +using System; +using System.Collections.Generic; + +namespace NetTopologySuite.IO.Esri.Test.Deprecated.Various +{ + /// + /// A class that manages shortest path computation. + /// + public class PathFinder + { + /// + /// A delegate that defines how to calculate the weight + /// of a line. + /// + /// A line. + /// The weight of the line. + public delegate double ComputeWeightDelegate(LineString line); + + private static readonly ComputeWeightDelegate DefaultComputer = line => line.Length; + + private readonly bool bidirectional; + + private GeometryFactory factory; + private readonly List strings; + + private readonly AdjacencyGraph> graph; + private IDictionary, double> consts; + + /// + /// Initializes a new instance of the class. + /// + /// + /// Specify if the graph must be build using both edges directions. + /// + public PathFinder(bool bidirectional) + { + this.bidirectional = bidirectional; + + factory = null; + strings = new List(); + graph = new AdjacencyGraph>(true); + } + + /// + /// Initializes a new instance of the class, + /// using a directed graph. + /// + public PathFinder() : this(false) { } + + /// + /// Adds each line to the graph structure. + /// + /// + /// + /// true if all lines + /// are added, false otherwise. + /// + /// + /// If geometries don't have the same factory. + /// + public bool Add(params LineString[] lines) + { + bool result = true; + foreach (var line in lines) + { + var newfactory = line.Factory; + if (factory == null) + factory = newfactory; + else if (!newfactory.PrecisionModel.Equals(factory.PrecisionModel)) + throw new TopologyException("all geometries must have the same precision model"); + + bool lineFound = strings.Contains(line); + result &= !lineFound; + if (!lineFound) + strings.Add(line); + else continue; // Skip vertex check because line is already present + + var coordinates = line.Coordinates; + int start = 0; + int end = coordinates.GetUpperBound(0); + AddCoordinateToGraph(coordinates[start]); // StartPoint + AddCoordinateToGraph(coordinates[end]); // EndPoint + } + return result; + } + + /// + /// + /// + /// + private void AddCoordinateToGraph(Coordinate coord) + { + if (!graph.ContainsVertex(coord)) + graph.AddVertex(coord); + } + + /// + /// Initialize the algorithm using the default + /// weight computer, + /// that uses string length + /// as weight value. + /// + /// + /// If you've don't added two or more geometries to the builder. + /// + /// + /// If builder is already initialized. + /// + public void Initialize() + { + BuildEdges(DefaultComputer); + } + + /// + /// Initialize the algorithm using the specified + /// weight computer + /// + /// + /// A function that computes the weight + /// of any edge of the graph. + /// + /// + /// If you've don't added two or more geometries to the builder. + /// + /// + /// If builder is already initialized. + /// + public void Initialize(ComputeWeightDelegate computer) + { + BuildEdges(computer); + } + + /// + /// + /// + /// + private void BuildEdges(ComputeWeightDelegate computer) + { + if (strings.Count < 2) + throw new TopologyException("you must specify two or more geometries to build a graph"); + + // Counts the number of edges in the set we pass to this method. + int numberOfEdgesInLines = strings.Count * 2; + + // Double values because we use also reversed edges... + if (bidirectional) + numberOfEdgesInLines *= 2; + + consts = new Dictionary, double>(numberOfEdgesInLines); + + foreach (var line in strings) + { + // Prepare a segment + var coordinates = line.Coordinates; + int start = 0; + int end = coordinates.GetUpperBound(0); + var src = coordinates[start]; + var dst = coordinates[end]; + + // Here we calculate the weight of the edge + double weight = computer(line); + + // Add the edge + IEdge localEdge = new Edge(src, dst); + graph.AddEdge(localEdge); + consts.Add(localEdge, weight); + + if (bidirectional) + { + // Add the reversed edge + IEdge localEdgeRev = new Edge(dst, src); + graph.AddEdge(localEdgeRev); + consts.Add(localEdgeRev, weight); + } + } + } + + /// + /// Carries out the shortest path anlayis between the two + /// nodes + /// passed as variables and returns an + /// giveing the shortest path. + /// + /// The source geom + /// The destination geom + /// A or a + /// with all the elements of the graph that composes the shortest path, + /// sequenced using a . + /// + public Geometry Find(Geometry source, Geometry destination) + { + return Find(source.Coordinate, destination.Coordinate); + } + + /// + /// Carries out the shortest path between the two nodes + /// ids passed as variables and returns an + /// giveing the shortest path. + /// + /// The source node + /// The destination node + /// A or a + /// with all the elements of the graph that composes the shortest path, + /// sequenced using a . + /// + public Geometry Find(Coordinate source, Coordinate destination) + { + if (!graph.ContainsVertex(source)) + throw new ArgumentException("key not found in the graph", "source"); + if (!graph.ContainsVertex(destination)) + throw new ArgumentException("key not found in the graph", "destination"); + + // Build algorithm + var dijkstra = + new DijkstraShortestPathAlgorithm>(graph, edge => consts[edge]); + + // Attach a Distance observer to give us the distances between edges + var distanceObserver = + new VertexDistanceRecorderObserver>(edge => consts[edge]); + distanceObserver.Attach(dijkstra); + + // Attach a Vertex Predecessor Recorder Observer to give us the paths + var predecessorObserver = + new VertexPredecessorRecorderObserver>(); + predecessorObserver.Attach(dijkstra); + + // Run the algorithm with A set to be the source + dijkstra.Compute(source); + + // Get the path computed to the destination. + IEnumerable> path; + bool result = predecessorObserver.TryGetPath(destination, out path); + + // Then we need to turn that into a geomery. + return result ? BuildString(new List>(path)) : null; + } + + /// + /// Takes the path returned from QuickGraph library and uses the + /// list of coordinates to reconstruct the path into a geometric + /// "shape" + /// + /// Shortest path from the QucikGraph Library + /// + /// A or a + /// with all the elements of the graph that composes the shortest path, + /// sequenced using a . + /// + private Geometry BuildString(ICollection> paths) + { + // if the path has no links then return a null reference + if (paths.Count < 1) + return null; + + var collector = new LineSequencer(); + foreach (var path in paths) + { + var src = path.Source; + var dst = path.Target; + foreach (var str in strings) + { + if (IsBound(str, src) && IsBound(str, dst)) + collector.Add(str); + } + } + + var sequence = collector.GetSequencedLineStrings(); + return sequence; + } + + /// + /// + /// + /// + /// + /// + private static bool IsBound(Geometry str, Coordinate src) + { + var coordinates = str.Coordinates; + int start = 0; + int end = str.Coordinates.GetUpperBound(0); + return coordinates[start].Equals(src) || + coordinates[end].Equals(src); + } + } +} diff --git a/test/NetTopologySuite.IO.Esri.Test/Deprecated/Various/PathFinderTest.cs b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Various/PathFinderTest.cs new file mode 100644 index 0000000..62c563d --- /dev/null +++ b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Various/PathFinderTest.cs @@ -0,0 +1,187 @@ +using NetTopologySuite.Geometries; +using NetTopologySuite.Features; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.IO; + +namespace NetTopologySuite.IO.Esri.Test.Deprecated.Various +{ + [TestFixture] + public class PathFinderTest + { + private const string shp = ".shp"; + private const string shx = ".shx"; + private const string dbf = ".dbf"; + + ShapefileReaderOptions options; + + [SetUp] + public void FixtureSetup() + { + Environment.CurrentDirectory = TestShapefiles.Directory; + + options = new ShapefileReaderOptions() + { + Factory = GeometryFactory.Fixed + }; + } + + [Ignore("strade.shp is not present")] + [Test] + public void BuildStradeFixed() + { + string path = "strade" + shp; + Assert.IsTrue(File.Exists(path)); + + using var reader = Shapefile.OpenRead(path, options); + var features = new List(reader.RecordCount); + while (reader.Read()) + { + var feature = new Feature(reader.Geometry, new AttributesTable()); + object[] values = reader.Fields.GetValues(); + for (int i = 0; i < values.Length; i++) + { + string name = reader.Fields[i].Name; + object value = values[i]; + feature.Attributes.Add(name, value); + } + features.Add(feature); + } + Assert.AreEqual(703, features.Count); + + string shapepath = "strade_fixed"; + TestShapefiles.DeleteShp(shapepath); + Assert.IsFalse(File.Exists(shapepath + shp)); + Assert.IsFalse(File.Exists(shapepath + shx)); + Assert.IsFalse(File.Exists(shapepath + dbf)); + + Shapefile.WriteAllFeatures(features, shapepath); + + Assert.IsTrue(File.Exists(shapepath + shp)); + Assert.IsTrue(File.Exists(shapepath + shx)); + Assert.IsTrue(File.Exists(shapepath + dbf)); + } + + private Geometry LoadGraphResult() + { + string path = "graphresult.shp"; + Assert.IsTrue(Path.GetExtension(path) == shp); + + var geometries = Shapefile.ReadAllGeometries(path); + var coll = new GeometryCollection(geometries); + Assert.AreEqual(1, coll.Count); + + var geom = coll.GetGeometryN(0); + Assert.IsInstanceOf(typeof(MultiLineString), geom); + var str = geom.GetGeometryN(0); + Assert.IsInstanceOf(typeof(LineString), str); + return str; + } + + private void SaveGraphResult(Geometry path) + { + if (path == null) + throw new ArgumentNullException("path"); + + string shapepath = "graphresult"; + TestShapefiles.DeleteShp(shapepath); + Assert.IsFalse(File.Exists(shapepath + shp)); + Assert.IsFalse(File.Exists(shapepath + shx)); + Assert.IsFalse(File.Exists(shapepath + dbf)); + + string field1 = "OBJECTID"; + var feature = new Feature(path, new AttributesTable()); + feature.Attributes.Add(field1, 0); + + Shapefile.WriteAllFeatures(new[] { feature }, shapepath); + + Assert.IsTrue(File.Exists(shapepath + shp)); + Assert.IsTrue(File.Exists(shapepath + shx)); + Assert.IsTrue(File.Exists(shapepath + dbf)); + } + + [Test] + [Ignore("graph.shp is not present")] + public void BuildGraphFromCompleteGraphShapefile() + { + string shapepath = "graph.shp"; + int count = 1179; + + Assert.IsTrue(File.Exists(shapepath), string.Format("File not found: '{0}'", shapepath)); + var geometries = Shapefile.ReadAllGeometries(shapepath); + var edges = new GeometryCollection(geometries); + Assert.IsNotNull(edges); + Assert.IsInstanceOf(typeof(GeometryCollection), edges); + Assert.AreEqual(count, edges.NumGeometries); + + // Insert arbitrary userdata + for (int i = 0; i < count; i++) + { + var g = edges.GetGeometryN(i) as MultiLineString; + Assert.IsNotNull(g); + var ls = g.GetGeometryN(0) as LineString; + Assert.IsNotNull(ls); + + Assert.IsNull(ls.UserData); + ls.UserData = i; + Assert.IsNotNull(ls.UserData); + } + + var startls = edges.GetGeometryN(515).GetGeometryN(0) as LineString; + Assert.IsNotNull(startls); + var startPoint = startls.EndPoint; + Assert.AreEqual(2317300d, startPoint.X); + Assert.AreEqual(4843961d, startPoint.Y); + + var endls = edges.GetGeometryN(141).GetGeometryN(0) as LineString; ; + Assert.IsNotNull(endls); + var endPoint = endls.StartPoint; + Assert.AreEqual(2322739d, endPoint.X); + Assert.AreEqual(4844539d, endPoint.Y); + + var finder = new PathFinder(true); + foreach (MultiLineString mlstr in edges.Geometries) + { + Assert.AreEqual(1, mlstr.NumGeometries); + var str = mlstr.GetGeometryN(0) as LineString; + Assert.IsNotNull(str); + Assert.IsNotNull(str.UserData); + Assert.IsTrue(finder.Add(str)); + } + finder.Initialize(); + + int expectedResultCount = 8; + var path = finder.Find(startPoint, endPoint); + Assert.IsNotNull(path); + Assert.IsInstanceOf(typeof(MultiLineString), path); + var strings = (MultiLineString)path; + Assert.AreEqual(expectedResultCount, strings.NumGeometries); + foreach (var g in strings.Geometries) + { + Assert.IsNotNull(g.UserData); + Console.WriteLine("{0} : {1}", g.UserData, g); + } + + var reversedPath = finder.Find(endPoint, startPoint); + Assert.IsNotNull(reversedPath); + Assert.IsInstanceOf(typeof(MultiLineString), reversedPath); + + var reversedStrings = (MultiLineString)reversedPath; + Assert.AreEqual(expectedResultCount, reversedStrings.NumGeometries); + foreach (var g in reversedStrings.Geometries) + { + Assert.IsNotNull(g.UserData); + Console.WriteLine("{0} : {1}", g.UserData, g); + } + + for (int i = 0; i < expectedResultCount; i++) + { + var item = strings.GetGeometryN(i); + var itemReversed = strings.GetGeometryN(expectedResultCount - 1 - i); + Assert.AreNotEqual(item.UserData, itemReversed.UserData); + Assert.AreNotEqual(item, itemReversed); + } + } + } +} diff --git a/test/NetTopologySuite.IO.Esri.Test/Deprecated/Various/UnionAggregateTest.cs b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Various/UnionAggregateTest.cs new file mode 100644 index 0000000..55158d7 --- /dev/null +++ b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Various/UnionAggregateTest.cs @@ -0,0 +1,178 @@ +using NetTopologySuite.Geometries; +using NetTopologySuite.Operation.Union; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; + +namespace NetTopologySuite.IO.Esri.Test.Deprecated.Various +{ + /// + /// + /// + [TestFixture] + public class UnionAggregateTest + { + /// + /// + /// + [SetUp] + public void SetUp() + { + // Set current dir to shapefiles dir + Environment.CurrentDirectory = TestShapefiles.Directory; + } + + /// + /// + /// + [Test()] + [Ignore("sa_region.shp not present")] + public void PerformUnionAggregateTest1() + { + Assert.Catch(() => + { + Assert.IsNotNull(CheckShapefile("sa_region")); + }); + } + + /// + /// + /// + [Test] + public void PerformUnionAggregateTest2() + { + Assert.IsNotNull(CheckShapefile("CA_Cable_region")); + } + + /// + /// + /// + [Test] + public void PerformUnionAggregateTest3() + { + Assert.IsNotNull(CheckShapefile("US_DMA_region.shp")); + } + + /// + /// + /// + /// + /// + private static Geometry CheckShapefile(string fileName) + { + //int count = 0; + var geoms = new List(); + var options = new ShapefileReaderOptions() { Factory = GeometryFactory.Floating }; + + using var reader = Shapefile.OpenRead(fileName, options); + while (reader.Read()) + { + var current = reader.Geometry; + if (!current.IsValid) + { + Debug.WriteLine("Imvalid geometry found: " + current); + continue; + } + geoms.Add(current); + //if (result == null) + // result = current; + //else result = result.Union(current); + //Debug.WriteLine("Iteration => " + ++count); + } + var result = UnaryUnionOp.Union(geoms); + var write = new WKTWriter { Formatted = true, MaxCoordinatesPerLine = 3, Tab = 2 }; + Debug.WriteLine("Operation result: " + write.Write(result)); + return result; + } + + /// + /// + /// + const string s1 = "MULTIPOLYGON(" + + "((267225 195936,267063 195904,266895 195872,266825 195858,266822 195891,266850 196044,266934 196203,267047 196249,267352 196374,267281 196302,267255 196275,267222 196244,267166 196191,267160 196153,267157 196139,267196 196136,267225 195936))," + + "((265704 193056,265703 193055,265692 193057,265678 193060,265663 193056,265659 193055,265655 193053,265653 193053,265651 193052,265648 193052,265646 193052,265631 193058,265616 193064,265612 193066,265609 193068,265605 193071,265603 193076,265595 193089,265596 193103,265597 193107,265623 193128,265631 193130,265630 193130,265609 193142,265608 193153,265608 193157,265607 193161,265607 193163,265606 193164,265603 193169,265619 193172,265623 193173,265619 193183,265618 193185,265618 193187,265613 193209,265618 193209,265622 193209,265620 193220,265626 193220,265626 193230,265617 193230,265616 193235,265616 193237,265615 193238,265604 193252,265606 193257,265607 193259,265607 193262,265607 193264,265607 193266,265607 193270,265607 193274,265607 193279,265606 193284,265605 193289,265602 193294,265595 193312,265598 193318,265600 193322,265601 193326,265602 193330,265602 193334,265602 193336,265602 193337,265599 193344,265610 193360,265611 193361,265612 193364,265613 193369,265615 193375,265615 193377,265615 193380,265613 193393,265609 193392,265607 193392,265605 193393,265587 193403,265588 193412,265589 193419,265610 193422,265629 193424,265631 193418,265633 193409,265637 193402,265642 193392,265659 193409,265661 193410,265662 193414,265662 193416,265663 193418,265664 193419,265664 193420,265665 193422,265665 193424,265666 193426,265666 193428,265665 193452,265657 193455,265652 193457,265640 193441,265636 193435,265623 193444,265642 193493,265645 193490,265649 193487,265665 193513,265673 193526,265681 193532,265683 193534,265684 193536,265685 193540,265686 193544,265687 193545,265687 193546,265690 193576,265682 193584,265655 193612,265667 193624,265668 193624,265670 193627,265679 193624,265683 193623,265693 193623,265692 193632,265692 193633,265693 193635,265695 193642,265693 193642,265692 193643,265691 193644,265690 193644,265689 193644,265679 193645,265679 193655,265678 193655,265677 193673,265676 193682,265670 193684,265659 193688,265654 193700,265652 193704,265631 193701,265629 193701,265628 193703,265619 193718,265617 193718,265616 193709,265612 193683,265614 193678,265609 193678,265571 193681,265551 193657,265495 193716,265492 193726,265481 193765,265481 193774,265480 193846,265479 193902,265478 193936,265526 193966,265563 193990,265548 194006,265547 194006,265546 194005,265544 194004,265543 194003,265542 194003,265541 194002,265540 194002,265540 194003,265521 194040,265525 194046,265527 194049,265527 194050,265528 194059,265528 194061,265543 194059,265544 194059,265544 194060,265549 194064,265554 194068,265555 194070,265557 194073,265558 194073,265558 194074,265559 194077,265561 194079,265564 194084,265567 194088,265568 194090,265570 194090,265581 194093,265569 194117,265567 194120,265567 194124,265567 194125,265568 194126,265569 194127,265569 194128,265570 194129,265571 194130,265572 194131,265573 194132,265574 194133,265575 194134,265576 194134,265577 194135,265596 194153,265583 194158,265540 194205,265530 194216,265473 194277,265462 194289,265461 194290,265451 194299,265432 194314,265410 194332,265394 194348,265372 194370,265371 194369,265359 194355,265362 194353,265362 194352,265367 194342,265342 194356,265341 194357,265340 194358,265339 194358,265338 194359,265337 194360,265336 194361,265335 194362,265335 194363,265334 194363,265333 194364,265332 194365,265331 194366,265330 194367,265325 194347,265308 194363,265302 194369,265326 194391,265308 194398,265306 194400,265304 194401,265303 194402,265302 194402,265301 194402,265300 194402,265294 194402,265288 194404,265286 194404,265286 194405,265285 194406,265284 194407,265283 194408,265282 194409,265282 194410,265281 194411,265280 194412,265279 194413,265278 194414,265277 194415,265276 194416,265275 194418,265275 194419,265274 194420,265273 194421,265272 194422,265272 194423,265271 194424,265270 194425,265269 194426,265268 194428,265268 194429,265267 194430,265266 194431,265266 194432,265265 194433,265264 194434,265263 194435,265262 194435,265261 194436,265260 194438,265258 194438,265253 194438,265254 194450,265254 194452,265253 194453,265252 194454,265251 194455,265243 194464,265234 194454,265238 194471,265238 194473,265237 194473,265235 194475,265234 194476,265232 194477,265231 194478,265230 194478,265229 194478,265228 194477,265227 194477,265226 194476,265225 194475,265224 194474,265224 194473,265223 194472,265222 194472,265222 194471,265221 194470,265220 194469,265217 194466,265216 194465,265166 194437,265165 194447,265164 194476,265158 194508,265158 194511,265173 194549,265176 194555,265199 194612,265205 194606,265214 194603,265238 194592,265244 194601,265246 194605,265257 194601,265263 194598,265265 194598,265266 194599,265268 194601,265270 194602,265272 194604,265274 194604,265278 194605,265281 194605,265282 194605,265283 194605,265292 194603,265290 194595,265284 194577,265320 194599,265336 194580,265351 194590,265302 194548,265300 194546,265298 194545,265295 194542,265295 194541,265298 194536,265285 194522,265285 194521,265284 194520,265283 194519,265282 194518,265281 194517,265280 194516,265279 194514,265279 194512,265247 194500,265262 194484,265277 194470,265278 194469,265287 194460,265304 194444,265306 194442,265325 194424,265330 194429,265370 194431,265371 194430,265372 194429,265374 194428,265376 194427,265377 194426,265378 194425,265379 194425,265380 194424,265381 194423,265382 194423,265383 194422,265384 194421,265385 194420,265386 194419,265387 194419,265388 194418,265389 194417,265390 194417,265390 194416,265391 194415,265392 194415,265393 194414,265395 194413,265396 194412,265398 194411,265399 194409,265401 194408,265402 194407,265403 194406,265404 194405,265405 194405,265406 194404,265407 194403,265408 194402,265409 194402,265410 194401,265411 194400,265412 194399,265413 194398,265414 194398,265414 194397,265416 194396,265417 194396,265418 194395,265419 194394,265420 194393,265421 194393,265422 194392,265423 194391,265424 194390,265425 194390,265426 194389,265427 194388,265428 194387,265429 194387,265430 194386,265431 194385,265432 194384,265433 194383,265434 194383,265435 194382,265436 194381,265437 194381,265438 194380,265439 194379,265440 194379,265441 194378,265442 194377,265443 194376,265444 194376,265445 194375,265446 194374,265447 194373,265448 194373,265448 194372,265449 194371,265450 194370,265451 194370,265451 194368,265453 194363,265466 194371,265467 194372,265467 194373,265468 194374,265469 194375,265469 194376,265470 194377,265471 194378,265471 194379,265471 194380,265469 194381,265469 194382,265468 194382,265466 194384,265462 194386,265425 194414,265430 194411,265430 194410,265428 194412,265433 194418,265448 194406,265463 194421,265464 194421,265465 194421,265466 194421,265467 194422,265487 194442,265507 194463,265548 194465,265548 194473,265578 194475,265578 194477,265579 194477,265602 194498,265595 194503,265594 194505,265593 194507,265592 194510,265590 194513,265590 194514,265589 194515,265589 194516,265589 194517,265588 194518,265587 194520,265587 194521,265586 194523,265585 194525,265585 194526,265582 194536,265579 194545,265578 194547,265577 194548,265576 194550,265575 194552,265574 194553,265573 194555,265572 194556,265571 194557,265570 194558,265570 194560,265569 194561,265569 194562,265568 194563,265568 194565,265567 194566,265566 194569,265565 194572,265565 194574,265564 194575,265562 194579,265560 194583,265559 194585,265557 194588,265557 194590,265556 194591,265555 194592,265555 194593,265554 194594,265553 194596,265553 194597,265552 194598,265552 194599,265551 194601,265551 194602,265550 194603,265549 194605,265548 194606,265548 194607,265548 194609,265547 194623,265538 194624,265508 194628,265508 194629,265501 194629,265461 194633,265456 194639,265458 194655,265462 194682,265463 194689,265491 194705,265527 194725,265534 194729,265561 194745,265575 194752,265589 194755,265593 194755,265598 194755,265603 194755,265607 194752,265630 194734,265651 194712,265652 194710,265654 194708,265656 194707,265657 194705,265658 194704,265658 194703,265659 194702,265659 194701,265659 194700,265659 194698,265660 194697,265660 194696,265663 194693,265666 194689,265666 194688,265666 194686,265666 194683,265666 194678,265666 194677,265667 194676,265671 194671,265675 194667,265678 194663,265682 194660,265689 194653,265698 194650,265714 194645,265700 194593,265699 194589,265701 194586,265704 194583,265706 194580,265711 194573,265716 194567,265724 194559,265730 194551,265734 194547,265737 194543,265739 194539,265742 194535,265747 194528,265749 194519,265752 194507,265754 194495,265755 194490,265757 194486,265769 194467,265779 194448,265780 194447,265780 194445,265780 194444,265780 194443,265780 194442,265780 194441,265779 194440,265779 194439,265779 194437,265779 194436,265780 194435,265780 194434,265779 194433,265780 194431,265780 194430,265779 194429,265779 194428,265779 194427,265779 194426,265779 194424,265779 194423,265779 194422,265778 194421,265778 194419,265777 194418,265777 194417,265777 194416,265777 194414,265777 194413,265777 194411,265777 194410,265777 194409,265778 194408,265784 194399,265791 194390,265791 194389,265791 194388,265790 194387,265790 194386,265790 194385,265789 194384,265789 194383,265789 194381,265788 194380,265788 194379,265788 194378,265787 194376,265787 194375,265787 194374,265786 194373,265786 194372,265786 194371,265786 194370,265785 194364,265785 194358,265785 194357,265785 194356,265784 194355,265784 194353,265784 194352,265783 194351,265783 194350,265783 194348,265782 194347,265782 194346,265782 194341,265783 194336,265783 194335,265783 194334,265783 194332,265783 194331,265783 194330,265784 194328,265784 194327,265785 194326,265785 194325,265786 194323,265787 194321,265788 194319,265788 194318,265787 194318,265785 194317,265785 194318,265775 194318,265775 194313,265768 194310,265748 194302,265754 194293,265761 194282,265765 194272,265773 194252,265780 194235,265781 194231,265780 194230,265778 194225,265775 194219,265775 194218,265775 194216,265774 194215,265774 194213,265770 194202,265767 194190,265767 194189,265767 194187,265768 194181,265802 194173,265813 194153,265814 194148,265834 194139,265836 194138,265838 194136,265840 194135,265841 194135,265842 194134,265843 194133,265845 194132,265847 194130,265848 194130,265849 194129,265850 194129,265851 194128,265852 194127,265853 194127,265854 194126,265857 194121,265858 194115,265859 194113,265860 194110,265860 194108,265862 194107,265876 194093,265869 194090,265868 194089,265868 194088,265867 194087,265867 194085,265866 194084,265866 194083,265866 194082,265865 194081,265865 194079,265865 194078,265864 194077,265864 194076,265864 194075,265863 194073,265863 194071,265862 194069,265862 194068,265861 194068,265849 194059,265853 194017,265853 194016,265852 194014,265852 194013,265852 194011,265851 194010,265851 194009,265851 194008,265850 194007,265849 194004,265848 194002,265848 194000,265847 193997,265845 193992,265845 193991,265845 193989,265853 193957,265844 193964,265806 193997,265780 194074,265743 193975,265748 193972,265748 193971,265746 193968,265740 193965,265731 193960,265727 193957,265732 193952,265733 193950,265734 193948,265735 193947,265734 193946,265734 193944,265734 193943,265734 193942,265734 193940,265734 193939,265734 193938,265734 193936,265734 193935,265734 193934,265733 193932,265733 193930,265732 193928,265726 193922,265732 193902,265732 193900,265732 193897,265730 193879,265723 193879,265716 193879,265714 193873,265744 193875,265749 193874,265715 193833,265717 193832,265717 193824,265718 193817,265718 193816,265763 193761,265764 193762,265767 193773,265768 193780,265775 193785,265783 193791,265773 193814,265770 193821,265768 193829,265766 193839,265762 193848,265759 193856,265758 193862,265758 193872,265763 193881,265768 193889,265781 193866,265790 193850,265794 193845,265840 193774,265837 193806,265837 193809,265831 193827,265860 193838,265882 193774,265905 193718,266059 193881,266139 193966,266196 193861,266170 193795,266180 193724,266194 193619,266209 193557,266221 193508,266225 193492,266224 193475,266222 193325,266223 193318,266225 193297,266227 193280,266228 193266,266234 193210,266137 193221,266121 193247,266068 193233,266066 193191,266066 193188,266064 193152,266039 193057,266035 193043,266025 192991,266019 192991,266019 192981,266023 192981,266021 192972,265989 192940,266005 192938,266006 192953,266064 192989,266071 192986,266087 192979,266104 192977,266228 192963,266223 192995,266219 193016,266270 193017,266400 192817,266413 192797,266561 192570,266615 192536,266620 192553,266632 192599,266607 192635,266547 192722,266661 192907,266693 192959,266872 192896,266899 192887,266926 192889,266972 192892,266968 192741,266967 192695,266871 192667,266737 192629,266764 192622,266727 192409,266717 192352,266808 192287,266885 192231,266873 192268,266868 192283,266894 192340,266987 192540,267003 192550,267304 192739,267285 192641,267270 192566,266933 192196,266928 192191,266906 192166,266889 192142,266873 192118,266813 192175,266797 192190,266763 192137,266358 192253,266369 192237,266370 192235,266367 192235,266296 192224,266281 192203,266023 191829,266016 191826,266005 191816,266002 191816,265998 191816,265991 191822,265991 191829,265991 191834,265989 191839,265987 191844,265983 191847,265980 191850,265976 191851,265974 191852,265969 191850,265969 191842,265967 191837,265966 191834,265964 191831,265960 191827,265958 191826,265950 191829,265949 191862,265932 191858,265932 191853,265931 191849,265929 191845,265925 191841,265922 191838,265899 191861,265888 191860,265886 191858,265883 191854,265881 191850,265880 191847,265879 191840,265866 191833,265859 191846,265852 191842,265846 191839,265840 191837,265838 191837,265837 191839,265835 191844,265834 191850,265835 191858,265834 191859,265831 191862,265828 191864,265825 191864,265821 191864,265818 191862,265802 191848,265800 191847,265796 191847,265790 191847,265785 191849,265768 191855,265765 191856,265761 191855,265755 191852,265742 191848,265739 191834,265725 191801,265720 191793,265715 191789,265709 191785,265705 191783,265696 191780,265684 191778,265681 191779,265680 191780,265679 191782,265679 191784,265683 191799,265681 191831,265677 191841,265663 191836,265652 191833,265648 191836,265643 191839,265638 191840,265632 191839,265631 191838,265629 191821,265626 191817,265624 191818,265623 191818,265620 191825,265618 191834,265615 191841,265612 191848,265608 191854,265603 191859,265597 191864,265592 191868,265589 191869,265586 191869,265579 191868,265575 191868,265567 191865,265559 191865,265556 191865,265552 191867,265548 191870,265543 191876,265537 191874,265533 191873,265528 191873,265523 191874,265515 191877,265491 191878,265479 191881,265468 191886,265456 191883,265445 191877,265413 191877,265399 191875,265389 191874,265372 191875,265360 191874,265334 191870,265329 191869,265398 192004,265565 192332,265564 192333,265557 192337,265556 192341,265555 192343,265555 192344,265556 192345,265559 192363,265563 192381,265564 192384,265565 192386,265580 192409,265596 192429,265599 192432,265603 192434,265610 192436,265616 192438,265621 192440,265626 192442,265648 192448,265670 192455,265673 192456,265676 192457,265679 192458,265682 192460,265694 192468,265708 192475,265717 192479,265726 192483,265729 192484,265731 192485,265734 192487,265738 192488,265748 192491,265759 192494,265772 192498,265784 192504,265807 192515,265824 192531,265847 192551,265855 192574,265865 192601,265864 192627,265864 192659,265802 192646,265842 192760,265835 192794,265823 192853,265779 192863,265804 192879,265823 192892,265812 192890,265808 192890,265807 192890,265806 192891,265805 192892,265803 192900,265801 192908,265800 192916,265800 192924,265800 192927,265772 192935,265767 192936,265765 192936,265764 192931,265761 192923,265760 192923,265757 192924,265756 192923,265729 192905,265724 192909,265715 192914,265704 192914,265698 192914,265698 192916,265695 192916,265687 192942,265704 192951,265719 192954,265715 192957,265710 192960,265706 192962,265706 192974,265707 192976,265705 192978,265704 192979,265704 192981,265701 192987,265698 192994,265695 192994,265691 192993,265687 192994,265680 192994,265677 193008,265676 193010,265685 193012,265712 193020,265712 193017,265713 193015,265711 193012,265711 193010,265726 193011,265725 193038,265721 193041,265704 193056)," + + "(265681 194364,265681 194367,265671 194367,265671 194358,265671 194357,265681 194357,265681 194364)," + + "(265672 194259,265672 194252,265682 194252,265682 194261,265682 194262,265672 194262,265672 194259)," + + "(265660 194241,265669 194241,265669 194251,265659 194251,265659 194249,265659 194241,265660 194241)," + + "(265730 193180,265761 193187,265770 193167,265773 193196,265778 193238,265780 193284,265780 193290,265781 193296,265781 193300,265781 193305,265781 193310,265781 193315,265737 193357,265704 193365,265692 193357,265689 193334,265698 193344,265699 193335,265701 193319,265703 193305,265718 193219,265723 193191,265728 193192,265730 193180)," + + "(265829 193249,265833 193249,265833 193259,265828 193259,265823 193259,265823 193250,265823 193249,265829 193249)," + + "(265892 193510,265906 193514,266004 193467,265951 193602,265890 193576,265899 193549,265892 193510)," + + "(266286 192856,266219 192756,266168 192757,266168 192748,266169 192722,266220 192689,266281 192648,266286 192670,266318 192815,266290 192850,266286 192856)," + + "(266044 192606,266037 192606,266037 192596,266047 192596,266047 192603,266047 192606,266044 192606)," + + "(266039 192790,266039 192800,266029 192800,266029 192790,266037 192790,266039 192790)," + + "(265749 192996,265749 192986,265759 192986,265759 192996,265749 192996)," + + "(265848 192834,265848 192824,265858 192824,265858 192834,265848 192834)," + + "(265978 192980,265978 192970,265988 192970,265988 192980,265978 192980)," + + "(266088 192809,266088 192813,266080 192813,266078 192813,266078 192803,266088 192803,266088 192809)," + + "(265981 192800,265981 192790,265991 192790,265991 192800,265981 192800)," + + "(265951 192844,265951 192834,265961 192834,265961 192844,265951 192844)," + + "(265917 193026,265917 193016,265927 193016,265927 193026,265917 193026)," + + "(265926 193055,265926 193045,265936 193045,265936 193055,265926 193055)," + + "(265966 192746,265966 192736,265976 192736,265976 192746,265966 192746)," + + "(266046 192708,266046 192698,266056 192698,266056 192708,266046 192708)," + + "(265692 193744,265687 193751,265684 193755,265682 193756,265667 193763,265635 193732,265630 193728,265650 193718,265652 193717,265662 193716,265673 193718,265676 193718,265679 193718,265681 193718,265682 193719,265683 193723,265687 193723,265687 193733,265692 193744)," + + "(265611 193820,265611 193810,265621 193810,265621 193820,265611 193820)," + + "(265731 193735,265731 193725,265741 193725,265741 193735,265731 193735)," + + "(265708 193562,265708 193552,265718 193552,265718 193562,265708 193562)," + + "(265650 193477,265640 193477,265640 193467,265648 193467,265650 193467,265650 193477)," + + "(265680 193109,265680 193099,265690 193099,265690 193109,265680 193109)," + + "(265611 193335,265611 193325,265621 193325,265621 193335,265611 193335)," + + "(265610 194005,265610 193995,265620 193995,265620 194005,265610 194005)," + + "(265570 193944,265570 193934,265580 193934,265580 193944,265570 193944)," + + "(265643 193872,265643 193862,265653 193862,265653 193872,265643 193872)," + + "(265909 193141,265909 193131,265919 193131,265919 193141,265909 193141)," + + "(265800 193015,265800 193005,265810 193005,265810 193015,265800 193015)," + + "(265728 194212,265728 194202,265738 194202,265738 194212,265728 194212)," + + "(265691 194370,265691 194360,265701 194360,265701 194370,265691 194370))," + + "((265057 193097,265050 193096,265044 193093,265043 193093,265041 193093,265038 193093,265037 193093,265035 193093,265029 193092,265031 193093,265035 193094,265060 193099,265076 193103,265058 193097,265057 193097))," + + "((265246 193239,265266 193230,265268 193230,265279 193209,265282 193211,265281 193210,265290 193199,265273 193188,265269 193196,265260 193213,265249 193234,265246 193239))," + + "((265525 192957,265525 192962,265524 192965,265526 192968,265530 192975,265533 192983,265536 192990,265540 192993,265562 193014,265575 193011,265609 193004,265606 192991,265602 192978,265607 192966,265613 192953,265573 192946,265533 192939,265527 192954,265525 192957))," + + "((265345 192929,265348 192937,265349 192941,265371 192945,265372 192945,265373 192938,265374 192935,265374 192934,265364 192919,265363 192920,265345 192929)))"; + const string s2 = "POLYGON((265433 194418,265428 194412,265430 194410,265430 194411,265425 194414,265462 194386,265466 194384,265468 194382,265469 194382,265469 194381,265471 194380,265471 194379,265471 194378,265470 194377,265469 194376,265469 194375,265468 194374,265467 194373,265467 194372,265466 194371,265453 194363,265451 194368,265451 194370,265450 194370,265449 194371,265448 194372,265448 194373,265447 194373,265446 194374,265445 194375,265444 194376,265443 194376,265442 194377,265441 194378,265440 194379,265439 194379,265438 194380,265437 194381,265436 194381,265435 194382,265434 194383,265433 194383,265432 194384,265431 194385,265430 194386,265429 194387,265428 194387,265427 194388,265426 194389,265425 194390,265424 194390,265423 194391,265422 194392,265421 194393,265420 194393,265419 194394,265418 194395,265417 194396,265416 194396,265414 194397,265414 194398,265413 194398,265412 194399,265411 194400,265410 194401,265409 194402,265408 194402,265407 194403,265406 194404,265405 194405,265404 194405,265403 194406,265402 194407,265402 194407,265401 194408,265399 194409,265398 194411,265396 194412,265395 194413,265393 194414,265392 194415,265391 194415,265390 194416,265390 194417,265389 194417,265388 194418,265387 194419,265386 194419,265385 194420,265384 194421,265383 194422,265382 194423,265381 194423,265380 194424,265379 194425,265378 194425,265377 194426,265376 194427,265374 194428,265372 194429,265371 194430,265370 194431,265330 194429,265369 194467,265379 194477,265422 194481,265427 194484,265433 194488,265467 194515,265469 194508,265487 194442,265467 194422,265466 194421,265465 194421,265464 194421,265463 194421,265448 194406,265433 194418))"; + + [Test] + //[ExpectedException(typeof(TopologyException))] + public void FailedUnionTest() + { + var reader = new WKTReader(GeometryFactory.Fixed); + Assert.IsNotNull(reader); + + var g1 = reader.Read(s1); + Assert.IsNotNull(g1); + Assert.IsFalse(g1.IsValid); + g1 = g1.Buffer(0); + + var g2 = reader.Read(s2).Buffer(0d); + Assert.IsNotNull(g2); + + var result = g1.Union(g2); + Assert.IsNotNull(result); + Debug.WriteLine(result); + } + + /// + /// + /// + [Test] + public void FixedUnionTest() + { + string s1 = "MULTIPOLYGON(((267225 195936,267063 195904,266895 195872,266825 195858,266822 195891,266850 196044,266934 196203,267047 196249,267352 196374,267281 196302,267255 196275,267222 196244,267166 196191,267160 196153,267157 196139,267196 196136,267225 195936)),((265704 193056,265703 193055,265692 193057,265678 193060,265663 193056,265659 193055,265655 193053,265653 193053,265651 193052,265648 193052,265646 193052,265631 193058,265616 193064,265612 193066,265609 193068,265605 193071,265603 193076,265595 193089,265596 193103,265597 193107,265623 193128,265631 193130,265630 193130,265609 193142,265608 193153,265608 193157,265607 193161,265607 193163,265606 193164,265603 193169,265619 193172,265623 193173,265619 193183,265618 193185,265618 193187,265613 193209,265618 193209,265622 193209,265620 193220,265626 193220,265626 193230,265617 193230,265616 193235,265616 193237,265615 193238,265604 193252,265606 193257,265607 193259,265607 193262,265607 193264,265607 193266,265607 193270,265607 193274,265607 193279,265606 193284,265605 193289,265602 193294,265595 193312,265598 193318,265600 193322,265601 193326,265602 193330,265602 193334,265602 193336,265602 193337,265599 193344,265610 193360,265611 193361,265612 193364,265613 193369,265615 193375,265615 193377,265615 193380,265613 193393,265609 193392,265607 193392,265605 193393,265587 193403,265588 193412,265589 193419,265610 193422,265629 193424,265631 193418,265633 193409,265637 193402,265642 193392,265659 193409,265661 193410,265662 193414,265662 193416,265663 193418,265664 193419,265664 193420,265665 193422,265665 193424,265666 193426,265666 193428,265665 193452,265657 193455,265652 193457,265640 193441,265636 193435,265623 193444,265642 193493,265645 193490,265649 193487,265665 193513,265673 193526,265681 193532,265683 193534,265684 193536,265685 193540,265686 193544,265687 193545,265687 193546,265690 193576,265682 193584,265655 193612,265667 193624,265668 193624,265670 193627,265679 193624,265683 193623,265693 193623,265692 193632,265692 193633,265693 193635,265695 193642,265693 193642,265692 193643,265691 193644,265690 193644,265689 193644,265679 193645,265679 193655,265678 193655,265677 193673,265676 193682,265670 193684,265659 193688,265654 193700,265652 193704,265631 193701,265629 193701,265628 193703,265619 193718,265617 193718,265616 193709,265612 193683,265614 193678,265609 193678,265571 193681,265551 193657,265495 193716,265492 193726,265481 193765,265481 193774,265480 193846,265479 193902,265478 193936,265526 193966,265563 193990,265548 194006,265547 194006,265546 194005,265544 194004,265543 194003,265542 194003,265541 194002,265540 194002,265540 194003,265521 194040,265525 194046,265527 194049,265527 194050,265528 194059,265528 194061,265543 194059,265544 194059,265544 194060,265549 194064,265554 194068,265555 194070,265557 194073,265558 194073,265558 194074,265559 194077,265561 194079,265564 194084,265567 194088,265568 194090,265570 194090,265581 194093,265569 194117,265567 194120,265567 194124,265567 194125,265568 194126,265569 194127,265569 194128,265570 194129,265571 194130,265572 194131,265573 194132,265574 194133,265575 194134,265576 194134,265577 194135,265596 194153,265583 194158,265540 194205,265530 194216,265473 194277,265462 194289,265461 194290,265451 194299,265432 194314,265410 194332,265394 194348,265372 194370,265371 194369,265359 194355,265362 194353,265362 194352,265367 194342,265342 194356,265341 194357,265340 194358,265339 194358,265338 194359,265337 194360,265336 194361,265335 194362,265335 194363,265334 194363,265333 194364,265332 194365,265331 194366,265330 194367,265325 194347,265308 194363,265302 194369,265326 194391,265308 194398,265306 194400,265304 194401,265303 194402,265302 194402,265301 194402,265300 194402,265294 194402,265288 194404,265286 194404,265286 194405,265285 194406,265284 194407,265283 194408,265282 194409,265282 194410,265281 194411,265280 194412,265279 194413,265278 194414,265277 194415,265276 194416,265275 194418,265275 194419,265274 194420,265273 194421,265272 194422,265272 194423,265271 194424,265270 194425,265269 194426,265268 194428,265268 194429,265267 194430,265266 194431,265266 194432,265265 194433,265264 194434,265263 194435,265262 194435,265261 194436,265260 194438,265258 194438,265253 194438,265254 194450,265254 194452,265253 194453,265252 194454,265251 194455,265243 194464,265234 194454,265238 194471,265238 194473,265237 194473,265235 194475,265234 194476,265232 194477,265231 194478,265230 194478,265229 194478,265228 194477,265227 194477,265226 194476,265225 194475,265224 194474,265224 194473,265223 194472,265222 194472,265222 194471,265221 194470,265220 194469,265217 194466,265216 194465,265166 194437,265165 194447,265164 194476,265158 194508,265158 194511,265173 194549,265176 194555,265199 194612,265205 194606,265214 194603,265238 194592,265244 194601,265246 194605,265257 194601,265263 194598,265265 194598,265266 194599,265268 194601,265270 194602,265272 194604,265274 194604,265278 194605,265281 194605,265282 194605,265283 194605,265292 194603,265290 194595,265284 194577,265320 194599,265336 194580,265351 194590,265302 194548,265300 194546,265298 194545,265295 194542,265295 194541,265298 194536,265285 194522,265285 194521,265284 194520,265283 194519,265282 194518,265281 194517,265280 194516,265279 194514,265279 194512,265247 194500,265262 194484,265277 194470,265278 194469,265287 194460,265304 194444,265306 194442,265325 194424,265330 194429,265370 194431,265371 194430,265372 194429,265374 194428,265376 194427,265377 194426,265378 194425,265379 194425,265380 194424,265381 194423,265382 194423,265383 194422,265384 194421,265385 194420,265386 194419,265387 194419,265388 194418,265389 194417,265390 194417,265390 194416,265391 194415,265392 194415,265393 194414,265395 194413,265396 194412,265398 194411,265399 194409,265401 194408,265402 194407,265403 194406,265404 194405,265405 194405,265406 194404,265407 194403,265408 194402,265409 194402,265410 194401,265411 194400,265412 194399,265413 194398,265414 194398,265414 194397,265416 194396,265417 194396,265418 194395,265419 194394,265420 194393,265421 194393,265422 194392,265423 194391,265424 194390,265425 194390,265426 194389,265427 194388,265428 194387,265429 194387,265430 194386,265431 194385,265432 194384,265433 194383,265434 194383,265435 194382,265436 194381,265437 194381,265438 194380,265439 194379,265440 194379,265441 194378,265442 194377,265443 194376,265444 194376,265445 194375,265446 194374,265447 194373,265448 194373,265448 194372,265449 194371,265450 194370,265451 194370,265451 194368,265453 194363,265466 194371,265467 194372,265467 194373,265468 194374,265469 194375,265469 194376,265470 194377,265471 194378,265471 194379,265471 194380,265469 194381,265469 194382,265468 194382,265466 194384,265462 194386,265425 194414,265430 194411,265430 194410,265428 194412,265433 194418,265448 194406,265463 194421,265464 194421,265465 194421,265466 194421,265467 194422,265487 194442,265507 194463,265548 194465,265548 194473,265578 194475,265578 194477,265579 194477,265602 194498,265595 194503,265594 194505,265593 194507,265592 194510,265590 194513,265590 194514,265589 194515,265589 194516,265589 194517,265588 194518,265587 194520,265587 194521,265586 194523,265585 194525,265585 194526,265582 194536,265579 194545,265578 194547,265577 194548,265576 194550,265575 194552,265574 194553,265573 194555,265572 194556,265571 194557,265570 194558,265570 194560,265569 194561,265569 194562,265568 194563,265568 194565,265567 194566,265566 194569,265565 194572,265565 194574,265564 194575,265562 194579,265560 194583,265559 194585,265557 194588,265557 194590,265556 194591,265555 194592,265555 194593,265554 194594,265553 194596,265553 194597,265552 194598,265552 194599,265551 194601,265551 194602,265550 194603,265549 194605,265548 194606,265548 194607,265548 194609,265547 194623,265538 194624,265508 194628,265508 194629,265501 194629,265461 194633,265456 194639,265458 194655,265462 194682,265463 194689,265491 194705,265527 194725,265534 194729,265561 194745,265575 194752,265589 194755,265593 194755,265598 194755,265603 194755,265607 194752,265630 194734,265651 194712,265652 194710,265654 194708,265656 194707,265657 194705,265658 194704,265658 194703,265659 194702,265659 194701,265659 194700,265659 194698,265660 194697,265660 194696,265663 194693,265666 194689,265666 194688,265666 194686,265666 194683,265666 194678,265666 194677,265667 194676,265671 194671,265675 194667,265678 194663,265682 194660,265689 194653,265698 194650,265714 194645,265700 194593,265699 194589,265701 194586,265704 194583,265706 194580,265711 194573,265716 194567,265724 194559,265730 194551,265734 194547,265737 194543,265739 194539,265742 194535,265747 194528,265749 194519,265752 194507,265754 194495,265755 194490,265757 194486,265769 194467,265779 194448,265780 194447,265780 194445,265780 194444,265780 194443,265780 194442,265780 194441,265779 194440,265779 194439,265779 194437,265779 194436,265780 194435,265780 194434,265779 194433,265780 194431,265780 194430,265779 194429,265779 194428,265779 194427,265779 194426,265779 194424,265779 194423,265779 194422,265778 194421,265778 194419,265777 194418,265777 194417,265777 194416,265777 194414,265777 194413,265777 194411,265777 194410,265777 194409,265778 194408,265784 194399,265791 194390,265791 194389,265791 194388,265790 194387,265790 194386,265790 194385,265789 194384,265789 194383,265789 194381,265788 194380,265788 194379,265788 194378,265787 194376,265787 194375,265787 194374,265786 194373,265786 194372,265786 194371,265786 194370,265785 194364,265785 194358,265785 194357,265785 194356,265784 194355,265784 194353,265784 194352,265783 194351,265783 194350,265783 194348,265782 194347,265782 194346,265782 194341,265783 194336,265783 194335,265783 194334,265783 194332,265783 194331,265783 194330,265784 194328,265784 194327,265785 194326,265785 194325,265786 194323,265787 194321,265788 194319,265788 194318,265787 194318,265785 194317,265785 194318,265775 194318,265775 194313,265768 194310,265748 194302,265754 194293,265761 194282,265765 194272,265773 194252,265780 194235,265781 194231,265780 194230,265778 194225,265775 194219,265775 194218,265775 194216,265774 194215,265774 194213,265770 194202,265767 194190,265767 194189,265767 194187,265768 194181,265802 194173,265813 194153,265814 194148,265834 194139,265836 194138,265838 194136,265840 194135,265841 194135,265842 194134,265843 194133,265845 194132,265847 194130,265848 194130,265849 194129,265850 194129,265851 194128,265852 194127,265853 194127,265854 194126,265857 194121,265858 194115,265859 194113,265860 194110,265860 194108,265862 194107,265876 194093,265869 194090,265868 194089,265868 194088,265867 194087,265867 194085,265866 194084,265866 194083,265866 194082,265865 194081,265865 194079,265865 194078,265864 194077,265864 194076,265864 194075,265863 194073,265863 194071,265862 194069,265862 194068,265861 194068,265849 194059,265853 194017,265853 194016,265852 194014,265852 194013,265852 194011,265851 194010,265851 194009,265851 194008,265850 194007,265849 194004,265848 194002,265848 194000,265847 193997,265845 193992,265845 193991,265845 193989,265853 193957,265844 193964,265806 193997,265780 194074,265743 193975,265748 193972,265748 193971,265746 193968,265740 193965,265731 193960,265727 193957,265732 193952,265733 193950,265734 193948,265735 193947,265734 193946,265734 193944,265734 193943,265734 193942,265734 193940,265734 193939,265734 193938,265734 193936,265734 193935,265734 193934,265733 193932,265733 193930,265732 193928,265726 193922,265732 193902,265732 193900,265732 193897,265730 193879,265723 193879,265716 193879,265714 193873,265744 193875,265749 193874,265715 193833,265717 193832,265717 193824,265718 193817,265718 193816,265763 193761,265764 193762,265767 193773,265768 193780,265775 193785,265783 193791,265773 193814,265770 193821,265768 193829,265766 193839,265762 193848,265759 193856,265758 193862,265758 193872,265763 193881,265768 193889,265781 193866,265790 193850,265794 193845,265840 193774,265837 193806,265837 193809,265831 193827,265860 193838,265882 193774,265905 193718,266059 193881,266139 193966,266196 193861,266170 193795,266180 193724,266194 193619,266209 193557,266221 193508,266225 193492,266224 193475,266222 193325,266223 193318,266225 193297,266227 193280,266228 193266,266234 193210,266137 193221,266121 193247,266068 193233,266066 193191,266066 193188,266064 193152,266039 193057,266035 193043,266025 192991,266019 192991,266019 192981,266023 192981,266021 192972,265989 192940,266005 192938,266006 192953,266064 192989,266071 192986,266087 192979,266104 192977,266228 192963,266223 192995,266219 193016,266270 193017,266400 192817,266413 192797,266561 192570,266615 192536,266620 192553,266632 192599,266607 192635,266547 192722,266661 192907,266693 192959,266872 192896,266899 192887,266926 192889,266972 192892,266968 192741,266967 192695,266871 192667,266737 192629,266764 192622,266727 192409,266717 192352,266808 192287,266885 192231,266873 192268,266868 192283,266894 192340,266987 192540,267003 192550,267304 192739,267285 192641,267270 192566,266933 192196,266928 192191,266906 192166,266889 192142,266873 192118,266813 192175,266797 192190,266763 192137,266358 192253,266369 192237,266370 192235,266367 192235,266296 192224,266281 192203,266023 191829,266016 191826,266005 191816,266002 191816,265998 191816,265991 191822,265991 191829,265991 191834,265989 191839,265987 191844,265983 191847,265980 191850,265976 191851,265974 191852,265969 191850,265969 191842,265967 191837,265966 191834,265964 191831,265960 191827,265958 191826,265950 191829,265949 191862,265932 191858,265932 191853,265931 191849,265929 191845,265925 191841,265922 191838,265899 191861,265888 191860,265886 191858,265883 191854,265881 191850,265880 191847,265879 191840,265866 191833,265859 191846,265852 191842,265846 191839,265840 191837,265838 191837,265837 191839,265835 191844,265834 191850,265835 191858,265834 191859,265831 191862,265828 191864,265825 191864,265821 191864,265818 191862,265802 191848,265800 191847,265796 191847,265790 191847,265785 191849,265768 191855,265765 191856,265761 191855,265755 191852,265742 191848,265739 191834,265725 191801,265720 191793,265715 191789,265709 191785,265705 191783,265696 191780,265684 191778,265681 191779,265680 191780,265679 191782,265679 191784,265683 191799,265681 191831,265677 191841,265663 191836,265652 191833,265648 191836,265643 191839,265638 191840,265632 191839,265631 191838,265629 191821,265626 191817,265624 191818,265623 191818,265620 191825,265618 191834,265615 191841,265612 191848,265608 191854,265603 191859,265597 191864,265592 191868,265589 191869,265586 191869,265579 191868,265575 191868,265567 191865,265559 191865,265556 191865,265552 191867,265548 191870,265543 191876,265537 191874,265533 191873,265528 191873,265523 191874,265515 191877,265491 191878,265479 191881,265468 191886,265456 191883,265445 191877,265413 191877,265399 191875,265389 191874,265372 191875,265360 191874,265334 191870,265329 191869,265398 192004,265565 192332,265564 192333,265557 192337,265556 192341,265555 192343,265555 192344,265556 192345,265559 192363,265563 192381,265564 192384,265565 192386,265580 192409,265596 192429,265599 192432,265603 192434,265610 192436,265616 192438,265621 192440,265626 192442,265648 192448,265670 192455,265673 192456,265676 192457,265679 192458,265682 192460,265694 192468,265708 192475,265717 192479,265726 192483,265729 192484,265731 192485,265734 192487,265738 192488,265748 192491,265759 192494,265772 192498,265784 192504,265807 192515,265824 192531,265847 192551,265855 192574,265865 192601,265864 192627,265864 192659,265802 192646,265842 192760,265835 192794,265823 192853,265779 192863,265804 192879,265823 192892,265812 192890,265808 192890,265807 192890,265806 192891,265805 192892,265803 192900,265801 192908,265800 192916,265800 192924,265800 192927,265772 192935,265767 192936,265765 192936,265764 192931,265761 192923,265760 192923,265757 192924,265756 192923,265729 192905,265724 192909,265715 192914,265704 192914,265698 192914,265698 192916,265695 192916,265687 192942,265704 192951,265719 192954,265715 192957,265710 192960,265706 192962,265706 192974,265707 192976,265705 192978,265704 192979,265704 192981,265701 192987,265698 192994,265695 192994,265691 192993,265687 192994,265680 192994,265677 193008,265676 193010,265685 193012,265712 193020,265712 193017,265713 193015,265711 193012,265711 193010,265726 193011,265725 193038,265721 193041,265704 193056),(265681 194364,265681 194367,265671 194367,265671 194358,265671 194357,265681 194357,265681 194364),(265672 194259,265672 194252,265682 194252,265682 194261,265682 194262,265672 194262,265672 194259),(265660 194241,265669 194241,265669 194251,265659 194251,265659 194249,265659 194241,265660 194241),(265730 193180,265761 193187,265770 193167,265773 193196,265778 193238,265780 193284,265780 193290,265781 193296,265781 193300,265781 193305,265781 193310,265781 193315,265737 193357,265704 193365,265692 193357,265689 193334,265698 193344,265699 193335,265701 193319,265703 193305,265718 193219,265723 193191,265728 193192,265730 193180),(265829 193249,265833 193249,265833 193259,265828 193259,265823 193259,265823 193250,265823 193249,265829 193249),(265892 193510,265906 193514,266004 193467,265951 193602,265890 193576,265899 193549,265892 193510),(266286 192856,266219 192756,266168 192757,266168 192748,266169 192722,266220 192689,266281 192648,266286 192670,266318 192815,266290 192850,266286 192856),(266044 192606,266037 192606,266037 192596,266047 192596,266047 192603,266047 192606,266044 192606),(266039 192790,266039 192800,266029 192800,266029 192790,266037 192790,266039 192790),(265749 192996,265749 192986,265759 192986,265759 192996,265749 192996),(265848 192834,265848 192824,265858 192824,265858 192834,265848 192834),(265978 192980,265978 192970,265988 192970,265988 192980,265978 192980),(266088 192809,266088 192813,266080 192813,266078 192813,266078 192803,266088 192803,266088 192809),(265981 192800,265981 192790,265991 192790,265991 192800,265981 192800),(265951 192844,265951 192834,265961 192834,265961 192844,265951 192844),(265917 193026,265917 193016,265927 193016,265927 193026,265917 193026),(265926 193055,265926 193045,265936 193045,265936 193055,265926 193055),(265966 192746,265966 192736,265976 192736,265976 192746,265966 192746),(266046 192708,266046 192698,266056 192698,266056 192708,266046 192708),(265692 193744,265687 193751,265684 193755,265682 193756,265667 193763,265635 193732,265630 193728,265650 193718,265652 193717,265662 193716,265673 193718,265676 193718,265679 193718,265681 193718,265682 193719,265683 193723,265687 193723,265687 193733,265692 193744),(265611 193820,265611 193810,265621 193810,265621 193820,265611 193820),(265731 193735,265731 193725,265741 193725,265741 193735,265731 193735),(265708 193562,265708 193552,265718 193552,265718 193562,265708 193562),(265650 193477,265640 193477,265640 193467,265648 193467,265650 193467,265650 193477),(265680 193109,265680 193099,265690 193099,265690 193109,265680 193109),(265611 193335,265611 193325,265621 193325,265621 193335,265611 193335),(265610 194005,265610 193995,265620 193995,265620 194005,265610 194005),(265570 193944,265570 193934,265580 193934,265580 193944,265570 193944),(265643 193872,265643 193862,265653 193862,265653 193872,265643 193872),(265909 193141,265909 193131,265919 193131,265919 193141,265909 193141),(265800 193015,265800 193005,265810 193005,265810 193015,265800 193015),(265728 194212,265728 194202,265738 194202,265738 194212,265728 194212),(265691 194370,265691 194360,265701 194360,265701 194370,265691 194370)),((265057 193097,265050 193096,265044 193093,265043 193093,265041 193093,265038 193093,265037 193093,265035 193093,265029 193092,265031 193093,265035 193094,265060 193099,265076 193103,265058 193097,265057 193097)),((265246 193239,265266 193230,265268 193230,265279 193209,265282 193211,265281 193210,265290 193199,265273 193188,265269 193196,265260 193213,265249 193234,265246 193239)),((265525 192957,265525 192962,265524 192965,265526 192968,265530 192975,265533 192983,265536 192990,265540 192993,265562 193014,265575 193011,265609 193004,265606 192991,265602 192978,265607 192966,265613 192953,265573 192946,265533 192939,265527 192954,265525 192957)),((265345 192929,265348 192937,265349 192941,265371 192945,265372 192945,265373 192938,265374 192935,265374 192934,265364 192919,265363 192920,265345 192929)))"; + string s2 = "POLYGON((265433 194418,265428 194412,265430 194410,265430 194411,265425 194414,265462 194386,265466 194384,265468 194382,265469 194382,265469 194381,265471 194380,265471 194379,265471 194378,265470 194377,265469 194376,265469 194375,265468 194374,265467 194373,265467 194372,265466 194371,265453 194363,265451 194368,265451 194370,265450 194370,265449 194371,265448 194372,265448 194373,265447 194373,265446 194374,265445 194375,265444 194376,265443 194376,265442 194377,265441 194378,265440 194379,265439 194379,265438 194380,265437 194381,265436 194381,265435 194382,265434 194383,265433 194383,265432 194384,265431 194385,265430 194386,265429 194387,265428 194387,265427 194388,265426 194389,265425 194390,265424 194390,265423 194391,265422 194392,265421 194393,265420 194393,265419 194394,265418 194395,265417 194396,265416 194396,265414 194397,265414 194398,265413 194398,265412 194399,265411 194400,265410 194401,265409 194402,265408 194402,265407 194403,265406 194404,265405 194405,265404 194405,265403 194406,265402 194407,265402 194407,265401 194408,265399 194409,265398 194411,265396 194412,265395 194413,265393 194414,265392 194415,265391 194415,265390 194416,265390 194417,265389 194417,265388 194418,265387 194419,265386 194419,265385 194420,265384 194421,265383 194422,265382 194423,265381 194423,265380 194424,265379 194425,265378 194425,265377 194426,265376 194427,265374 194428,265372 194429,265371 194430,265370 194431,265330 194429,265369 194467,265379 194477,265422 194481,265427 194484,265433 194488,265467 194515,265469 194508,265487 194442,265467 194422,265466 194421,265465 194421,265464 194421,265463 194421,265448 194406,265433 194418))"; + + var reader = new WKTReader(GeometryFactory.Fixed); + Assert.IsNotNull(reader); + + var g1 = reader.Read(s1).Buffer(0); + Assert.IsNotNull(g1); + + var g2 = reader.Read(s2).Buffer(0); + Assert.IsNotNull(g2); + + var result = g1.Union(g2); + Assert.IsNotNull(result); + Debug.WriteLine(result); + } + } +} diff --git a/test/NetTopologySuite.IO.Esri.Test/Deprecated/Writers/ShapeFileDataWriterTest.cs b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Writers/ShapeFileDataWriterTest.cs new file mode 100644 index 0000000..d535d56 --- /dev/null +++ b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Writers/ShapeFileDataWriterTest.cs @@ -0,0 +1,782 @@ +using NetTopologySuite.Geometries; +using NetTopologySuite.Algorithm.Match; +using NetTopologySuite.Features; +using NetTopologySuite.Geometries.Implementation; +using NUnit.Framework; +using System; +using System.Collections.ObjectModel; +using System.IO; +using System.Text; +using System.Collections.Generic; +using NetTopologySuite.IO.Esri; + +namespace NetTopologySuite.IO.Esri.Test.Deprecated.Writers +{ + + [TestFixture] + public class ShapeFileDataWriterTest + { + protected GeometryFactory Factory { get; private set; } + + protected WKTReader Reader { get; private set; } + + public ShapeFileDataWriterTest() + { + // Set current dir to shapefiles dir + Environment.CurrentDirectory = TestShapefiles.Directory; + + Factory = new GeometryFactory(); + Reader = new WKTReader(); + } + + [Test] + public void TestCreateEmptyShapefile() + { + const string filename = "__empty"; + const string emptyShp = filename + ".shp"; + const string emptyShx = filename + ".shx"; + const string emptyDbf = filename + ".dbf"; + if (File.Exists(emptyShp)) + File.Delete(emptyShp); + if (File.Exists(emptyShx)) + File.Delete(emptyShx); + if (File.Exists(emptyDbf)) + File.Delete(emptyDbf); + + var options = new ShapefileWriterOptions(ShapeType.Point); + using var writer = Shapefile.OpenWrite(filename, options); + writer.Write(new IFeature[0]); + + Assert.That(File.Exists(emptyShp), Is.True); + Assert.That(File.Exists(emptyShx), Is.True); + Assert.That(File.Exists(emptyDbf), Is.True); + } + + [Test] + public void TestWriteZValuesShapeFile() + { + TestWriteZMValuesShapeFile(false); + } + + [Test] + public void TestWriteZMValuesShapeFile() + { + TestWriteZMValuesShapeFile(true); + } + + private void TestWriteZMValuesShapeFile(bool testM) + { + var points = new Coordinate[3]; + points[0] = new Coordinate(0, 0); + points[1] = new Coordinate(1, 0); + points[2] = new Coordinate(1, 1); + + var csFactory = DotSpatialAffineCoordinateSequenceFactory.Instance; + var sequence = csFactory.Create(3, Ordinates.XYZM); + for (int i = 0; i < 3; i++) + { + sequence.SetOrdinate(i, Ordinate.X, points[i].X); + sequence.SetOrdinate(i, Ordinate.Y, points[i].Y); + sequence.SetOrdinate(i, Ordinate.Z, 1 + i); + if (testM) + sequence.SetOrdinate(i, Ordinate.M, 11 + i); + } + var lineString = Factory.CreateLineString(sequence); + + var attributes = new AttributesTable(); + attributes.Add("FOO", "Trond"); + + var feature = new Feature(Factory.CreateMultiLineString(new[] { lineString }), attributes); + var features = new Feature[1]; + features[0] = feature; + Shapefile.WriteAllFeatures(features, "ZMtest"); + + // Now let's read the file and verify that we got Z and M back + var factory = new GeometryFactory(DotSpatialAffineCoordinateSequenceFactory.Instance); + + using (var reader = Shapefile.OpenRead("ZMtest")) + { + reader.Read(); + var geom = (MultiLineString)reader.Geometry; + + for (int i = 0; i < 3; i++) + { + var c = geom.Coordinates[i]; + Assert.AreEqual(i + 1, c.Z); + } + + if (testM) + { + sequence = ((LineString)geom[0]).CoordinateSequence; + for (int i = 0; i < 3; i++) + { + Assert.AreEqual(sequence.GetOrdinate(i, Ordinate.M), 11 + i); + } + } + + // Run a simple attribute test too + string v = reader.Fields["FOO"].Value?.ToString(); + Assert.AreEqual(v, "Trond"); + } + } + + [Test] + public void TestWriteSimpleShapeFile() + { + var p1 = Factory.CreatePoint(new Coordinate(100, 100)); + var p2 = Factory.CreatePoint(new Coordinate(200, 200)); + + var coll = new GeometryCollection(new Geometry[] { p1, p2, }); + WriteGeometryCollection(@"test_arcview", coll); + } + + [Test] + public void TestReadWritePoint() + { + var geomsWrite = ShapeFileShapeFactory.CreateShapes(OgcGeometryType.Point, Ordinates.XY); + DoTest(geomsWrite, Ordinates.XY); + geomsWrite = ShapeFileShapeFactory.CreateShapes(OgcGeometryType.Point, Ordinates.XYM); + DoTest(geomsWrite, Ordinates.XYM); + geomsWrite = ShapeFileShapeFactory.CreateShapes(OgcGeometryType.Point, Ordinates.XYZM); + DoTest(geomsWrite, Ordinates.XYZM); + } + + [Test] + public void TestReadWriteMultiPoint() + { + var geomsWrite = ShapeFileShapeFactory.CreateShapes(OgcGeometryType.MultiPoint, Ordinates.XY); + DoTest(geomsWrite, Ordinates.XY); + geomsWrite = ShapeFileShapeFactory.CreateShapes(OgcGeometryType.MultiPoint, Ordinates.XYM); + DoTest(geomsWrite, Ordinates.XYM); + geomsWrite = ShapeFileShapeFactory.CreateShapes(OgcGeometryType.MultiPoint, Ordinates.XYZM); + DoTest(geomsWrite, Ordinates.XYZM); + } + + [Test] + public void TestReadWriteLineal() + { + var geomsWrite = ShapeFileShapeFactory.CreateShapes(OgcGeometryType.LineString, Ordinates.XY); + DoTest(geomsWrite, Ordinates.XY); + geomsWrite = ShapeFileShapeFactory.CreateShapes(OgcGeometryType.LineString, Ordinates.XYM); + DoTest(geomsWrite, Ordinates.XYM); + geomsWrite = ShapeFileShapeFactory.CreateShapes(OgcGeometryType.LineString, Ordinates.XYZM); + DoTest(geomsWrite, Ordinates.XYZM); + } + + [Test] + public void TestReadWritePolygonal() + { + var geomsWrite = ShapeFileShapeFactory.CreateShapes(OgcGeometryType.Polygon, Ordinates.XY); + DoTest(geomsWrite, Ordinates.XY); + geomsWrite = ShapeFileShapeFactory.CreateShapes(OgcGeometryType.Polygon, Ordinates.XYM); + DoTest(geomsWrite, Ordinates.XYM); + geomsWrite = ShapeFileShapeFactory.CreateShapes(OgcGeometryType.Polygon, Ordinates.XYZM); + DoTest(geomsWrite, Ordinates.XYZM, false); + } + + private static void WriteGeometryCollection(string shpPath, GeometryCollection geomColl) + { + var shapeType = Shapefile.GetShapeType(geomColl); + var options = new ShapefileWriterOptions(shapeType); + using var shp = Shapefile.OpenWrite(shpPath, options); + + for (int i = 0; i < geomColl.Count; i++) + { + shp.Geometry = geomColl[i]; + shp.Write(); + } + } + + private static void DoTest(GeometryCollection geomsWrite, Ordinates ordinates, bool testGetOrdinate = true) + { + string fileName = string.Empty; + + try + { + + fileName = Path.GetTempFileName(); + fileName = Path.ChangeExtension(fileName, "shp"); + WriteGeometryCollection(fileName, geomsWrite); + var geomsRead = new GeometryCollection(Shapefile.ReadAllGeometries(fileName)); + + // This tests x- and y- values + if (!geomsWrite.EqualsExact(geomsRead)) + { + Assert.AreEqual(geomsWrite.NumGeometries, geomsRead.NumGeometries); + // + // This akward test is necessary since EqualsTopologically throws currently exceptions + bool equal = true; + for (int i = 0; i < geomsRead.NumGeometries; i++) + { + var gw = geomsWrite.GetGeometryN(i); + var gr = geomsRead.GetGeometryN(i); + if (gw.IsEmpty && gr.IsEmpty) + { + if (gw is ILineal && gr is ILineal || + gw is IPolygonal && gr is IPolygonal) + { + // suppose these are equal + } + else + { + Console.WriteLine(string.Format("Geometries don't match at index {0}", i)); + Console.WriteLine(string.Format(" written: {0}", gw.AsText())); + Console.WriteLine(string.Format(" read : {0}", gr.AsText())); + equal = false; + Assert.IsTrue(equal, "Differenced found in geometries written and read!"); + } + } + else if (!gw.EqualsExact(gr)) + { + double hsm = new HausdorffSimilarityMeasure().Measure(gw, gr); + double asm = new AreaSimilarityMeasure().Measure(gw, gr); + double smc = SimilarityMeasureCombiner.Combine(hsm, asm); + if (!gw.EqualsNormalized(gr) || 1d - smc > 1e-7) + { + Console.WriteLine(string.Format("Geometries don't match at index {0}", i)); + Console.WriteLine(string.Format(" written: {0}", gw.AsText())); + Console.WriteLine(string.Format(" read : {0}", gr.AsText())); + equal = false; + Assert.IsTrue(equal, "Differenced found in geometries written and read!"); + } + } + } + + //For polygons this has a tendency to fail, since the polygonhandler might rearrange the whole thing + if (testGetOrdinate) + { + if ((ordinates & Ordinates.Z) == Ordinates.Z) + { + double[] writeZ = geomsWrite.GetOrdinates(Ordinate.Z); + double[] readZ = geomsRead.GetOrdinates(Ordinate.Z); + Assert.IsTrue(ArraysEqual(writeZ, readZ)); + } + + if ((ordinates & Ordinates.M) == Ordinates.M) + { + double[] writeM = geomsWrite.GetOrdinates(Ordinate.M); + double[] readM = geomsRead.GetOrdinates(Ordinate.M); + Assert.IsTrue(ArraysEqual(writeM, readM)); + } + } + + } + + // delete sample files + File.Delete(fileName); + File.Delete(Path.ChangeExtension(fileName, "shx")); + File.Delete(Path.ChangeExtension(fileName, "dbf")); + } + catch (AssertionException ex) + { + Console.WriteLine("Failed test with {0}", ordinates); + Console.WriteLine(ex.Message); + Console.WriteLine(" Testfile '{0}' not deleted!", fileName); + throw; + } + + } + + private static bool ArraysEqual(double[] writeZ, double[] readZ) + { + if (writeZ == null ^ readZ == null) + return false; + + if (writeZ == null) + return true; + + if (writeZ.Length != readZ.Length) + return false; + + for (int i = 0; i < writeZ.Length; i++) + if (Math.Abs(writeZ[i] - readZ[i]) > 1E-7) return false; + + return true; + } + + private static class ShapeFileShapeFactory + { + public static GeometryCollection CreateShapes(OgcGeometryType type, Ordinates ordinates, int number = 50) + { + bool[] empty = new bool[number]; + empty[Rnd.Next(2, number / 2)] = true; + empty[Rnd.Next(number / 2, number)] = true; + + var result = new Geometry[number]; + for (int i = 0; i < number; i++) + { + switch (type) + { + case OgcGeometryType.Point: + result[i] = CreatePoint(ordinates, empty[i]); + break; + case OgcGeometryType.MultiPoint: + result[i] = CreateMultiPoint(ordinates, empty[i]); + break; + + case OgcGeometryType.LineString: + case OgcGeometryType.MultiLineString: + result[i] = CreateLineal(ordinates, empty[i]); + break; + case OgcGeometryType.Polygon: + case OgcGeometryType.MultiPolygon: + result[i] = CreatePolygonal(ordinates, empty[i]); + break; + } + + /* + // Ensure no empty elements + if (result[i] == null || (result[i].IsEmpty && result[i].OgcGeometryType == OgcGeometryType.GeometryCollection)) + i--; + */ + // Ensure not null and not geometry collection + if (result[i] == null || result[i].OgcGeometryType == OgcGeometryType.GeometryCollection) + i--; + } + + return Factory.CreateGeometryCollection(result); + } + + private static readonly Random Rnd = new Random(9936528); + + private static readonly CoordinateSequenceFactory CsFactory = + DotSpatialAffineCoordinateSequenceFactory.Instance; + + public static readonly GeometryFactory FactoryRead = new GeometryFactory(new PrecisionModel(PrecisionModels.Floating), 4326, CsFactory); + + public static readonly GeometryFactory Factory = new GeometryFactory(new PrecisionModel(1000), 4326, CsFactory); + + private static Geometry CreatePoint(Ordinates ordinates, bool empty) + { + if (empty) + { + return Factory.CreatePoint((CoordinateSequence)null); + } + + var seq = CsFactory.Create(1, ordinates); + foreach (var o in ToOrdinateArray(ordinates)) + seq.SetOrdinate(0, o, RandomOrdinate(o, Factory.PrecisionModel)); + return Factory.CreatePoint(seq); + } + + private static Geometry CreateMultiPoint(Ordinates ordinates, bool empty) + { + if (empty) + { + return Factory.CreateMultiPoint((CoordinateSequence)null); + } + int numPoints = Rnd.Next(75, 101); + var seq = CsFactory.Create(numPoints, ordinates); + for (int i = 0; i < numPoints; i++) + foreach (var o in ToOrdinateArray(ordinates)) + seq.SetOrdinate(i, o, RandomOrdinate(o, Factory.PrecisionModel)); + + return Factory.CreateMultiPoint(seq); + } + + private static Geometry CreateLineal(Ordinates ordinates, bool empty) + { + return CreateMultiLineString(ordinates, empty); + } + + private static Geometry CreateLineString(Ordinates ordinates, bool empty) + { + if (empty) + { + return Factory.CreateLineString((CoordinateSequence)null); + } + + int numPoints = Rnd.Next(75, 101); + var seq = CsFactory.Create(numPoints, ordinates); + for (int i = 0; i < numPoints; i++) + foreach (var o in ToOrdinateArray(ordinates)) + seq.SetOrdinate(i, o, RandomOrdinate(o, Factory.PrecisionModel)); + + return Factory.CreateLineString(seq); + } + + private static Geometry CreateMultiLineString(Ordinates ordinates, bool empty) + { + if (empty) + { + Factory.CreateMultiLineString(null); + } + + int numLineStrings = Rnd.Next(0, 11); + if (numLineStrings <= 2) + numLineStrings = 0; + + var lineString = new LineString[numLineStrings]; + for (int i = 0; i < numLineStrings; i++) + lineString[i] = (LineString)CreateLineString(ordinates, false); + + return Factory.CreateMultiLineString(lineString); + } + + private static Geometry CreatePolygonal(Ordinates ordinates, bool empty) + { + return CreateMultiPolygon(ordinates, empty); + } + + private static Geometry CreatePolygon(Ordinates ordinates, bool empty, int nextKind = -1) + { + if (empty) + { + Factory.CreatePolygon((CoordinateSequence)null); + } + if (nextKind == -1) nextKind = Rnd.Next(0, 5); + + double x = RandomOrdinate(Ordinate.X, Factory.PrecisionModel); + double y = RandomOrdinate(Ordinate.Y, Factory.PrecisionModel); + + switch (nextKind) + { + case 0: // circle + var ring = CreateCircleRing(ordinates, x, y, 3 * Rnd.NextDouble()); + return Factory.CreatePolygon(ring, null); + case 1: // rectangle + ring = CreateRectangleRing(ordinates, x, y, 6 * Rnd.NextDouble(), 3 * Rnd.NextDouble()); + return Factory.CreatePolygon(ring, null); + case 2: // cirle with hole + double radius = 3 * Rnd.NextDouble(); + var shell = CreateCircleRing(ordinates, x, y, radius); + var hole = CreateCircleRing(ordinates, x, y, 0.66 * radius, true); + return Factory.CreatePolygon(shell, new[] { hole }); + case 3: // rectanglee with hole + double width = 6 * Rnd.NextDouble(); + double height = 3 * Rnd.NextDouble(); + shell = CreateRectangleRing(ordinates, x, y, width, height); + hole = CreateRectangleRing(ordinates, x, y, 0.66 * width, 0.66 * height, true); + return Factory.CreatePolygon(shell, new[] { hole }); + case 4: // rectanglee with hole + width = 6 * Rnd.NextDouble(); + height = 3 * Rnd.NextDouble(); + shell = CreateRectangleRing(ordinates, x, y, width, height); + hole = CreateCircleRing(ordinates, x, y, 0.33 * Math.Min(width, height), true); + return Factory.CreatePolygon(shell, new[] { hole }); + default: + throw new NotSupportedException(); + } + } + + private static LinearRing CreateCircleRing(Ordinates ordinates, double x, double y, double radius, bool reverse = false) + { + var seq = CsFactory.Create(4 * 12 + 1, ordinates); + double angle = Math.PI * 2; + const double quandrantStep = Math.PI / 2d / 12d; + int k = 0; + for (int i = 0; i < 4; i++) + { + for (int j = 0; j < 12; j++) + { + double dx = radius * Math.Cos(angle); + double dy = radius * Math.Sin(angle); + seq.SetOrdinate(k, Ordinate.X, Factory.PrecisionModel.MakePrecise(x + dx)); + seq.SetOrdinate(k, Ordinate.Y, Factory.PrecisionModel.MakePrecise(y + dy)); + if ((ordinates & Ordinates.Z) == Ordinates.Z) + seq.SetOrdinate(k, Ordinate.Z, RandomOrdinate(Ordinate.Z, Factory.PrecisionModel)); + if ((ordinates & Ordinates.Z) == Ordinates.Z) + seq.SetOrdinate(k, Ordinate.M, RandomOrdinate(Ordinate.M, Factory.PrecisionModel)); + k++; + angle -= quandrantStep; + } + } + seq.SetOrdinate(k, Ordinate.X, seq.GetOrdinate(0, Ordinate.X)); + seq.SetOrdinate(k, Ordinate.Y, seq.GetOrdinate(0, Ordinate.Y)); + if ((ordinates & Ordinates.Z) == Ordinates.Z) + seq.SetOrdinate(k, Ordinate.Z, seq.GetOrdinate(0, Ordinate.Z)); + if ((ordinates & Ordinates.M) == Ordinates.M) + seq.SetOrdinate(k, Ordinate.M, seq.GetOrdinate(0, Ordinate.M)); + + return Factory.CreateLinearRing(reverse ? seq.Reversed() : seq); + } + + private static LinearRing CreateRectangleRing(Ordinates ordinates, double x, double y, double width, double height, bool reverse = false) + { + double dx = Factory.PrecisionModel.MakePrecise(width / 2); + double dy = Factory.PrecisionModel.MakePrecise(height / 2); + + var seq = CsFactory.Create(5, ordinates); + + seq.SetOrdinate(0, Ordinate.X, Factory.PrecisionModel.MakePrecise(x - dx)); + seq.SetOrdinate(0, Ordinate.Y, Factory.PrecisionModel.MakePrecise(y - dy)); + seq.SetOrdinate(1, Ordinate.X, Factory.PrecisionModel.MakePrecise(x - dx)); + seq.SetOrdinate(1, Ordinate.Y, Factory.PrecisionModel.MakePrecise(y + dy)); + seq.SetOrdinate(2, Ordinate.X, Factory.PrecisionModel.MakePrecise(x + dx)); + seq.SetOrdinate(2, Ordinate.Y, Factory.PrecisionModel.MakePrecise(y + dy)); + seq.SetOrdinate(3, Ordinate.X, Factory.PrecisionModel.MakePrecise(x + dx)); + seq.SetOrdinate(3, Ordinate.Y, Factory.PrecisionModel.MakePrecise(y - dy)); + seq.SetOrdinate(4, Ordinate.X, Factory.PrecisionModel.MakePrecise(x - dx)); + seq.SetOrdinate(4, Ordinate.Y, Factory.PrecisionModel.MakePrecise(y - dy)); + + if ((ordinates & (Ordinates.Z | Ordinates.M)) != Ordinates.None) + { + int k = 0; + for (; k < 4; k++) + { + if ((ordinates & Ordinates.Z) == Ordinates.Z) + seq.SetOrdinate(k, Ordinate.Z, RandomOrdinate(Ordinate.Z, Factory.PrecisionModel)); + if ((ordinates & Ordinates.Z) == Ordinates.Z) + seq.SetOrdinate(k, Ordinate.M, RandomOrdinate(Ordinate.M, Factory.PrecisionModel)); + } + if ((ordinates & Ordinates.Z) == Ordinates.Z) + seq.SetOrdinate(k, Ordinate.Z, seq.GetOrdinate(0, Ordinate.Z)); + if ((ordinates & Ordinates.M) == Ordinates.M) + seq.SetOrdinate(k, Ordinate.M, seq.GetOrdinate(0, Ordinate.M)); + } + + return Factory.CreateLinearRing(reverse ? seq.Reversed() : seq); + } + + private static Geometry CreateMultiPolygon(Ordinates ordinates, bool empty) + { + if (empty) + { + Factory.CreateMultiPolygon(null); + } + + switch (Rnd.Next(2)) + { + case 0: + int numPolygons = Rnd.Next(4); + var polygons = new Polygon[numPolygons]; + for (int i = 0; i < numPolygons; i++) + polygons[i] = (Polygon)CreatePolygon(ordinates, false); + var union = Factory.BuildGeometry(new Collection(polygons)).Union(); + if (union.IsEmpty) + { + return MultiPolygon.Empty; + } + if (union is MultiPolygon multiPolygon) + { + return multiPolygon; + } + return new MultiPolygon(new Polygon[] { (Polygon)union }); + + case 1: + polygons = new Polygon[2]; + double radius = 5 * Rnd.NextDouble(); + double x = RandomOrdinate(Ordinate.X, Factory.PrecisionModel); + double y = RandomOrdinate(Ordinate.Y, Factory.PrecisionModel); + var shell = CreateCircleRing(ordinates, x, y, radius); + var hole = CreateCircleRing(ordinates, x, y, 0.66 * radius, true); + polygons[0] = Factory.CreatePolygon(shell, new[] { hole }); + shell = CreateCircleRing(ordinates, x, y, 0.5 * radius); + hole = CreateCircleRing(ordinates, x, y, 0.15 * radius, true); + polygons[1] = Factory.CreatePolygon(shell, new[] { hole }); + return Factory.CreateMultiPolygon(polygons); + + default: + throw new NotSupportedException(); + } + } + + private static double RandomOrdinate(Ordinate o, PrecisionModel pm) + { + switch (o) + { + case Ordinate.X: + return pm.MakePrecise(-180 + 360 * Rnd.NextDouble()); + case Ordinate.Y: + return pm.MakePrecise(-90 + 180 * Rnd.NextDouble()); + case Ordinate.Z: + return 200 * Rnd.NextDouble(); + case Ordinate.M: + return 200 + 200 * Rnd.NextDouble(); + default: + throw new NotSupportedException(); + } + } + } + + [Test /*, ExpectedException(typeof(ArgumentException))*/] + // see https://code.google.com/p/nettopologysuite/issues/detail?id=146 + public void Issue146_ShapeCreationWithInvalidAttributeName() + { + var points = new Coordinate[3]; + points[0] = new Coordinate(0, 0); + points[1] = new Coordinate(1, 0); + points[2] = new Coordinate(1, 1); + var ls = new LineString(points); + var mls = GeometryFactory.Default.CreateMultiLineString(new LineString[] { ls }); + + var attrs = new AttributesTable(); + attrs.Add("Simulation name", "FOO"); + + var features = new[] { new Feature(mls, attrs) }; + Assert.Throws( + () => Shapefile.WriteAllFeatures(features, "invalid_field_name") + ); + + //Assert.Throws(() => shp_writer.Write(features)); + } + + [Test/*, ExpectedException(typeof(ArgumentException))*/] + // see: https://github.com/NetTopologySuite/NetTopologySuite/issues/111 + public void issue_111_pointhandler_with_invalid_values() + { + var factory = GeometryFactory.Default; + + var p = factory.CreatePoint(new CoordinateZ(0, 0)); + Geometry[] arr = { p, GeometryCollection.Empty }; + var geometries = factory.CreateGeometryCollection(arr); + + var shapeType = Shapefile.GetShapeType(geometries); + Assert.AreEqual(ShapeType.PointZM, shapeType); + + string tempPath = TestShapefiles.GetTempShpPath(); + Assert.Throws( + () => WriteGeometryCollection(tempPath, geometries) + ); + } + + [Test/*, ExpectedException(typeof(ArgumentException))*/] + // see: https://github.com/NetTopologySuite/NetTopologySuite/issues/111 + public void issue_111_multiPointhandler_with_invalid_values() + { + var factory = GeometryFactory.Default; + + var p = factory.CreatePoint(new CoordinateZ(0, 0)); + var mp = factory.CreateMultiPoint(new[] { p }); + Geometry[] arr = new[] { mp, GeometryCollection.Empty }; + var geometries = factory.CreateGeometryCollection(arr); + + var shapeType = Shapefile.GetShapeType(geometries); + Assert.AreEqual(ShapeType.MultiPointZM, shapeType); + + string tempPath = TestShapefiles.GetTempShpPath(); + Assert.Throws( + () => WriteGeometryCollection(tempPath, geometries) + ); + } + + [Test/*, ExpectedException(typeof(ArgumentException))*/] + // see: https://github.com/NetTopologySuite/NetTopologySuite/issues/111 + public void issue_111_multilinehandler_with_invalid_values() + { + var factory = GeometryFactory.Default; + + var points = new Coordinate[3]; + points[0] = new CoordinateZ(0, 0); + points[1] = new CoordinateZ(1, 0); + points[2] = new CoordinateZ(1, 1); + var ls = factory.CreateLineString(points); + + var mls = factory.CreateMultiLineString(new[] { ls }); + Geometry[] arr = new[] { mls, GeometryCollection.Empty }; + var geometries = factory.CreateGeometryCollection(arr); + + var shapeType = Shapefile.GetShapeType(geometries); + Assert.AreEqual(ShapeType.PolyLineZM, shapeType); + + string tempPath = TestShapefiles.GetTempShpPath(); + Assert.Throws( + () => WriteGeometryCollection(tempPath, geometries) + ); + } + + [Test/*, ExpectedException(typeof(ArgumentException))*/] + // see: https://github.com/NetTopologySuite/NetTopologySuite/issues/111 + public void issue_111_polygonhandler_with_invalid_values() + { + var factory = GeometryFactory.Default; + + var points = new Coordinate[5]; + points[0] = new CoordinateZ(0, 0); + points[1] = new CoordinateZ(1, 0); + points[2] = new CoordinateZ(1, 1); + points[3] = new CoordinateZ(0, 1); + points[4] = new CoordinateZ(0, 0); + var poly = factory.CreatePolygon(points); + + var mpoly = factory.CreateMultiPolygon(new[] { poly }); + Geometry[] arr = new[] { mpoly, GeometryCollection.Empty }; + var geometries = factory.CreateGeometryCollection(arr); + + var shapeType = Shapefile.GetShapeType(geometries); + Assert.AreEqual(ShapeType.PolygonZM, shapeType); + + string tempPath = TestShapefiles.GetTempShpPath(); + Assert.Throws( + () => WriteGeometryCollection(tempPath, geometries) + ); + } + + // ESRI spec says index file is mandatory, but we do allow writing a + // shapefile without it if someone specifically asks us to. + [Test] + public void WriteShouldWorkWithoutIndexFileWhenRequested() + { + // TODO: Remove no longer relevant test (Shapefile can be read without SHX file, but for compability reasons SHX file is always written) + // If one really requires not to have the SHX file then the SXH file must be deleted manually by using File.Delete(). + /* + var pt = GeometryFactory.Default.CreatePoint(new Coordinate(2, 3)); + var attributes = new AttributesTable { { "Foo", "Bar" } }; + Feature[] features = { new Feature(pt, attributes) }; + + string baseFileName = TestContext.CurrentContext.Test.ID; + string shpFilePath = baseFileName + ".shp"; + string dbfFilePath = baseFileName + ".dbf"; + string shxFilePath = baseFileName + ".shx"; + + var reg = new ShapefileStreamProviderRegistry( + shapeStream: new FileStreamProvider(StreamTypes.Shape, shpFilePath), + dataStream: new FileStreamProvider(StreamTypes.Data, dbfFilePath), + indexStream: null, + validateShapeProvider: true, + validateDataProvider: true, + validateIndexProvider: false); + + var wr = new ShapefileDataWriter(reg, GeometryFactory.Default, CodePagesEncodingProvider.Instance.GetEncoding(1252)); + wr.Header = ShapefileDataWriter.GetHeader(features[0], features.Length); + wr.Write(features); + + Assert.True(File.Exists(shpFilePath)); + Assert.True(File.Exists(dbfFilePath)); + Assert.False(File.Exists(shxFilePath)); + */ + } + + private static Ordinate[] ToOrdinateArray(Ordinates ordinates) + { + var result = new Ordinate[OrdinatesUtility.OrdinatesToDimension(ordinates)]; + int nextIndex = 0; + for (int i = 0; i < 32; i++) + { + if (ordinates.HasFlag((Ordinates)(1 << i))) + { + result[nextIndex++] = (Ordinate)i; + } + } + + return result; + } + + [Test, ShapeFileIssueNumber(24)] + public void WriteShouldWriteMultiPoints() + { + var attribs = new AttributesTable + { + { "Id", 10 } + }; + var coors = new Coordinate[2] + { + new Coordinate(123.0, 023.0), + new Coordinate(123.0, 100.0) + }; + var points = Factory.CreateMultiPointFromCoords(coors); + var feature = new Feature(points, attribs); + + var filename = TestShapefiles.GetTempShpPath(); + Shapefile.WriteAllFeatures(new[] { feature }, filename); + + using var reader = Shapefile.OpenRead(filename); + while (reader.Read()) + { + Assert.IsNotNull(reader.Geometry); + Assert.IsInstanceOf(reader.Geometry); + Assert.AreEqual(10, reader.Fields["Id"].Value); + } + } + } +} diff --git a/test/NetTopologySuite.IO.Esri.Test/Deprecated/Writers/WKTReaderTests.cs b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Writers/WKTReaderTests.cs new file mode 100644 index 0000000..f2021dc --- /dev/null +++ b/test/NetTopologySuite.IO.Esri.Test/Deprecated/Writers/WKTReaderTests.cs @@ -0,0 +1,25 @@ +using NetTopologySuite.Geometries; +using NUnit.Framework; + +namespace NetTopologySuite.IO.Esri.Test.Deprecated.Writers +{ + public class WKTReaderTests + { + private readonly WKTReader _wktReader; + + public WKTReaderTests() + { + _wktReader = new WKTReader(new GeometryFactory(new PrecisionModel(), 4326)) { IsOldNtsCoordinateSyntaxAllowed = false }; + } + + [Test] + public void TestShapeType() + { + string wkt = "POLYGON ((-86.7605020509258 41.5101338613656, -86.7604972038273 41.5100611525915, -86.7604971708084 41.5100606308085, -86.7604611720717 41.5094596307695, -86.7604611426546 41.5094591103497, -86.7604291439208 41.5088571103154, -86.760429130715 41.508856853856, -86.7603991319814 41.5082548538241, -86.7603991259966 41.5082547317887, -86.7603701303631 41.5076537960468, -86.7603401446338 41.5070530565908, -86.7603071566895 41.5064532528163, -86.7603071500912 41.506453131098, -86.7602814240795 41.5059715533315, -86.7605549835241 41.5059607024218, -86.7605808466407 41.5064448078787, -86.760613844555 41.5070447469854, -86.7606138651484 41.5070451395365, -86.7606438664126 41.5076461395046, -86.7606438727239 41.5076462680791, -86.7606728710439 41.5082472070294, -86.7607028628788 41.5088490177453, -86.7607348434949 41.5094506292495, -86.7607708135428 41.5100511081057, -86.760776407335 41.5101350123382, -86.7605020509258 41.5101338613656))"; + var geometry = (Polygon)_wktReader.Read(wkt); + + Assert.IsTrue(geometry.Shell.CoordinateSequence.Ordinates == Ordinates.XY); + Assert.IsTrue(Shapefile.GetShapeType(geometry) == ShapeType.Polygon); + } + } +} diff --git a/test/NetTopologySuite.IO.Esri.Test/EsriIssueNumberAttribute.cs b/test/NetTopologySuite.IO.Esri.Test/EsriIssueNumberAttribute.cs new file mode 100644 index 0000000..a4fef8f --- /dev/null +++ b/test/NetTopologySuite.IO.Esri.Test/EsriIssueNumberAttribute.cs @@ -0,0 +1,20 @@ +using System; +using NUnit.Framework; + +namespace NetTopologySuite.IO.Esri.Test +{ + /// + /// The issue number used in this test (or fixture) refers to an issue on + /// https://github.com/NetTopologySuite/NetTopologySuite.IO.Esri, created + /// after this project was moved to new repository (and thus, it got its own + /// set of issue numbers). + /// + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)] + public sealed class EsriIssueNumberAttribute : PropertyAttribute + { + public EsriIssueNumberAttribute(int issueNumber) + : base("NetTopologySuite.IO.Esri issue", issueNumber) + { + } + } +} diff --git a/test/NetTopologySuite.IO.Esri.Test/Geometries/MultipolygonWithPolygonInHole.cs b/test/NetTopologySuite.IO.Esri.Test/Geometries/MultipolygonWithPolygonInHole.cs new file mode 100644 index 0000000..182768f --- /dev/null +++ b/test/NetTopologySuite.IO.Esri.Test/Geometries/MultipolygonWithPolygonInHole.cs @@ -0,0 +1,76 @@ +using NetTopologySuite.Features; +using NetTopologySuite.Geometries; +using NetTopologySuite.IO.Esri.Dbf; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NetTopologySuite.IO.Esri.Test.Geometries +{ + /// + /// Tests for MultiPolygon having a polygon inside the hole. + /// https://github.com/NetTopologySuite/NetTopologySuite.IO.ShapeFile/pull/80#issuecomment-1019816823 + /// + /// + internal class MultipolygonWithPolygonInHole + { + private static string Wkt = @" + MULTIPOLYGON ( + ( + ( + -124.134 -79.199, -124.141 -79.316, -124.164 -79.431, -124.202 -79.542, -124.254 -79.647, -124.319 -79.745, -124.396 -79.833, -124.484 -79.91, + -124.582 -79.975, -124.687 -80.027, -124.798 -80.065, -124.913 -80.088, -125.03 -80.095, -125.147 -80.088, -125.262 -80.065, -125.373 -80.027, + -125.478 -79.975, -125.576 -79.91, -125.664 -79.833, -125.741 -79.745, -125.806 -79.647, -125.858 -79.542, -125.896 -79.431, -125.919 -79.316, + -125.926 -79.199, -125.919 -79.082, -125.896 -78.967, -125.858 -78.856, -125.806 -78.751, -125.741 -78.653, -125.664 -78.565, -125.576 -78.488, + -125.478 -78.423, -125.373 -78.371, -125.262 -78.333, -125.147 -78.31, -125.03 -78.303, -124.913 -78.31, -124.798 -78.333, -124.687 -78.371, + -124.582 -78.423, -124.484 -78.488, -124.396 -78.565, -124.319 -78.653, -124.254 -78.751, -124.202 -78.856, -124.164 -78.967, -124.141 -79.082, -124.134 -79.199 + ), + ( + -124.438 -79.199, -124.443 -79.122, -124.459 -79.046, -124.483 -78.973, -124.518 -78.903, -124.561 -78.839, -124.612 -78.781, -124.67 -78.73, + -124.734 -78.687, -124.804 -78.652, -124.877 -78.628, -124.953 -78.612, -125.03 -78.607, -125.107 -78.612, -125.183 -78.628, -125.256 -78.652, + -125.326 -78.687, -125.39 -78.73, -125.448 -78.781, -125.499 -78.839, -125.542 -78.903, -125.577 -78.973, -125.601 -79.046, -125.617 -79.122, + -125.622 -79.199, -125.617 -79.276, -125.601 -79.352, -125.577 -79.425, -125.542 -79.495, -125.499 -79.559, -125.448 -79.617, -125.39 -79.668, + -125.326 -79.711, -125.256 -79.746, -125.183 -79.77, -125.107 -79.786, -125.03 -79.791, -124.953 -79.786, -124.877 -79.77, -124.804 -79.746, + -124.734 -79.711, -124.67 -79.668, -124.612 -79.617, -124.561 -79.559, -124.518 -79.495, -124.483 -79.425, -124.459 -79.352, -124.443 -79.276, -124.438 -79.199 + ) + ), + ( + ( + -124.582 -79.199, -124.586 -79.257, -124.597 -79.315, -124.616 -79.371, -124.642 -79.423, -124.674 -79.472, -124.713 -79.516, -124.757 -79.555, + -124.806 -79.587, -124.858 -79.613, -124.914 -79.632, -124.972 -79.643, -125.03 -79.647, -125.088 -79.643, -125.146 -79.632, -125.202 -79.613, + -125.254 -79.587, -125.303 -79.555, -125.347 -79.516, -125.386 -79.472, -125.418 -79.423, -125.444 -79.371, -125.463 -79.315, -125.474 -79.257, + -125.478 -79.199, -125.474 -79.141, -125.463 -79.083, -125.444 -79.027, -125.418 -78.975, -125.386 -78.926, -125.347 -78.882, -125.303 -78.843, + -125.254 -78.811, -125.202 -78.785, -125.146 -78.766, -125.088 -78.755, -125.03 -78.751, -124.972 -78.755, -124.914 -78.766, -124.858 -78.785, + -124.806 -78.811, -124.757 -78.843, -124.713 -78.882, -124.674 -78.926, -124.642 -78.975, -124.616 -79.027, -124.597 -79.083, -124.586 -79.141, -124.582 -79.199 + ), + ( + -124.896 -79.199, -124.897 -79.181, -124.9 -79.164, -124.906 -79.148, -124.914 -79.132, -124.923 -79.117, -124.935 -79.104, -124.948 -79.092, + -124.963 -79.083, -124.979 -79.075, -124.995 -79.069, -125.012 -79.066, -125.03 -79.065, -125.048 -79.066, -125.065 -79.069, -125.081 -79.075, + -125.097 -79.083, -125.112 -79.092, -125.125 -79.104, -125.137 -79.117, -125.146 -79.132, -125.154 -79.148, -125.16 -79.164, -125.163 -79.181, + -125.164 -79.199, -125.163 -79.217, -125.16 -79.234, -125.154 -79.25, -125.146 -79.266, -125.137 -79.281, -125.125 -79.294, -125.112 -79.306, + -125.097 -79.315, -125.081 -79.323, -125.065 -79.329, -125.048 -79.332, -125.03 -79.333, -125.012 -79.332, -124.995 -79.329, -124.979 -79.323, + -124.963 -79.315, -124.948 -79.306, -124.935 -79.294, -124.923 -79.281, -124.914 -79.266, -124.906 -79.25, -124.9 -79.234, -124.897 -79.217, -124.896 -79.199 + ) + ) + )"; + + + [Test] + public void WriteMultipolygonWithPolygonInHole() + { + var wktReader = new WKTReader(); + var wktMultiPolygon = (MultiPolygon)wktReader.Read(Wkt); + var feature = new Feature(wktMultiPolygon, new AttributesTable()); + + var tempShpPath = TestShapefiles.GetTempShpPath(); + Shapefile.WriteAllFeatures(Enumerable.Repeat(feature, 1), tempShpPath); + var shpMultiPolygon = (MultiPolygon)Shapefile.ReadAllGeometries(tempShpPath).First(); + TestShapefiles.DeleteShp(tempShpPath); + + Assert.IsTrue(shpMultiPolygon.EqualsExact(wktMultiPolygon), "SHP MultiPolygon is not equal to WKT MultiPolygon."); + } + } +} diff --git a/test/NetTopologySuite.IO.Esri.Test/NetTopologySuite.IO.Esri.Test.csproj b/test/NetTopologySuite.IO.Esri.Test/NetTopologySuite.IO.Esri.Test.csproj new file mode 100644 index 0000000..bb0865b --- /dev/null +++ b/test/NetTopologySuite.IO.Esri.Test/NetTopologySuite.IO.Esri.Test.csproj @@ -0,0 +1,28 @@ + + + + net6.0 + + false + + true + + + + + TestShapefiles/%(RecursiveDir)/%(Filename)%(Extension) + TestShapefiles/%(RecursiveDir)/%(Filename)%(Extension) + PreserveNewest + + + + + + + + + + + + + diff --git a/test/NetTopologySuite.IO.Esri.Test/TestShapefiles.cs b/test/NetTopologySuite.IO.Esri.Test/TestShapefiles.cs new file mode 100644 index 0000000..e1d2a87 --- /dev/null +++ b/test/NetTopologySuite.IO.Esri.Test/TestShapefiles.cs @@ -0,0 +1,49 @@ +using System; +using System.IO; + +internal sealed class TestShapefiles +{ + public static readonly string Directory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "TestShapefiles"); + + public static string PathTo(string shpName) + { + return Path.Combine(Directory, shpName); + } + + public static string PathToCountriesPt(string ext = ".shp") + { + return PathTo(Path.Combine("eurostat", "countries_pt" + ext)); + } + + public static string PathToCountriesLn(string ext = ".shp") + { + return PathTo(Path.Combine("eurostat", "countries_ln" + ext)); + } + + public static string PathToCountriesPg(string ext = ".shp") + { + return PathTo(Path.Combine("eurostat", "countries_pg" + ext)); + } + + /// + /// Creates a uniquely named, temporary shapefile name and returns the full path of that file. + /// + /// The full path of the temporary Shapefile. + public static string GetTempShpPath() + { + var tempFile = Path.GetTempFileName(); + var shpFile = Path.ChangeExtension(tempFile, ".shp"); + File.Move(tempFile, shpFile); + return shpFile; + } + + public static void DeleteShp(string shpPath) + { + var shpDir = Path.GetDirectoryName(shpPath); + var shpName = Path.GetFileNameWithoutExtension(shpPath); + foreach (var shpFile in System.IO.Directory.GetFiles(shpDir, shpName + ".*")) + { + File.Delete(shpFile); + } + } +} diff --git a/test/NetTopologySuite.IO.Esri.Test/Various/ReadmeSamples.cs b/test/NetTopologySuite.IO.Esri.Test/Various/ReadmeSamples.cs new file mode 100644 index 0000000..dd7a6e3 --- /dev/null +++ b/test/NetTopologySuite.IO.Esri.Test/Various/ReadmeSamples.cs @@ -0,0 +1,98 @@ +using NetTopologySuite.Features; +using NetTopologySuite.Geometries; +using NetTopologySuite.IO.Esri.Dbf; +using NetTopologySuite.IO.Esri.Shp.Readers; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.IO; + +namespace NetTopologySuite.IO.Esri.Test.Various +{ + /// + /// Tests for samples included int the README.md + /// + internal class ReadmeSamples + { + private readonly string dbfPath; + private readonly string shpPath; + + public ReadmeSamples() + { + shpPath = TestShapefiles.PathTo("fields_utf8.shp"); + dbfPath = Path.ChangeExtension(shpPath, ".dbf"); + } + + [Test] + public void DbfReader() + { + using var dbf = new DbfReader(dbfPath); + foreach (var record in dbf) + { + foreach (var fieldName in record.GetNames()) + { + Console.WriteLine($"{fieldName,10} {record[fieldName]}"); + } + Console.WriteLine(); + } + } + + [Test] + public void ShpReader() + { + foreach (var geometry in Shapefile.ReadAllGeometries(shpPath)) + { + Console.WriteLine(geometry); + } + } + + [Test] + public void ShapefileReader() + { + foreach (var feature in Shapefile.ReadAllFeatures(shpPath)) + { + foreach (var attrName in feature.Attributes.GetNames()) + { + Console.WriteLine($"{attrName,10}: {feature.Attributes[attrName]}"); + } + Console.WriteLine($" SHAPE: {feature.Geometry}"); + Console.WriteLine(); + } + } + + [Test] + public void ShapefileWriter() + { + var shpPath = TestShapefiles.GetTempShpPath(); + + var features = new List(); + for (int i = 1; i < 5; i++) + { + var lineCoords = new List + { + new CoordinateZ(i, i + 1, i), + new CoordinateZ(i, i, i), + new CoordinateZ(i + 1, i, i) + }; + var line = new LineString(lineCoords.ToArray()); + var mline = new MultiLineString(new LineString[] { line }); + + var attributes = new AttributesTable + { + { "date", new DateTime(2000, 1, i + 1) }, + { "float", i * 0.1 }, + { "int", i }, + { "logical", i % 2 == 0 }, + { "text", i.ToString("0.00") } + }; + + var feature = new Feature(mline, attributes); + features.Add(feature); + } + Shapefile.WriteAllFeatures(features, shpPath); + + TestShapefiles.DeleteShp(shpPath); + } + + } +} diff --git a/test/NetTopologySuite.IO.Esri.TestConsole/App.config b/test/NetTopologySuite.IO.Esri.TestConsole/App.config deleted file mode 100644 index 56efbc7..0000000 --- a/test/NetTopologySuite.IO.Esri.TestConsole/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/test/NetTopologySuite.IO.Esri.TestConsole/NetTopologySuite.IO.Esri.TestConsole.csproj b/test/NetTopologySuite.IO.Esri.TestConsole/NetTopologySuite.IO.Esri.TestConsole.csproj index 9b2c1c1..fbb3db2 100644 --- a/test/NetTopologySuite.IO.Esri.TestConsole/NetTopologySuite.IO.Esri.TestConsole.csproj +++ b/test/NetTopologySuite.IO.Esri.TestConsole/NetTopologySuite.IO.Esri.TestConsole.csproj @@ -1,85 +1,15 @@ - - - + + - Debug - AnyCPU - {AA37EFC6-737F-4E8A-A79C-2D179C1B38EC} Exe - NetTopologySuite.IO.Esri.TestConsole - NetTopologySuite.IO.Esri.TestConsole - v4.7.2 - 512 - true - true + net6.0 + enable + enable + NetTopologySuite.IO.Esri.TestConsole.Program - - AnyCPU - true - full - false - bin\Debug\ - TRACE;DEBUG;DEBUG_BINARY - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 2.1.0 - - - 2.1.0 - - + - - {6e95bc91-8066-4420-b61f-854de5f3a746} - NetTopologySuite.IO.Esri.Shapefile - + - - \ No newline at end of file + + diff --git a/test/NetTopologySuite.IO.Esri.TestConsole/Program.cs b/test/NetTopologySuite.IO.Esri.TestConsole/Program.cs index 3ba030c..3cec766 100644 --- a/test/NetTopologySuite.IO.Esri.TestConsole/Program.cs +++ b/test/NetTopologySuite.IO.Esri.TestConsole/Program.cs @@ -6,7 +6,7 @@ namespace NetTopologySuite.IO.Esri.TestConsole class Program { - private static Test[] TestList = { + private static readonly Test[] TestList = { new DBF_ReadIterator(), //"arcmap/shp/fields_utf8.dbf" new DBF_ReadCore(), new SHP_ReadCore(), @@ -68,11 +68,11 @@ private static void WriteTestList() Console.Write("Write test number or pres ENTER to exit: "); } - private static void WriteError(Exception ex, bool exitApp = false) + private static void WriteError(Exception? ex, bool exitApp = false) { Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine("ERROR: " + ex.Message); - ex = ex.InnerException; + Console.WriteLine("ERROR: " + ex?.Message); + ex = ex?.InnerException; while (ex != null) { Console.WriteLine("- " + ex.Message); diff --git a/test/NetTopologySuite.IO.Esri.TestConsole/Properties/AssemblyInfo.cs b/test/NetTopologySuite.IO.Esri.TestConsole/Properties/AssemblyInfo.cs deleted file mode 100644 index 503f24d..0000000 --- a/test/NetTopologySuite.IO.Esri.TestConsole/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("NetTopologySuite.IO.Esri.TestConsole")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("NetTopologySuite.IO.Esri.TestConsole")] -[assembly: AssemblyCopyright("Copyright © 2021")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("aa37efc6-737f-4e8a-a79c-2d179c1b38ec")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/test/NetTopologySuite.IO.Esri.TestConsole/Samples/DBF_ReadIterator.cs b/test/NetTopologySuite.IO.Esri.TestConsole/Samples/DBF_ReadIterator.cs index 90bd5bf..8f615de 100644 --- a/test/NetTopologySuite.IO.Esri.TestConsole/Samples/DBF_ReadIterator.cs +++ b/test/NetTopologySuite.IO.Esri.TestConsole/Samples/DBF_ReadIterator.cs @@ -8,18 +8,15 @@ public class DBF_ReadIterator : Test public override void Run() { var dbfPath = GetTestFilePath("arcmap/shp/pt_utf8.dbf"); - using (var dbf = new DbfReader(dbfPath)) + using var dbf = new DbfReader(dbfPath); + foreach (var fields in dbf) { - foreach (var fields in dbf) + Console.WriteLine("Record ID: " + fields["Id"]); + foreach (var fieldName in fields.GetNames()) { - Console.WriteLine("Record ID: " + fields["Id"]); - var fieldNames = fields.Keys; - foreach (var fieldName in fieldNames) - { - Console.WriteLine($"{fieldName, 10} {fields[fieldName]}"); - } - Console.WriteLine(); + Console.WriteLine($"{fieldName, 10} {fields[fieldName]}"); } + Console.WriteLine(); } } diff --git a/test/NetTopologySuite.IO.Esri.TestConsole/Samples/SHP_ReadCore.cs b/test/NetTopologySuite.IO.Esri.TestConsole/Samples/SHP_ReadCore.cs index af890d4..1e95118 100644 --- a/test/NetTopologySuite.IO.Esri.TestConsole/Samples/SHP_ReadCore.cs +++ b/test/NetTopologySuite.IO.Esri.TestConsole/Samples/SHP_ReadCore.cs @@ -12,11 +12,11 @@ public override void Run() var shpPath = GetTestFilePath("arcmap/shp/pt_utf8.shp"); using (var shpStream = File.OpenRead(shpPath)) - using (var shp = new ShpPointReader(shpStream, GeometryFactory.Default)) + using (var shp = new ShpPointReader(shpStream)) { while (shp.Read()) { - Console.WriteLine(shp.Geometry); + Console.WriteLine(shp.Shape); } } } diff --git a/test/NetTopologySuite.IO.Esri.TestConsole/Samples/Shapefile_WriteLineCore.cs b/test/NetTopologySuite.IO.Esri.TestConsole/Samples/Shapefile_WriteLineCore.cs index 025e55d..31486e0 100644 --- a/test/NetTopologySuite.IO.Esri.TestConsole/Samples/Shapefile_WriteLineCore.cs +++ b/test/NetTopologySuite.IO.Esri.TestConsole/Samples/Shapefile_WriteLineCore.cs @@ -11,17 +11,18 @@ public override void Run() { var shpPath = GetTempFilePath("abcd2.shp"); - var dateField = new DbfDateField("date"); - var floatField = new DbfFloatField("float"); - var intField = new DbfNumericField("int"); - var LogicalField = new DbfLogicalField("logical"); - var textField = new DbfCharacterField("text"); + var options = new ShapefileWriterOptions(ShapeType.PolyLine); + var dateField = options.AddDateField("date"); + var floatField = options.AddFloatField("float"); + var intField = options.AddNumericInt32Field("int"); + var LogicalField = options.AddLogicalField("logical"); + var textField = options.AddCharacterField("text"); var pointsBuffer = new CoordinateSequenceBuffer(); var lineBuffer = GeometryFactory.Default.CreateLineString(pointsBuffer); var multiLineBuffer = GeometryFactory.Default.CreateMultiLineString(new LineString[] { lineBuffer }); - using (var shp = new ShapefilePolyLineWriter(shpPath, ShapeType.PolyLine, dateField, floatField, intField, LogicalField, textField)) + using (var shp = new ShapefilePolyLineWriter(shpPath, options)) { for (int i = 1; i < 5; i++) { diff --git a/test/NetTopologySuite.IO.Esri.TestConsole/Samples/Shapefile_WriteLineFeatures.cs b/test/NetTopologySuite.IO.Esri.TestConsole/Samples/Shapefile_WriteLineFeatures.cs index 55c7666..74388f2 100644 --- a/test/NetTopologySuite.IO.Esri.TestConsole/Samples/Shapefile_WriteLineFeatures.cs +++ b/test/NetTopologySuite.IO.Esri.TestConsole/Samples/Shapefile_WriteLineFeatures.cs @@ -13,15 +13,15 @@ public override void Run() { var shpPath = GetTempFilePath("abcd1.shp"); - - var dateField = DbfField.Create("date", typeof(DateTime)); - var floatField = DbfField.Create("float", typeof(double)); - var intField = DbfField.Create("int", typeof(int)); - var LogicalField = DbfField.Create("logical", typeof(bool)); - var textField = DbfField.Create("text", typeof(string)); + var options = new ShapefileWriterOptions(ShapeType.PolyLine); + options.AddField("date", typeof(DateTime)); + options.AddField("float", typeof(double)); + options.AddField("int", typeof(int)); + options.AddField("logical", typeof(bool)); + options.AddField("text", typeof(string)); var features = GetFeatures(); - using (var shp = new ShapefilePolyLineWriter(shpPath, ShapeType.PolyLine, dateField, floatField, intField, LogicalField, textField)) + using (var shp = new ShapefilePolyLineWriter(shpPath, options)) { shp.Write(features); Console.WriteLine($"{features.Count} features was written."); @@ -33,7 +33,7 @@ public override void Run() } } - private List GetFeatures() + private static List GetFeatures() { var features = new List(); for (int i = 1; i < 5; i++) @@ -44,12 +44,14 @@ private List GetFeatures() var line = GeometryFactory.Default.CreateLineString(new Coordinate[] { p1, p2, p3 }); var mline = GeometryFactory.Default.CreateMultiLineString(new LineString[] { line }); - var attributes = new AttributesTable(StringComparer.Ordinal); - attributes.Add("date", new DateTime(2000, 1, i + 1)); - attributes.Add("float", i * 0.1); - attributes.Add("int", i); - attributes.Add("logical", i % 2 == 0); - attributes.Add("text", i.ToString("0.00")); + var attributes = new AttributesTable(new(StringComparer.Ordinal)) + { + { "date", new DateTime(2000, 1, i + 1) }, + { "float", i * 0.1 }, + { "int", i }, + { "logical", i % 2 == 0 }, + { "text", i.ToString("0.00") } + }; var feature = new Feature(mline, attributes); features.Add(feature); diff --git a/test/NetTopologySuite.IO.Esri.TestConsole/Samples/Shapefile_WritePointCore.cs b/test/NetTopologySuite.IO.Esri.TestConsole/Samples/Shapefile_WritePointCore.cs index 53e0473..ed65b4b 100644 --- a/test/NetTopologySuite.IO.Esri.TestConsole/Samples/Shapefile_WritePointCore.cs +++ b/test/NetTopologySuite.IO.Esri.TestConsole/Samples/Shapefile_WritePointCore.cs @@ -11,20 +11,22 @@ public override void Run() { var shpPath = GetTempFilePath("point.shp"); - var dateField = new DbfDateField("date"); - var floatField = new DbfFloatField("float"); - var intField = new DbfNumericField("int"); - var LogicalField = new DbfLogicalField("logical"); - var textField = new DbfCharacterField("text"); - using (var shp = new ShapefilePointWriter(shpPath, ShapeType.Point, dateField, floatField, intField, LogicalField, textField)) + var options = new ShapefileWriterOptions(ShapeType.Point); + var dateField = options.AddDateField("date"); + var floatField = options.AddFloatField("float"); + var intField = options.AddNumericInt32Field("int"); + var logicalField = options.AddLogicalField("logical"); + var textField = options.AddCharacterField("text"); + + using (var shp = new ShapefilePointWriter(shpPath, options)) { for (int i = 1; i < 5; i++) { dateField.DateValue = new DateTime(2000, 1, i + 1); floatField.NumericValue = i * 0.1; intField.NumericValue = i; - LogicalField.LogicalValue = i % 2 == 0; + logicalField.LogicalValue = i % 2 == 0; textField.StringValue = i.ToString("0.00"); var coordinates = new CoordinateZ(i, i + 1, i + 2); diff --git a/test/NetTopologySuite.IO.Esri.TestConsole/Tests/CompareShapefilesTest.cs b/test/NetTopologySuite.IO.Esri.TestConsole/Tests/CompareShapefilesTest.cs index 0eb3769..5b617d4 100644 --- a/test/NetTopologySuite.IO.Esri.TestConsole/Tests/CompareShapefilesTest.cs +++ b/test/NetTopologySuite.IO.Esri.TestConsole/Tests/CompareShapefilesTest.cs @@ -37,7 +37,7 @@ protected string CopyShapefile(string srcPath) //srcPath = GetTestFilePath(srcPath); using (var src = Shapefile.OpenRead(srcPath)) - using (var copy = Shapefile.OpenWrite(destPath, src.ShapeType, src.Fields, src.Encoding, src.Projection)) + using (var copy = Shapefile.OpenWrite(destPath, new ShapefileWriterOptions(src))) { var srcFeatures = src.ToArray(); copy.Write(srcFeatures); diff --git a/test/NetTopologySuite.IO.Esri.TestConsole/Tests/CopyAndCompareArcMapShapefilesTest.cs b/test/NetTopologySuite.IO.Esri.TestConsole/Tests/CopyAndCompareArcMapShapefilesTest.cs index 1ea346d..4c6caf1 100644 --- a/test/NetTopologySuite.IO.Esri.TestConsole/Tests/CopyAndCompareArcMapShapefilesTest.cs +++ b/test/NetTopologySuite.IO.Esri.TestConsole/Tests/CopyAndCompareArcMapShapefilesTest.cs @@ -7,7 +7,7 @@ public class CopyAndCompareArcMapShapefilesTest : CompareShapefilesTest public override void Run() { var filePath = GetTestFilePath("arcmap/shp/point.shp"); - var shpDataDir = Path.GetDirectoryName(filePath); + var shpDataDir = Path.GetDirectoryName(filePath) ?? "shp_dir"; foreach (var shpFilePath in Directory.GetFiles(shpDataDir, "*.shp")) { diff --git a/test/NetTopologySuite.IO.Esri.TestConsole/Tests/ReadArcMapFilesTest.cs b/test/NetTopologySuite.IO.Esri.TestConsole/Tests/ReadArcMapFilesTest.cs index 5c98c1e..7a39335 100644 --- a/test/NetTopologySuite.IO.Esri.TestConsole/Tests/ReadArcMapFilesTest.cs +++ b/test/NetTopologySuite.IO.Esri.TestConsole/Tests/ReadArcMapFilesTest.cs @@ -7,18 +7,15 @@ public class ReadArcMapFilesTest : Test public override void Run() { var filePath = GetTestFilePath("arcmap/shp/point.shp"); - var shpDataDir = Path.GetDirectoryName(filePath); + var shpDataDir = Path.GetDirectoryName(filePath) ?? "shp_dir"; foreach (var shpFilePath in Directory.GetFiles(shpDataDir, "*.shp")) { var fileName = "arcmap/shp/" + Path.GetFileName(shpFilePath); PrintSectionTitle(fileName); - using (var shapefile = Shapefile.OpenRead(shpFilePath)) - { - PrintFeatures(shapefile); - - } + using var shapefile = Shapefile.OpenRead(shpFilePath); + PrintFeatures(shapefile); } } } diff --git a/test/NetTopologySuite.IO.Esri.TestConsole/Tests/Test.cs b/test/NetTopologySuite.IO.Esri.TestConsole/Tests/Test.cs index 06d84da..e4513a2 100644 --- a/test/NetTopologySuite.IO.Esri.TestConsole/Tests/Test.cs +++ b/test/NetTopologySuite.IO.Esri.TestConsole/Tests/Test.cs @@ -23,11 +23,11 @@ public Test() public static readonly string FieldSpace = " ".PadRight(12); - public static string TestDataDir = GetTestDataDir(Assembly.GetExecutingAssembly().Location); + public static string TestDataDir { get; private set; } = GetTestDataDir(Assembly.GetExecutingAssembly().Location); private static string GetTestDataDir(string dir) { - dir = Path.GetDirectoryName(dir); + dir = Path.GetDirectoryName(dir) ?? ""; var testDataDir = Path.Combine(dir, "TestData"); if (Directory.Exists(testDataDir)) @@ -58,7 +58,7 @@ public static string GetTestFilePath(string filePath) public static string GetTempFilePath(string fileName) { var tempFilePath = Path.Combine(TestDataDir, "temp", fileName); - Directory.CreateDirectory(Path.GetDirectoryName(tempFilePath)); + Directory.CreateDirectory(Path.GetDirectoryName(tempFilePath) ?? "shp_test"); return tempFilePath; } @@ -76,7 +76,7 @@ public override string ToString() return Title; } - protected void PrintFieldNames(IReadOnlyList fields) + protected static void PrintFieldNames(IReadOnlyList fields) { Console.WriteLine("FIELD LIST"); Console.WriteLine("----------"); @@ -87,32 +87,32 @@ protected void PrintFieldNames(IReadOnlyList fields) Console.WriteLine(); } - protected void PrintFieldValues(IReadOnlyDictionary values) + protected static void PrintFieldValues(IAttributesTable fields) { Console.WriteLine(); - foreach (var nameVal in values) + foreach (var name in fields.GetNames()) { - PrintFieldValue(nameVal.Key, nameVal.Value); + PrintFieldValue(name, fields[name]); } } - protected void PrintFieldValue(string name, object value) + protected static void PrintFieldValue(string name, object value) { name += ": "; Console.WriteLine(name.PadRight(12) + ToText(value)); } - public void PrintFields(DbfReader dbf) + public static void PrintFields(DbfReader dbf) { PrintFieldNames(dbf.Fields); - foreach (var values in dbf) + foreach (var fields in dbf) { - PrintFieldValues(values); + PrintFieldValues(fields); } } - protected void PrintGeometry(Geometry geometry) + protected static void PrintGeometry(Geometry geometry) { if (geometry.IsEmpty) { @@ -137,7 +137,7 @@ protected void PrintGeometry(Geometry geometry) } } - private void PrintCoordinates(Coordinate[] coordinates) + private static void PrintCoordinates(Coordinate[] coordinates) { foreach (var c in coordinates) { @@ -146,7 +146,7 @@ private void PrintCoordinates(Coordinate[] coordinates) } - public void PrintFeature(Feature feature) + public static void PrintFeature(Feature feature) { Console.WriteLine(); foreach (var name in feature.Attributes.GetNames()) @@ -156,7 +156,7 @@ public void PrintFeature(Feature feature) PrintGeometry(feature.Geometry); } - public void PrintFeatures(ShapefileReader shp) + public static void PrintFeatures(ShapefileReader shp) { PrintFieldNames(shp.Fields); @@ -169,24 +169,21 @@ public void PrintFeatures(ShapefileReader shp) } - protected string ToText(object value) + protected static string ToText(object value) { - if (value == null) - return ""; - if (value is string s) return "'" + s + "'"; - return value.ToString(); + return value?.ToString() ?? ""; } - protected void PrintRecordListHeader() + protected static void PrintRecordListHeader() { Console.WriteLine("RECORD LIST"); Console.WriteLine("-----------"); Console.WriteLine(); } - public void PrintSectionTitle(string title) + public static void PrintSectionTitle(string title) { Console.ForegroundColor = ConsoleColor.White; Console.WriteLine(); @@ -197,14 +194,14 @@ public void PrintSectionTitle(string title) Console.ResetColor(); } - public void PrintValidationResult(bool isValid, string message) + public static void PrintValidationResult(bool isValid, string message) { Console.ForegroundColor = isValid ? ConsoleColor.Green : ConsoleColor.Red; Console.WriteLine(message); Console.ResetColor(); } - public CoordinateSequence CreateCoordinateSequence(int size, Ordinates ordinates = Ordinates.XY) + public static CoordinateSequence CreateCoordinateSequence(int size, Ordinates ordinates = Ordinates.XY) { return NtsGeometryServices.Instance.DefaultCoordinateSequenceFactory.Create(size, ordinates); } diff --git a/test/TestData/TestShapefiles/AllNulls.dbf b/test/TestData/TestShapefiles/AllNulls.dbf new file mode 100644 index 0000000..ba2881d Binary files /dev/null and b/test/TestData/TestShapefiles/AllNulls.dbf differ diff --git a/test/TestData/TestShapefiles/AllNulls.shp b/test/TestData/TestShapefiles/AllNulls.shp new file mode 100644 index 0000000..272ad87 Binary files /dev/null and b/test/TestData/TestShapefiles/AllNulls.shp differ diff --git a/test/TestData/TestShapefiles/CA_Cable_region.dbf b/test/TestData/TestShapefiles/CA_Cable_region.dbf new file mode 100644 index 0000000..c58bd76 Binary files /dev/null and b/test/TestData/TestShapefiles/CA_Cable_region.dbf differ diff --git a/test/TestData/TestShapefiles/CA_Cable_region.shp b/test/TestData/TestShapefiles/CA_Cable_region.shp new file mode 100644 index 0000000..ffe2306 Binary files /dev/null and b/test/TestData/TestShapefiles/CA_Cable_region.shp differ diff --git a/test/TestData/TestShapefiles/EmptyShapeFile.shp b/test/TestData/TestShapefiles/EmptyShapeFile.shp new file mode 100644 index 0000000..29de63a Binary files /dev/null and b/test/TestData/TestShapefiles/EmptyShapeFile.shp differ diff --git a/test/TestData/TestShapefiles/Issue167.shp b/test/TestData/TestShapefiles/Issue167.shp new file mode 100644 index 0000000..25fc301 Binary files /dev/null and b/test/TestData/TestShapefiles/Issue167.shp differ diff --git a/test/TestData/TestShapefiles/LSOA_2011_EW_BGC.dbf b/test/TestData/TestShapefiles/LSOA_2011_EW_BGC.dbf new file mode 100644 index 0000000..4596edd Binary files /dev/null and b/test/TestData/TestShapefiles/LSOA_2011_EW_BGC.dbf differ diff --git a/test/TestData/TestShapefiles/LSOA_2011_EW_BGC.prj b/test/TestData/TestShapefiles/LSOA_2011_EW_BGC.prj new file mode 100644 index 0000000..429899d --- /dev/null +++ b/test/TestData/TestShapefiles/LSOA_2011_EW_BGC.prj @@ -0,0 +1 @@ +PROJCS["British_National_Grid",GEOGCS["GCS_OSGB_1936",DATUM["D_OSGB_1936",SPHEROID["Airy_1830",6377563.396,299.3249646]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Transverse_Mercator"],PARAMETER["False_Easting",400000.0],PARAMETER["False_Northing",-100000.0],PARAMETER["Central_Meridian",-2.0],PARAMETER["Scale_Factor",0.999601272],PARAMETER["Latitude_Of_Origin",49.0],UNIT["Meter",1.0]] \ No newline at end of file diff --git a/test/TestData/TestShapefiles/LSOA_2011_EW_BGC.sbn b/test/TestData/TestShapefiles/LSOA_2011_EW_BGC.sbn new file mode 100644 index 0000000..c799875 Binary files /dev/null and b/test/TestData/TestShapefiles/LSOA_2011_EW_BGC.sbn differ diff --git a/test/TestData/TestShapefiles/LSOA_2011_EW_BGC.sbx b/test/TestData/TestShapefiles/LSOA_2011_EW_BGC.sbx new file mode 100644 index 0000000..df6e7fc Binary files /dev/null and b/test/TestData/TestShapefiles/LSOA_2011_EW_BGC.sbx differ diff --git a/test/TestData/TestShapefiles/LSOA_2011_EW_BGC.shp b/test/TestData/TestShapefiles/LSOA_2011_EW_BGC.shp new file mode 100644 index 0000000..26d5445 Binary files /dev/null and b/test/TestData/TestShapefiles/LSOA_2011_EW_BGC.shp differ diff --git a/test/TestData/TestShapefiles/LSOA_2011_EW_BGC.shx b/test/TestData/TestShapefiles/LSOA_2011_EW_BGC.shx new file mode 100644 index 0000000..aca182b Binary files /dev/null and b/test/TestData/TestShapefiles/LSOA_2011_EW_BGC.shx differ diff --git a/test/TestData/TestShapefiles/Sept_polygones.dbf b/test/TestData/TestShapefiles/Sept_polygones.dbf new file mode 100644 index 0000000..68480aa Binary files /dev/null and b/test/TestData/TestShapefiles/Sept_polygones.dbf differ diff --git a/test/TestData/TestShapefiles/Sept_polygones.shp b/test/TestData/TestShapefiles/Sept_polygones.shp new file mode 100644 index 0000000..276fd1b Binary files /dev/null and b/test/TestData/TestShapefiles/Sept_polygones.shp differ diff --git a/test/TestData/TestShapefiles/US_DMA_region.dbf b/test/TestData/TestShapefiles/US_DMA_region.dbf new file mode 100644 index 0000000..09c0f19 Binary files /dev/null and b/test/TestData/TestShapefiles/US_DMA_region.dbf differ diff --git a/test/TestData/TestShapefiles/US_DMA_region.shp b/test/TestData/TestShapefiles/US_DMA_region.shp new file mode 100644 index 0000000..752e1fe Binary files /dev/null and b/test/TestData/TestShapefiles/US_DMA_region.shp differ diff --git a/test/TestData/TestShapefiles/UnifiedChecksMaterial.dbf b/test/TestData/TestShapefiles/UnifiedChecksMaterial.dbf new file mode 100644 index 0000000..241ab8b Binary files /dev/null and b/test/TestData/TestShapefiles/UnifiedChecksMaterial.dbf differ diff --git a/test/TestData/TestShapefiles/UnifiedChecksMaterial.shp b/test/TestData/TestShapefiles/UnifiedChecksMaterial.shp new file mode 100644 index 0000000..675a882 Binary files /dev/null and b/test/TestData/TestShapefiles/UnifiedChecksMaterial.shp differ diff --git a/test/TestData/TestShapefiles/UnifiedChecksMaterialNullAtEnd.shp b/test/TestData/TestShapefiles/UnifiedChecksMaterialNullAtEnd.shp new file mode 100644 index 0000000..7accf20 Binary files /dev/null and b/test/TestData/TestShapefiles/UnifiedChecksMaterialNullAtEnd.shp differ diff --git a/test/TestData/TestShapefiles/UnifiedChecksMaterialNullAtStart.shp b/test/TestData/TestShapefiles/UnifiedChecksMaterialNullAtStart.shp new file mode 100644 index 0000000..44cfa8c Binary files /dev/null and b/test/TestData/TestShapefiles/UnifiedChecksMaterialNullAtStart.shp differ diff --git a/test/TestData/TestShapefiles/UnifiedChecksMaterialNullInMiddle.shp b/test/TestData/TestShapefiles/UnifiedChecksMaterialNullInMiddle.shp new file mode 100644 index 0000000..dc121e8 Binary files /dev/null and b/test/TestData/TestShapefiles/UnifiedChecksMaterialNullInMiddle.shp differ diff --git a/test/TestData/TestShapefiles/Victoria North.dbf b/test/TestData/TestShapefiles/Victoria North.dbf new file mode 100644 index 0000000..b76934f Binary files /dev/null and b/test/TestData/TestShapefiles/Victoria North.dbf differ diff --git a/test/TestData/TestShapefiles/Victoria North.shp b/test/TestData/TestShapefiles/Victoria North.shp new file mode 100644 index 0000000..f5326f3 Binary files /dev/null and b/test/TestData/TestShapefiles/Victoria North.shp differ diff --git a/test/TestData/TestShapefiles/Victoria North.shx b/test/TestData/TestShapefiles/Victoria North.shx new file mode 100644 index 0000000..665af3e Binary files /dev/null and b/test/TestData/TestShapefiles/Victoria North.shx differ diff --git a/test/TestData/TestShapefiles/Volume2.shp b/test/TestData/TestShapefiles/Volume2.shp new file mode 100644 index 0000000..2cedba2 Binary files /dev/null and b/test/TestData/TestShapefiles/Volume2.shp differ diff --git a/test/TestData/TestShapefiles/__emptyShapefile.dbf b/test/TestData/TestShapefiles/__emptyShapefile.dbf new file mode 100644 index 0000000..cc042e7 Binary files /dev/null and b/test/TestData/TestShapefiles/__emptyShapefile.dbf differ diff --git a/test/TestData/TestShapefiles/__emptyShapefile.shp b/test/TestData/TestShapefiles/__emptyShapefile.shp new file mode 100644 index 0000000..07da08d Binary files /dev/null and b/test/TestData/TestShapefiles/__emptyShapefile.shp differ diff --git a/test/TestData/TestShapefiles/__emptyShapefile.shx b/test/TestData/TestShapefiles/__emptyShapefile.shx new file mode 100644 index 0000000..07da08d Binary files /dev/null and b/test/TestData/TestShapefiles/__emptyShapefile.shx differ diff --git a/test/TestData/TestShapefiles/afvalbakken.dbf b/test/TestData/TestShapefiles/afvalbakken.dbf new file mode 100644 index 0000000..e6f05f2 Binary files /dev/null and b/test/TestData/TestShapefiles/afvalbakken.dbf differ diff --git a/test/TestData/TestShapefiles/afvalbakken.shp b/test/TestData/TestShapefiles/afvalbakken.shp new file mode 100644 index 0000000..33c40b9 Binary files /dev/null and b/test/TestData/TestShapefiles/afvalbakken.shp differ diff --git a/test/TestData/TestShapefiles/chinese_encoding.dbf b/test/TestData/TestShapefiles/chinese_encoding.dbf new file mode 100644 index 0000000..e919183 Binary files /dev/null and b/test/TestData/TestShapefiles/chinese_encoding.dbf differ diff --git a/test/TestData/TestShapefiles/chinese_encoding.shp b/test/TestData/TestShapefiles/chinese_encoding.shp new file mode 100644 index 0000000..13c8fab Binary files /dev/null and b/test/TestData/TestShapefiles/chinese_encoding.shp differ diff --git a/test/TestData/TestShapefiles/chinese_encoding.shx b/test/TestData/TestShapefiles/chinese_encoding.shx new file mode 100644 index 0000000..1c684b1 Binary files /dev/null and b/test/TestData/TestShapefiles/chinese_encoding.shx differ diff --git a/test/TestData/TestShapefiles/christchurch-canterbury-h.shp b/test/TestData/TestShapefiles/christchurch-canterbury-h.shp new file mode 100644 index 0000000..1a7281b --- /dev/null +++ b/test/TestData/TestShapefiles/christchurch-canterbury-h.shp @@ -0,0 +1,592 @@ + ' + z  /d se@w2EYo,e@> |~E  + )(Pe@^"SE  + $ Ξe@ܗE  + =* + gp1e@4+zE ? + ᩰe@\QB-E @ + __e@Z E A + Ede@!0E B + 5jLe@jE C + Tbe@Ԟo_E D + B=%e@"^E E + ܯe@E F + >:[e@aœgE G + Ub)e@)_P}%E H + e@\KE I + |*e@CmɌE J + iEe@BpuE K + B˜e@2E L + 6Te@3%E M + !pe@x]:E N + cΰe@dE O + XVe@X |~E \ + 8#)e@ʟx5E ] + J6e@PE ^ + ,ڴee@ fE _ + ze@zDME ` + j^e@ 絨E a + J22œe@*E b + Fe@IE c + غe@;uCE d + *fe@xE e + HĖe@N-u.E f + 9Ɖe@nE g + d#e@E h + f e@u#E i + ×e@eE j + KYUBe@ȽzE k + ԧe@1E { + ƪƀ=e@5E | + bue@+lE } + *y?ke@'E ~ + ~ue@-QE  + 2yJNe@!\E + e@ݹ>=E + n۰Ye@sI<E + z/Ϟe@IE + e@sݭE + ءje@`)E + 3i"e@)E + e@C-E + R!e@*`~E + ue@70E + Bpٞe@d=>E + X,e@. J E + )e@_iE + {W'|؞e@DVE + R'e@ 03]E + &r.e@uE + E"Ȟe@kE + YG\ e@œ"E + HTô +e@֟aE + .Şe@E + l})e@wroE + Ae@:E + JBݓe@i6?zE + =ke@^E + -e@jVE + >e@R+zE + e@=iE + ΐFfe@)IE + I{f1e@}eE + O A&"e@sE + I{f1e@}eE + ?4e@6aE + E?e@ E + |)e@Jm%E + [ɍe@×E + -*e@^E + xגe@GE + ;e@S@E + e@2bQE + >e@ E + >e@ E + >e@ E + :L e@X E + e@?E + {Ge@5E + wPe@[GS6E + +Rce@HJDYE + &e@j5vxE + qe@;ݕE + {/[Ke@)lE + F#d3he@K\E + 4 +ғe@DIfE + -?pe@GE + 5{e@z;x_E + )Ie@EۈE + Nёe@NrΩE + [e@2E + >g;ie@DdE + }de@ .E + Te@AdC{E + ,e@ce'rE + 嬆m0e@bE + QMe@!sE + ]ntؓe@u]E + tJe@eE + \?e@']ӥE + !e@ XE + )e@؇Q>E + F;e@ oE + ꖌe@N+E + <e@`=E + { e@E + b"e@VE + Ke@^E + 9+_e@мE + ,ye@$E + De@F|'E + Ahe@ ~VE + #87e@I6E + e@԰kE + ʷke@%E + le@:=rE + Phe@0E + 6ee@ysE + -Se@VE + Es Je@iWE + hHe@ZdE + le@?^E + QTe@)E + ]Dqe@3_E + p(e@FE + Y?e@ղE + gvse@i;[E + ,$re@4E + yke@kE + ~:e@flE  + .ee@('E  + m;W#e@GQE  + >g#3e@`E  + UN e@rZ;E  + U)=e@ΘjE  + tPe@6.ɲE  + ڊre@BRHE  + X8ړe@n0 E  + .e@E ! + Q0e@wE " + X K8e@%/E & + \e@h=?e@ʷEE 1 + Ʈڔe@jE 2 + *e@zAE 3 + #X{e@W%E 4 + kfe@=E 5 + t%7e@*E 6 + Mxe@8[3E 7 + %2e@g CE 8 + )Ie@EۈE 9 + P?e@}E : + NѶhe@uE ; + ΄/Q7e@YE < + %~e@G\E = + q de@RqE > + nK#\e@nsE ? + \e@agE @ + +he@3KE A + je@|E B + 4u%e@ڰzE C + /o>e@1E D + dcFPe@(z8E E + @Pe@r6 E F + A^Z7e@MqE G + Ie3e@nqE H + ࡓe@E I + 1e@aOtE J + YE V + <;e@K +WE W + Koe@ LE X + Lbߵie@_bE Y + `C,e@ DE Z + `C,e@ DE [ + e@R+zE _ + F4e@+6ݰE ` + ?T5e@ g2˰E a + s/e@5E b + %ue@ME c + De@󐉓E d + Te@~E e + 9e@/E f + Дe@V衰E g + e@,FE h + >e@R+zE i + >e@R+zE j + O$e@0W*E k + 0Ne@#iE l + e@䗧E m + tAe@)ME n + SD{e@ 3E o + q@,e@t78-5E p + iV{e@xWtE q + iN*e@өNצE r + "e@YR)E s + 8(ߒe@&=E t + 2ƒe@k E u + *4e@U?*E v + SY/e@ץ#,E w + ɒe@[T4TE x + NÒe@@qWE y + Y e@'#@SҦE z + # e@nۦE { + u/e@1)E | + I|:e@E } + 1.e@9#E ~ + r8#te@zE  + ? ge@!ptE  + +pe@_E  + ue@70E  + +pe@_E  + Ah֜e@E  + +pe@_E  + +pe@_E  + 3{ƙe@fJDE  + ɞe@Y E  + _e@|-E  + e@$E  + &r.e@uE  + +=Ӕe@+E  + (\e@wE  + De@,(E  + q)Ke@E  + j ’e@6LE  + wKe@NF E  + 4+̞e@E  + ٦e@YeE  + J,e@ E  + \-a̞e@p E  + $e@bXwE  + ,e@GE  + A +e@Uci>tE  + Xe@0 E  + ue@70E  + 9Gue@tE  + 2e@MZE  + h0Me@ڷ1E  + Ʈڔe@jE  + Ʈڔe@jE  + ƞe@E  + ᩰe@\QB-E  + *K}e@N4 E  + GJe@6\E  + PHݔe@V`E  + b32e@ԄJE  + Z.#j&e@(OME  + ge@[ڦ E  + Dye@E%E  + Dye@E%E  + Һee@R<3E  + \$Tϓe@Ϥ*nE  + DKDe@ ,=E  + 'dve@OE  + ?,e@yTkE  + záe@ܰ08E  + SObe@%rΙ6E  + ype@a=uPE  + Ps e@\E  + nˏc͖e@pDE  + Y&e@\E  + Y&e@\E  + ]{-e@͚ E  + mwm@e@E  + Z]Te@x68E  + Z]Te@x68E  + Z]Te@x68E  + Z]Te@x68E  + Z]Te@x68E  + Ȝe@ʼ[YFe@-YE  + 聰]e@pM93E  + ige@P5AE  + n*He@ͻE ! + Vce@NE " + RD{Me@mZE # + ^e@3E +E $ + TB%ޓe@yHE % + zke@TE & + RD{Me@mZE ' + ae@!JE ( + gTe@̲E ) + 3e@^3w-E * + jk3ae@9E + +  +Ge@p E , + $gme@YmE - + ⿘'e@͉@E . + @9e@ԫC>E / + @e@: SE 0 + ZILe@1wE 1 + U; ge@l,E 2 + h1e@Z8E 3 + ar؞e@AE 4 + N^oe@M CDE 5 + x;ݤe@uE 6 + 3cAye@ߏu"E 7 + lQe@BE 8 + J"e@4VE 9 + RYoe@o6E : + Me@ُޙE ; + e@ \ No newline at end of file diff --git a/test/TestData/TestShapefiles/eurostat/countries_ln.cpg b/test/TestData/TestShapefiles/eurostat/countries_ln.cpg new file mode 100644 index 0000000..3ad133c --- /dev/null +++ b/test/TestData/TestShapefiles/eurostat/countries_ln.cpg @@ -0,0 +1 @@ +UTF-8 \ No newline at end of file diff --git a/test/TestData/TestShapefiles/eurostat/countries_ln.dbf b/test/TestData/TestShapefiles/eurostat/countries_ln.dbf new file mode 100644 index 0000000..3c2caf2 Binary files /dev/null and b/test/TestData/TestShapefiles/eurostat/countries_ln.dbf differ diff --git a/test/TestData/TestShapefiles/eurostat/countries_ln.prj b/test/TestData/TestShapefiles/eurostat/countries_ln.prj new file mode 100644 index 0000000..f45cbad --- /dev/null +++ b/test/TestData/TestShapefiles/eurostat/countries_ln.prj @@ -0,0 +1 @@ +GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]] \ No newline at end of file diff --git a/test/TestData/TestShapefiles/eurostat/countries_ln.shp b/test/TestData/TestShapefiles/eurostat/countries_ln.shp new file mode 100644 index 0000000..2b67b78 Binary files /dev/null and b/test/TestData/TestShapefiles/eurostat/countries_ln.shp differ diff --git a/test/TestData/TestShapefiles/eurostat/countries_ln.shx b/test/TestData/TestShapefiles/eurostat/countries_ln.shx new file mode 100644 index 0000000..6a77780 Binary files /dev/null and b/test/TestData/TestShapefiles/eurostat/countries_ln.shx differ diff --git a/test/TestData/TestShapefiles/eurostat/countries_pg.cpg b/test/TestData/TestShapefiles/eurostat/countries_pg.cpg new file mode 100644 index 0000000..3ad133c --- /dev/null +++ b/test/TestData/TestShapefiles/eurostat/countries_pg.cpg @@ -0,0 +1 @@ +UTF-8 \ No newline at end of file diff --git a/test/TestData/TestShapefiles/eurostat/countries_pg.dbf b/test/TestData/TestShapefiles/eurostat/countries_pg.dbf new file mode 100644 index 0000000..21a9bfe Binary files /dev/null and b/test/TestData/TestShapefiles/eurostat/countries_pg.dbf differ diff --git a/test/TestData/TestShapefiles/eurostat/countries_pg.prj b/test/TestData/TestShapefiles/eurostat/countries_pg.prj new file mode 100644 index 0000000..f45cbad --- /dev/null +++ b/test/TestData/TestShapefiles/eurostat/countries_pg.prj @@ -0,0 +1 @@ +GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]] \ No newline at end of file diff --git a/test/TestData/TestShapefiles/eurostat/countries_pg.shp b/test/TestData/TestShapefiles/eurostat/countries_pg.shp new file mode 100644 index 0000000..c3ae50f Binary files /dev/null and b/test/TestData/TestShapefiles/eurostat/countries_pg.shp differ diff --git a/test/TestData/TestShapefiles/eurostat/countries_pg.shx b/test/TestData/TestShapefiles/eurostat/countries_pg.shx new file mode 100644 index 0000000..5ecbb1b Binary files /dev/null and b/test/TestData/TestShapefiles/eurostat/countries_pg.shx differ diff --git a/test/TestData/TestShapefiles/eurostat/countries_pt.cpg b/test/TestData/TestShapefiles/eurostat/countries_pt.cpg new file mode 100644 index 0000000..3ad133c --- /dev/null +++ b/test/TestData/TestShapefiles/eurostat/countries_pt.cpg @@ -0,0 +1 @@ +UTF-8 \ No newline at end of file diff --git a/test/TestData/TestShapefiles/eurostat/countries_pt.dbf b/test/TestData/TestShapefiles/eurostat/countries_pt.dbf new file mode 100644 index 0000000..c208295 Binary files /dev/null and b/test/TestData/TestShapefiles/eurostat/countries_pt.dbf differ diff --git a/test/TestData/TestShapefiles/eurostat/countries_pt.prj b/test/TestData/TestShapefiles/eurostat/countries_pt.prj new file mode 100644 index 0000000..f45cbad --- /dev/null +++ b/test/TestData/TestShapefiles/eurostat/countries_pt.prj @@ -0,0 +1 @@ +GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]] \ No newline at end of file diff --git a/test/TestData/TestShapefiles/eurostat/countries_pt.shp b/test/TestData/TestShapefiles/eurostat/countries_pt.shp new file mode 100644 index 0000000..151e183 Binary files /dev/null and b/test/TestData/TestShapefiles/eurostat/countries_pt.shp differ diff --git a/test/TestData/TestShapefiles/eurostat/countries_pt.shx b/test/TestData/TestShapefiles/eurostat/countries_pt.shx new file mode 100644 index 0000000..cc69a97 Binary files /dev/null and b/test/TestData/TestShapefiles/eurostat/countries_pt.shx differ diff --git a/test/TestData/TestShapefiles/fields_utf8.CPG b/test/TestData/TestShapefiles/fields_utf8.CPG new file mode 100644 index 0000000..3ad133c --- /dev/null +++ b/test/TestData/TestShapefiles/fields_utf8.CPG @@ -0,0 +1 @@ +UTF-8 \ No newline at end of file diff --git a/test/TestData/TestShapefiles/fields_utf8.dbf b/test/TestData/TestShapefiles/fields_utf8.dbf new file mode 100644 index 0000000..7ed225e Binary files /dev/null and b/test/TestData/TestShapefiles/fields_utf8.dbf differ diff --git a/test/TestData/TestShapefiles/fields_utf8.sbn b/test/TestData/TestShapefiles/fields_utf8.sbn new file mode 100644 index 0000000..a3b358a Binary files /dev/null and b/test/TestData/TestShapefiles/fields_utf8.sbn differ diff --git a/test/TestData/TestShapefiles/fields_utf8.sbx b/test/TestData/TestShapefiles/fields_utf8.sbx new file mode 100644 index 0000000..dd1c950 Binary files /dev/null and b/test/TestData/TestShapefiles/fields_utf8.sbx differ diff --git a/test/TestData/TestShapefiles/fields_utf8.shp b/test/TestData/TestShapefiles/fields_utf8.shp new file mode 100644 index 0000000..d1a1019 Binary files /dev/null and b/test/TestData/TestShapefiles/fields_utf8.shp differ diff --git a/test/TestData/TestShapefiles/fields_utf8.shx b/test/TestData/TestShapefiles/fields_utf8.shx new file mode 100644 index 0000000..c93da43 Binary files /dev/null and b/test/TestData/TestShapefiles/fields_utf8.shx differ diff --git a/test/TestData/TestShapefiles/line_ed50_geo.dbf b/test/TestData/TestShapefiles/line_ed50_geo.dbf new file mode 100644 index 0000000..b9d933a Binary files /dev/null and b/test/TestData/TestShapefiles/line_ed50_geo.dbf differ diff --git a/test/TestData/TestShapefiles/line_ed50_geo.shp b/test/TestData/TestShapefiles/line_ed50_geo.shp new file mode 100644 index 0000000..7b2277f Binary files /dev/null and b/test/TestData/TestShapefiles/line_ed50_geo.shp differ diff --git a/test/TestData/TestShapefiles/line_ed50_utm36.shp b/test/TestData/TestShapefiles/line_ed50_utm36.shp new file mode 100644 index 0000000..64bd7b7 Binary files /dev/null and b/test/TestData/TestShapefiles/line_ed50_utm36.shp differ diff --git a/test/TestData/TestShapefiles/line_wgs84_geo.shp b/test/TestData/TestShapefiles/line_wgs84_geo.shp new file mode 100644 index 0000000..e9ae664 Binary files /dev/null and b/test/TestData/TestShapefiles/line_wgs84_geo.shp differ diff --git a/test/TestData/TestShapefiles/nested_polygons.dbf b/test/TestData/TestShapefiles/nested_polygons.dbf new file mode 100644 index 0000000..d9c9376 Binary files /dev/null and b/test/TestData/TestShapefiles/nested_polygons.dbf differ diff --git a/test/TestData/TestShapefiles/nested_polygons.shp b/test/TestData/TestShapefiles/nested_polygons.shp new file mode 100644 index 0000000..9f06c9e Binary files /dev/null and b/test/TestData/TestShapefiles/nested_polygons.shp differ diff --git a/test/TestData/TestShapefiles/nested_polygons.shx b/test/TestData/TestShapefiles/nested_polygons.shx new file mode 100644 index 0000000..20580ec Binary files /dev/null and b/test/TestData/TestShapefiles/nested_polygons.shx differ diff --git a/test/TestData/TestShapefiles/point_ed50_geo.dbf b/test/TestData/TestShapefiles/point_ed50_geo.dbf new file mode 100644 index 0000000..68544c9 Binary files /dev/null and b/test/TestData/TestShapefiles/point_ed50_geo.dbf differ diff --git a/test/TestData/TestShapefiles/point_ed50_geo.shp b/test/TestData/TestShapefiles/point_ed50_geo.shp new file mode 100644 index 0000000..880e1d8 Binary files /dev/null and b/test/TestData/TestShapefiles/point_ed50_geo.shp differ diff --git a/test/TestData/TestShapefiles/polygon intersecting line.shp b/test/TestData/TestShapefiles/polygon intersecting line.shp new file mode 100644 index 0000000..c5d151a Binary files /dev/null and b/test/TestData/TestShapefiles/polygon intersecting line.shp differ diff --git a/test/TestData/TestShapefiles/polygon_ed50_geo.shp b/test/TestData/TestShapefiles/polygon_ed50_geo.shp new file mode 100644 index 0000000..bd9fc50 Binary files /dev/null and b/test/TestData/TestShapefiles/polygon_ed50_geo.shp differ diff --git a/test/TestData/TestShapefiles/polygon_wgs84_geo.shp b/test/TestData/TestShapefiles/polygon_wgs84_geo.shp new file mode 100644 index 0000000..f0016b3 Binary files /dev/null and b/test/TestData/TestShapefiles/polygon_wgs84_geo.shp differ diff --git a/test/TestData/TestShapefiles/shape_PointZM.shp b/test/TestData/TestShapefiles/shape_PointZM.shp new file mode 100644 index 0000000..cad9f0c Binary files /dev/null and b/test/TestData/TestShapefiles/shape_PointZM.shp differ diff --git a/test/TestData/TestShapefiles/shape_pointM.shp b/test/TestData/TestShapefiles/shape_pointM.shp new file mode 100644 index 0000000..43159e9 Binary files /dev/null and b/test/TestData/TestShapefiles/shape_pointM.shp differ diff --git a/test/TestData/TestShapefiles/shape_pointZM_MissingM values.shp b/test/TestData/TestShapefiles/shape_pointZM_MissingM values.shp new file mode 100644 index 0000000..70ae570 Binary files /dev/null and b/test/TestData/TestShapefiles/shape_pointZM_MissingM values.shp differ diff --git a/test/TestData/TestShapefiles/tnp_pol.shp b/test/TestData/TestShapefiles/tnp_pol.shp new file mode 100644 index 0000000..d30ddc9 Binary files /dev/null and b/test/TestData/TestShapefiles/tnp_pol.shp differ diff --git a/test/TestData/TestShapefiles/with_M.dbf b/test/TestData/TestShapefiles/with_M.dbf new file mode 100644 index 0000000..9e3da36 Binary files /dev/null and b/test/TestData/TestShapefiles/with_M.dbf differ diff --git a/test/TestData/TestShapefiles/with_M.shp b/test/TestData/TestShapefiles/with_M.shp new file mode 100644 index 0000000..4e68495 Binary files /dev/null and b/test/TestData/TestShapefiles/with_M.shp differ