Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Test project from NetTopologySuite.IO.ShapeFile repository #7

Merged
merged 123 commits into from
Nov 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
123 commits
Select commit Hold shift + click to select a range
5dff6ef
Add Test project from NetTopologySuite.IO.ShapeFile repository (https…
KubaSzostak Jul 16, 2022
a25810e
Add EsriIssueNumberAttribute
KubaSzostak Jul 16, 2022
d556201
Add dedicated folders
KubaSzostak Jul 16, 2022
044c865
Rename namespaces
KubaSzostak Jul 16, 2022
6d3355b
Move tests to dedicated folders
KubaSzostak Jul 16, 2022
b90745e
Delete DbfDateTest
KubaSzostak Jul 16, 2022
8743423
Move DbfDateTest to Attributes folder
KubaSzostak Jul 16, 2022
3fdef7d
Exclude all files causing compile erros.
KubaSzostak Jul 16, 2022
21918e7
Rename CommonHelpers to TestShapefiles
KubaSzostak Jul 16, 2022
948412f
Add PathTo() TestShapefiles.
KubaSzostak Jul 16, 2022
ffc4b29
Merge pull request #4 from kubaszostak/test/nts.io.shapefile
KubaSzostak Jul 16, 2022
7baadab
Rename ShpReader.
KubaSzostak Jul 16, 2022
86e4edb
Add ReadAllGeometries() method.
KubaSzostak Jul 16, 2022
7a411e7
Move ReadAllGeometries() method to Shapefile class.
KubaSzostak Jul 16, 2022
56957f7
Move TestShapefiles to TestData folder
KubaSzostak Jul 16, 2022
055247a
Add Issue123Test.
KubaSzostak Jul 16, 2022
dd4203b
Do not read SHP records exceding DBF record count.
KubaSzostak Jul 16, 2022
dd7fa4a
Add Issue161.
KubaSzostak Jul 16, 2022
b20561a
Add LSOA_2011_EW_BGC.shp file.
KubaSzostak Jul 16, 2022
a6c13c0
Allow reading ShapeType by specifiyng shapefile name (without extensi…
KubaSzostak Jul 16, 2022
0ed14c1
Add Issue173Fixture.
KubaSzostak Jul 16, 2022
02d83e7
Add Issue174.
KubaSzostak Jul 16, 2022
dc2bc66
Throw ShapefileException instead of FileLoadException.
KubaSzostak Jul 16, 2022
cc1a7cf
Add Issue178Fixture.
KubaSzostak Jul 16, 2022
2caa320
Add BoundingBox property to ShapefileReader.
KubaSzostak Jul 16, 2022
2aa8c31
Add MBR Filter to ShapefileReader.
KubaSzostak Jul 16, 2022
e66b638
Add Issue27Fixture.
KubaSzostak Jul 16, 2022
01f1f37
Add Issue36Tests.
KubaSzostak Jul 16, 2022
bcceb3d
Add ShapefileWriterOptions.
KubaSzostak Jul 16, 2022
c3a7741
Add ShapefileReaderOptions.
KubaSzostak Jul 16, 2022
92298ec
Add Issue46Fixture.
KubaSzostak Jul 16, 2022
b308880
Allow omiting ShapefileReaderOptions.
KubaSzostak Jul 17, 2022
cb635e2
Add constructor based on ShapefileReader.
KubaSzostak Jul 17, 2022
bbdf382
Update TestConsole samples.
KubaSzostak Jul 17, 2022
93821df
Update readme samples.
KubaSzostak Jul 17, 2022
d7e1ba9
Add ReadmeSamples test.
KubaSzostak Jul 17, 2022
b9764c0
Add IsNull property.
KubaSzostak Jul 17, 2022
3ad8a89
Automatically update ID field (when needed).
KubaSzostak Jul 17, 2022
26f48d3
Add Issue4Fixture.
KubaSzostak Jul 17, 2022
6547040
Add DBF numeric fields based on CLR numeric types.
KubaSzostak Jul 17, 2022
f860994
Add Issue56Fixture.
KubaSzostak Jul 17, 2022
6030dd7
Add Issue60Fixture.
KubaSzostak Jul 17, 2022
8dae54f
Remove unessesary generic arguments.
KubaSzostak Jul 17, 2022
fac7e50
Add GetTempShpPath() and DeleteShp() helper methods.
KubaSzostak Jul 17, 2022
d97a96d
Update TestConsole samples.
KubaSzostak Jul 17, 2022
d10f988
Add link to dBASE format description.
KubaSzostak Jul 17, 2022
9e7921f
Update referenced files.
KubaSzostak Jul 17, 2022
f45e019
Set DbfWriter encoding as optional.
KubaSzostak Jul 21, 2022
40c2ff7
Add DbfFieldsExtensions.
KubaSzostak Jul 21, 2022
6ed7f1e
Add Issue64Fixture.
KubaSzostak Jul 21, 2022
1009d8e
Add Write(T geometry) method.
KubaSzostak Jul 21, 2022
0c28914
Add Issue74Fixture.
KubaSzostak Jul 21, 2022
39a2a0a
Change OpenRead() exception type to ShapefileException.
KubaSzostak Jul 21, 2022
691179b
Add Issue79Fixture.
KubaSzostak Jul 21, 2022
d3430b1
Remove unnecessaryfactory variable.
KubaSzostak Jul 21, 2022
66879a4
Add Issue27Fixture.
KubaSzostak Jul 21, 2022
867c00d
Update the test to use ShapefileWriterOptions.
KubaSzostak Jul 21, 2022
53224dc
Update included source files.
KubaSzostak Jul 21, 2022
47e5a38
Fix typo.
KubaSzostak Jul 23, 2022
01abc71
Add country shapefiles.
KubaSzostak Jul 23, 2022
d05cf28
Add plain Read() method.
KubaSzostak Jul 24, 2022
ca2a373
Add AttributesTest.
KubaSzostak Jul 24, 2022
d8688a2
Add DbfDateTest.
KubaSzostak Jul 24, 2022
7eb4702
Update Attributes tests references.
KubaSzostak Jul 24, 2022
e9a84d7
Add ShapeRead test.
KubaSzostak Jul 24, 2022
2cce17d
Add ToDictionary() and GetValues() methods.
KubaSzostak Jul 24, 2022
9bf36e9
Add null Attributes check.
KubaSzostak Jul 24, 2022
181f0a1
Adjust Shapefile type error message.
KubaSzostak Jul 24, 2022
06b9418
Handle invlaid CCW polygons.
KubaSzostak Jul 24, 2022
330f537
Fix ReadXYBoundingBox bug.
KubaSzostak Jul 24, 2022
5beedda
Add ShapeFileDataReaderTest.
KubaSzostak Jul 24, 2022
8c4e8b9
Update Readers tests references.
KubaSzostak Jul 24, 2022
235a9e0
Do not create Feature instanace at EOF.
KubaSzostak Jul 30, 2022
834107c
Add UseOfIndexAndPreparedGeometry test.
KubaSzostak Jul 30, 2022
23f1574
Allow null attributes as DBF fields source.
KubaSzostak Jul 30, 2022
bb75532
Add LineStringSamples test.
KubaSzostak Jul 30, 2022
b9d7a33
Fix MultiPoint Ordinates selector.
KubaSzostak Jul 30, 2022
c0a1efb
Add MultiPointSamples test.
KubaSzostak Jul 30, 2022
ed0a722
Add PointSamples test.
KubaSzostak Jul 31, 2022
b6f6171
Add PolygonSamples test.
KubaSzostak Jul 31, 2022
d8cd3bb
Update Geometries tests references.
KubaSzostak Jul 31, 2022
0e62436
Allow to set DbfField length and precision.
KubaSzostak Jul 31, 2022
9c08b25
Add ShapeFileEncodingTest.
KubaSzostak Jul 31, 2022
ff48187
Add ShapeFileInvalidHeaderTest.
KubaSzostak Jul 31, 2022
3ad7c86
Add support for numeric codepage in CPG file.
KubaSzostak Jul 31, 2022
78257a0
Add abstract Write() method.
KubaSzostak Jul 31, 2022
aa0224d
Add GetShapeType() helper method.
KubaSzostak Jul 31, 2022
4d98d0f
Add missing encoding codepages.
KubaSzostak Jul 31, 2022
19b5d18
Add ShapeFileDataWriterTest.
KubaSzostak Jul 31, 2022
08bb0c1
Add WKTReaderTests.
KubaSzostak Jul 31, 2022
5a14ca6
Add parameterless Read() method.
KubaSzostak Jul 31, 2022
e9b5e20
Add ByteStreamProviderFixture.
KubaSzostak Jul 31, 2022
0bd8d5b
Add ManagedStreamProviderFixture.
KubaSzostak Jul 31, 2022
a573803
Change IReadOnlyDictionary<> into IAttributesTable.
KubaSzostak Jul 31, 2022
044f20d
Add DbaseReaderTests.
KubaSzostak Jul 31, 2022
692da3b
Add MbrFilterOption.
KubaSzostak Oct 22, 2022
79aa884
Add ShapeDataReaderTests.
KubaSzostak Oct 22, 2022
7fa4616
Fix SHP RecordNumber after restarting enumerator.
KubaSzostak Oct 22, 2022
fddb4d5
Allow default ShpReader options.
KubaSzostak Oct 22, 2022
7fe1b47
Add ShapeReaderTests.
KubaSzostak Oct 22, 2022
64e3f58
Add ShapefileDataWriterTests.
KubaSzostak Oct 22, 2022
d8f5e89
Add CascadedPolygonUnionFixture test.
KubaSzostak Oct 22, 2022
20e468c
Add GraphBuilder2Test.
KubaSzostak Oct 23, 2022
a5ef385
Add NormalizeTest.
KubaSzostak Oct 23, 2022
6c2531b
Add NestedPolygonsTest.
KubaSzostak Oct 23, 2022
c1d39a1
Add nested polygons WKT.
KubaSzostak Oct 23, 2022
d395199
Add PathFinderTest.
KubaSzostak Oct 23, 2022
b7dffc2
Add nested_polygons.shp
KubaSzostak Oct 23, 2022
dbf520b
Add UnionAggregateTest.
KubaSzostak Oct 23, 2022
fe49a00
Update readme samples.
KubaSzostak Oct 23, 2022
8443cb7
Add ReadmeSamples tests.
KubaSzostak Oct 23, 2022
9efd502
Add GraphBuilder2Test.
KubaSzostak Oct 23, 2022
03e6fb6
Add OpenRead() to TempFileWriter.
KubaSzostak Oct 23, 2022
89ad7c6
Allow AssertPolygonsEqual() to compare MultiPolygon with Polygon.
KubaSzostak Oct 23, 2022
62daca8
Code cleanup.
KubaSzostak Oct 23, 2022
c94149b
Include missing test classes.
KubaSzostak Oct 23, 2022
84ebe57
Merge branch 'NetTopologySuite:test/nts.io.shapefile' into test/nts.i…
KubaSzostak Oct 23, 2022
1319476
Merge pull request #5 from kubaszostak/test/nts.io.shapefile
KubaSzostak Oct 23, 2022
d32bd67
Upgrade TestConsole app to .NET 6.
KubaSzostak Nov 3, 2022
5ca106c
Remove broken TestConsole app reference.
KubaSzostak Nov 5, 2022
dfd8dbd
Move all tests ported from NetTopologySuite.IO.ShapeFile repository i…
KubaSzostak Nov 5, 2022
afd3ea5
Add MultipolygonWithPolygonInHole test.
KubaSzostak Nov 5, 2022
7078b73
Add GeometryBuilderMode enum.
KubaSzostak Nov 6, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
17 changes: 16 additions & 1 deletion NetTopologySuite.IO.Esri.sln
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
50 changes: 22 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
```

Expand All @@ -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);
}
```

Expand All @@ -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]}");
Expand All @@ -74,24 +65,27 @@ foreach (var feature in Shapefile.ReadAllFeatures(shpPath))
var features = new List<Feature>();
for (int i = 1; i < 5; i++)
{
var lineCoords = new List<CoordinateZ>();
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<CoordinateZ>
{
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);
```

Expand Down
63 changes: 56 additions & 7 deletions src/NetTopologySuite.IO.Esri.Shapefile/Dbf/DbfReader.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using NetTopologySuite.Features;
using NetTopologySuite.IO.Esri.Dbf.Fields;
using System;
using System.Collections;
Expand All @@ -15,7 +16,7 @@ namespace NetTopologySuite.IO.Esri.Dbf
/// <summary>
/// Class that allows records in a dbase file to be enumerated.
/// </summary>
public class DbfReader : ManagedDisposable, IEnumerable<IReadOnlyDictionary<string, object>>
public class DbfReader : ManagedDisposable, IEnumerable<IAttributesTable>
{
private int HeaderSize;
private int CurrentIndex = 0;
Expand Down Expand Up @@ -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)
Expand All @@ -178,6 +183,31 @@ private string ReadFileText(string filePath, string fileExtension)
return File.ReadAllText(filePath).Trim();
}



/// <summary>
/// Reads field values from underlying stream and advances the enumerator to the next record.
/// </summary>
/// <returns>
/// true if the enumerator was successfully advanced to the next record;
/// false if the enumerator has passed the end of the table.
/// </returns>
public bool Read()
{
var readSucceeded = Read(out var deleted);
if (!readSucceeded)
{
return false;
}
if (deleted)
{
return Read();
}
return true;
}



/// <summary>
/// Reads field values from underlying stream and advances the enumerator to the next record.
/// </summary>
Expand Down Expand Up @@ -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.
/// </returns>
public bool Read(out IReadOnlyDictionary<string, object> 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;
}

/// <summary>
/// Moves enumerator to specified position and reads values from underlying stream.
/// </summary>
/// <param name="index">Thre record index.</param>
/// <returns>
/// Table containing record fields (attributes).
/// </returns>
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<IReadOnlyDictionary<string, object>> IEnumerable<IReadOnlyDictionary<string, object>>.GetEnumerator()
IEnumerator<IAttributesTable> IEnumerable<IAttributesTable>.GetEnumerator()
{
return new DbfEnumerator(this);
}
Expand All @@ -242,10 +291,10 @@ IEnumerator IEnumerable.GetEnumerator()
return new DbfEnumerator(this);
}

private class DbfEnumerator : IEnumerator<IReadOnlyDictionary<string, object>>
private class DbfEnumerator : IEnumerator<IAttributesTable>
{
private readonly DbfReader Owner;
public IReadOnlyDictionary<string, object> Current { get; private set; }
public IAttributesTable Current { get; private set; }
object IEnumerator.Current => Current;

public DbfEnumerator(DbfReader owner)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
55 changes: 46 additions & 9 deletions src/NetTopologySuite.IO.Esri.Shapefile/Dbf/DbfWriter.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -15,6 +16,8 @@ namespace NetTopologySuite.IO.Esri.Dbf
public class DbfWriter : ManagedDisposable
{
private Stream DbfStream;
private int Id = 1;
private DbfField IdField;

/// <summary>
/// Returns the fields in the dbase file.
Expand Down Expand Up @@ -46,8 +49,8 @@ public class DbfWriter : ManagedDisposable
/// </summary>
/// <param name="stream">Stream of source DBF file.</param>
/// <param name="fields">dBASE field definitions.</param>
/// <param name="encoding">DBF file encoding or null if encoding should be resolved from DBF reserved bytes.</param>
public DbfWriter(Stream stream, IReadOnlyList<DbfField> fields, Encoding encoding)
/// <param name="encoding">DBF file encoding. Defaults to UTF8.</param>
public DbfWriter(Stream stream, IReadOnlyList<DbfField> fields, Encoding encoding = null)
{
Encoding = encoding ?? Encoding.UTF8;
IntializeFields(fields);
Expand All @@ -60,8 +63,8 @@ public DbfWriter(Stream stream, IReadOnlyList<DbfField> fields, Encoding encodin
/// </summary>
/// <param name="dbfPath">Path to DBF file.</param>
/// <param name="fields">dBASE field definitions.</param>
/// <param name="encoding">DBF file encoding or null if encoding should be resolved from DBF reserved bytes.</param>
public DbfWriter(string dbfPath, IReadOnlyList<DbfField> fields, Encoding encoding)
/// <param name="encoding">DBF file encoding. Defaults to UTF8.</param>
public DbfWriter(string dbfPath, IReadOnlyList<DbfField> fields, Encoding encoding = null)
{
Encoding = encoding ?? Encoding.UTF8;
IntializeFields(fields);
Expand All @@ -80,20 +83,41 @@ public DbfWriter(string dbfPath, IReadOnlyList<DbfField> fields, Encoding encodi
private void IntializeFields(IReadOnlyList<DbfField> 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<DbfField>() { 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));
Expand Down Expand Up @@ -148,6 +172,7 @@ private void WriteCpgEncoding(string dbfPath, Encoding encoding)
/// </returns>
public void Write()
{
UpdateIdField();
DbfStream.WriteByte(Dbf.ValidRecordMark);

for (int i = 0; i < Fields.Count; i++)
Expand Down Expand Up @@ -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++;
}
}
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ public override object Value
set { StringValue = value?.ToString(); }
}

/// <inheritdoc/>
public override bool IsNull => StringValue == null;

internal override void ReadValue(Stream stream)
{
StringValue = stream.ReadString(Length, Encoding)?.TrimEnd();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ public override object Value
set { DateValue = (DateTime?)value; }
}

/// <inheritdoc/>
public override bool IsNull => DateValue == null;


internal override void ReadValue(Stream stream)
{
Expand Down