From d27c69073d02a9931bd17f56987969b7832b4ed7 Mon Sep 17 00:00:00 2001 From: Mahdi Rastegari Koopaei Date: Tue, 2 Jul 2024 10:46:44 +0100 Subject: [PATCH 1/2] Issue #51 Fix the issue with the column length mismatch: the 'D' column should be 8 characters long, and the 'L' column should be 1 character long. --- .../Dbf/DbfStreamExtensions.cs | 4 +- .../Dbf/Fields/DbfDateField.cs | 7 +-- .../Dbf/Fields/DbfLogicalField.cs | 9 ++-- .../Issues/Issue051.cs | 46 ++++++++++++++++++ .../#51-columnLengthMissmatch.dbf | Bin 0 -> 594 bytes 5 files changed, 57 insertions(+), 9 deletions(-) create mode 100644 test/NetTopologySuite.IO.Esri.Test/Issues/Issue051.cs create mode 100644 test/TestData/TestShapefiles/#51-columnLengthMissmatch.dbf diff --git a/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/DbfStreamExtensions.cs b/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/DbfStreamExtensions.cs index bdb6ebd..c444268 100644 --- a/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/DbfStreamExtensions.cs +++ b/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/DbfStreamExtensions.cs @@ -115,7 +115,7 @@ public static DbfField ReadDbaseFieldDescriptor(this Stream stream, Encoding enc } else if (type == DbfType.Date) { - return new DbfDateField(name); + return new DbfDateField(name, length); } else if (type == DbfType.Numeric) { @@ -127,7 +127,7 @@ public static DbfField ReadDbaseFieldDescriptor(this Stream stream, Encoding enc } else if (type == DbfType.Logical) { - return new DbfLogicalField(name); + return new DbfLogicalField(name, length); } else { diff --git a/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfDateField.cs b/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfDateField.cs index 3f4dd03..9e6aacf 100644 --- a/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfDateField.cs +++ b/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfDateField.cs @@ -11,15 +11,16 @@ namespace NetTopologySuite.IO.Esri.Dbf.Fields /// public class DbfDateField : DbfField { - private static readonly int FieldLength = 8; // This width is fixed and cannot be changed + private const int FieldLength = 8; // This width is fixed and cannot be changed private static readonly string DateFormat = "yyyyMMdd"; /// /// Initializes a new instance of the field class. /// /// Field name. - public DbfDateField(string name) - : base(name, DbfType.Date, FieldLength, 0) + /// Field length. + public DbfDateField(string name, int length = FieldLength) + : base(name, DbfType.Date, length, 0) { } diff --git a/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfLogicalField.cs b/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfLogicalField.cs index 36c9610..7883c21 100644 --- a/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfLogicalField.cs +++ b/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfLogicalField.cs @@ -12,7 +12,7 @@ public class DbfLogicalField : DbfField private readonly static byte DefaultValue = (byte)' '; // (byte)'?'; Initialized to 0x20 (space) otherwise T or F (http://www.dbase.com/KnowledgeBase/int/db7_file_fmt.htm) private readonly static byte TrueValue = (byte)'T'; private readonly static byte FalseValue = (byte)'F'; - private readonly static int FieldLength = 1; // This width is fixed and cannot be changed + private const int FieldLength = 1; // This width is fixed and cannot be changed private readonly static string TrueValues = "TtYy"; private readonly static string FalseValues = "FfNn"; @@ -22,8 +22,9 @@ public class DbfLogicalField : DbfField /// Initializes a new instance of the field class. /// /// Field name. - public DbfLogicalField(string name) - : base(name, DbfType.Logical, FieldLength, 0) + /// Field length. + public DbfLogicalField(string name, int length = FieldLength) + : base(name, DbfType.Logical, length, 0) { } @@ -45,7 +46,7 @@ public override object Value internal override void ReadValue(Stream stream) { var logicalValue = stream.ReadByteChar(); - + stream.Seek(stream.Position + Length - 1, SeekOrigin.Begin); if (TrueValues.Contains(logicalValue)) { LogicalValue = true; diff --git a/test/NetTopologySuite.IO.Esri.Test/Issues/Issue051.cs b/test/NetTopologySuite.IO.Esri.Test/Issues/Issue051.cs new file mode 100644 index 0000000..25da8ab --- /dev/null +++ b/test/NetTopologySuite.IO.Esri.Test/Issues/Issue051.cs @@ -0,0 +1,46 @@ +using System.Collections.Generic; +using System.Linq; +using NetTopologySuite.Features; +using NetTopologySuite.IO.Esri.Dbf; +using NUnit.Framework; + +namespace NetTopologySuite.IO.Esri.Test.Issues +{ + /// + /// https://github.com/NetTopologySuite/NetTopologySuite.IO.Esri/issues/51 + /// + internal class Issue051 + { + [Test] + public void Read_When_ColumnLengthMissmatch() + { + var shpPath = TestShapefiles.PathTo("#51-columnLengthMissmatch.dbf"); + List attributes = null; + Assert.DoesNotThrow(() => + { + var dbfReader = new DbfReader(shpPath); + attributes = dbfReader.ToList(); + }); + + Assert.NotNull(attributes); + + //First row is null and empty + Assert.AreEqual(string.Empty, attributes[0]["RoadLinkId"]); + Assert.AreEqual(null, attributes[0]["Tunnel"]); + Assert.AreEqual(null, attributes[0]["Bridge"]); + Assert.AreEqual(null, attributes[0]["CarAccess"]); + Assert.AreEqual(null, attributes[0]["Walk"]); + Assert.AreEqual(null, attributes[0]["MF_4_7"]); + Assert.AreEqual(null, attributes[0]["COREID"]); + + //Second row has value but length is missmatch in each column + Assert.AreEqual("osgb4000000080908489", attributes[1]["RoadLinkId"]); + Assert.AreEqual(true, attributes[1]["Tunnel"]); + Assert.AreEqual(false, attributes[1]["Bridge"]); + Assert.AreEqual(true, attributes[1]["CarAccess"]); + Assert.AreEqual(4.8, attributes[1]["Walk"]); + Assert.AreEqual(50, attributes[1]["MF_4_7"]); + Assert.AreEqual(55483, attributes[1]["COREID"]); + } + } +} diff --git a/test/TestData/TestShapefiles/#51-columnLengthMissmatch.dbf b/test/TestData/TestShapefiles/#51-columnLengthMissmatch.dbf new file mode 100644 index 0000000000000000000000000000000000000000..46e80860feff980dbb5218a4c664c587a8e62d6a GIT binary patch literal 594 zcmZRsVP|AwU|?WmTmd98K~R2Tice-;US4VrSkMPV@}S8(6=kNR zr{b1(PAqavPEIW@W?;aoKRhuf8)~E*h~@z~776&e#hb*Ng9Wh4JNpN@db)t+{Xir; zn28{G73cx%i2z#ofkJ+9dXk9&1XvhY8d#WESW?^VAxH+gA+eFgVZtVQ76wqZsezsW MkZo#eVqq)=0B8D0cK`qY literal 0 HcmV?d00001 From 752b7b96cdc9583391ec009a3b364a6976fe7926 Mon Sep 17 00:00:00 2001 From: Mahdi Rastegari Koopaei Date: Sun, 28 Jul 2024 14:28:14 +0100 Subject: [PATCH 2/2] Consider more scenario in reading logical values in DBF. --- .../Dbf/Fields/DbfLogicalField.cs | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfLogicalField.cs b/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfLogicalField.cs index 7883c21..5e5f752 100644 --- a/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfLogicalField.cs +++ b/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfLogicalField.cs @@ -1,6 +1,7 @@ using System; using System.IO; using System.Linq; +using System.Text; namespace NetTopologySuite.IO.Esri.Dbf.Fields { @@ -45,13 +46,22 @@ public override object Value internal override void ReadValue(Stream stream) { - var logicalValue = stream.ReadByteChar(); - stream.Seek(stream.Position + Length - 1, SeekOrigin.Begin); - if (TrueValues.Contains(logicalValue)) + // Logic column should have a Length of 1. However, some data providers produce shapfiles with a length greater than 1. + // Handle also those not specification compliant cases. + var logicalValueString = stream.ReadString(Length, Encoding.ASCII)?.Trim(); + if (string.IsNullOrEmpty(logicalValueString)) + { + LogicalValue = null; + return; + } + + var logicalValueChar = logicalValueString[0]; + + if (TrueValues.Contains(logicalValueChar)) { LogicalValue = true; } - else if (FalseValues.Contains(logicalValue)) + else if (FalseValues.Contains(logicalValueChar)) { LogicalValue = false; } @@ -79,7 +89,4 @@ internal override void WriteValue(Stream stream) } } - - - }