Skip to content

Commit c4cca6c

Browse files
author
Diego Guidi
committed
handled "null bounds" error when creating an empty shapefile
1 parent 105db26 commit c4cca6c

File tree

3 files changed

+81
-48
lines changed

3 files changed

+81
-48
lines changed

.gitignore

+4-1
Original file line numberDiff line numberDiff line change
@@ -188,4 +188,7 @@ FakesAssemblies/
188188
# Data generated from unit tests
189189
NetTopologySuite.Samples.Shapefiles/ZMtest.shp
190190
NetTopologySuite.Samples.Shapefiles/ZMtest.shx
191-
NetTopologySuite.Samples.Shapefiles/ZMtest.dbf
191+
NetTopologySuite.Samples.Shapefiles/ZMtest.dbf
192+
NetTopologySuite.Samples.Shapefiles/__empty.dbf
193+
NetTopologySuite.Samples.Shapefiles/__empty.shp
194+
NetTopologySuite.Samples.Shapefiles/__empty.shx

NetTopologySuite.IO/NetTopologySuite.IO.GeoTools/ShapefileWriter.cs

+8-3
Original file line numberDiff line numberDiff line change
@@ -222,18 +222,23 @@ private static void WriteNullShapeRecord(BigEndianBinaryWriter shpBinaryWriter,
222222
/*return recordLength;*/
223223
}
224224

225+
private Envelope NotNull(Envelope bounds)
226+
{
227+
return bounds ?? new Envelope();
228+
}
229+
225230
private void WriteShxHeader(BigEndianBinaryWriter shxBinaryWriter, int shxLength, Envelope bounds)
226231
{
227232
// write the .shx header
228-
var shxHeader = new ShapefileHeader { FileLength = shxLength, Bounds = bounds, ShapeType = _geometryType };
233+
var shxHeader = new ShapefileHeader { FileLength = shxLength, Bounds = NotNull(bounds), ShapeType = _geometryType };
229234

230235
// assumes Geometry type of the first item will the same for all other items in the collection.
231236
shxHeader.Write(shxBinaryWriter);
232237
}
233238

234-
private void WriteShpHeader(BigEndianBinaryWriter shpBinaryWriter, int shpLength, Envelope bounds)
239+
private void WriteShpHeader(BigEndianBinaryWriter shpBinaryWriter, int shpLength, Envelope bounds)
235240
{
236-
var shpHeader = new ShapefileHeader {FileLength = shpLength, Bounds = bounds, ShapeType = _geometryType};
241+
var shpHeader = new ShapefileHeader { FileLength = shpLength, Bounds = NotNull(bounds), ShapeType = _geometryType };
237242

238243
// assumes Geometry type of the first item will the same for all other items
239244
// in the collection.

NetTopologySuite.Samples.Console/Tests/IO/ShapefileWriteTest.cs

+69-44
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22
using System.Collections.ObjectModel;
33
using System.IO;
44
using GeoAPI.Geometries;
5+
using NetTopologySuite.Algorithm.Match;
56
using NetTopologySuite.Features;
67
using NetTopologySuite.Geometries;
8+
using NetTopologySuite.Geometries.Implementation;
79
using NetTopologySuite.IO;
810
using NetTopologySuite.Samples.SimpleTests;
911
using NUnit.Framework;
@@ -16,11 +18,34 @@ public class ShapeFileDataWriterTest : BaseSamples
1618
public ShapeFileDataWriterTest()
1719
{
1820
// Set current dir to shapefiles dir
19-
Environment.CurrentDirectory = Path.Combine(
20-
AppDomain.CurrentDomain.BaseDirectory,
21+
Environment.CurrentDirectory = Path.Combine(
22+
AppDomain.CurrentDomain.BaseDirectory,
2123
string.Format("..{0}..{0}..{0}NetTopologySuite.Samples.Shapefiles", Path.DirectorySeparatorChar));
2224
}
2325

26+
[Test]
27+
public void TestCreateEmptyShapefile()
28+
{
29+
const string filename = "__empty";
30+
const string emptyShp = filename + ".shp";
31+
const string emptyShx = filename + ".shx";
32+
const string emptyDbf = filename + ".dbf";
33+
if (File.Exists(emptyShp))
34+
File.Delete(emptyShp);
35+
if (File.Exists(emptyShx))
36+
File.Delete(emptyShx);
37+
if (File.Exists(emptyDbf))
38+
File.Delete(emptyDbf);
39+
40+
ShapefileDataWriter writer = new ShapefileDataWriter(filename, Factory);
41+
writer.Header = new DbaseFileHeader();
42+
writer.Write(new IFeature[0]);
43+
44+
Assert.That(File.Exists(emptyShp), Is.True);
45+
Assert.That(File.Exists(emptyShx), Is.True);
46+
Assert.That(File.Exists(emptyDbf), Is.True);
47+
}
48+
2449
[Test]
2550
public void TestWriteZValuesShapeFile()
2651
{
@@ -39,14 +64,14 @@ private void TestWriteZMValuesShapeFile(bool testM)
3964
points[1] = new Coordinate(1, 0);
4065
points[2] = new Coordinate(1, 1);
4166

42-
var csFactory = NetTopologySuite.Geometries.Implementation.DotSpatialAffineCoordinateSequenceFactory.Instance;
67+
var csFactory = DotSpatialAffineCoordinateSequenceFactory.Instance;
4368
var sequence = csFactory.Create(3, Ordinates.XYZM);
44-
for (var i = 0; i < 3; i ++)
69+
for (var i = 0; i < 3; i++)
4570
{
4671
sequence.SetOrdinate(i, Ordinate.X, points[i].X);
4772
sequence.SetOrdinate(i, Ordinate.Y, points[i].Y);
4873
sequence.SetOrdinate(i, Ordinate.Z, 1 + i);
49-
if (testM)
74+
if (testM)
5075
sequence.SetOrdinate(i, Ordinate.M, 11 + i);
5176
}
5277
var lineString = Factory.CreateLineString(sequence);
@@ -65,7 +90,7 @@ private void TestWriteZMValuesShapeFile(bool testM)
6590
shpWriter.Write(features);
6691

6792
// Now let's read the file and verify that we got Z and M back
68-
var factory = new GeometryFactory(NetTopologySuite.Geometries.Implementation.DotSpatialAffineCoordinateSequenceFactory.Instance);
93+
var factory = new GeometryFactory(DotSpatialAffineCoordinateSequenceFactory.Instance);
6994

7095
using (var reader = new ShapefileDataReader("ZMtest", factory))
7196
{
@@ -75,12 +100,12 @@ private void TestWriteZMValuesShapeFile(bool testM)
75100
for (var i = 0; i < 3; i++)
76101
{
77102
var c = geom.Coordinates[i];
78-
Assert.AreEqual(i + 1, c.Z);
103+
Assert.AreEqual(i + 1, c.Z);
79104
}
80105

81106
if (testM)
82107
{
83-
sequence = ((ILineString) geom).CoordinateSequence;
108+
sequence = ((ILineString)geom).CoordinateSequence;
84109
for (var i = 0; i < 3; i++)
85110
{
86111
Assert.AreEqual(sequence.GetOrdinate(i, Ordinate.M), 11 + i);
@@ -91,7 +116,7 @@ private void TestWriteZMValuesShapeFile(bool testM)
91116
var v = reader.GetString(0);
92117
Assert.AreEqual(v, "Trond");
93118
}
94-
}
119+
}
95120

96121
[Test]
97122
public void TestWriteSimpleShapeFile()
@@ -101,7 +126,7 @@ public void TestWriteSimpleShapeFile()
101126

102127
var coll = new GeometryCollection(new IGeometry[] { p1, p2, });
103128
ShapefileWriter.WriteGeometryCollection(@"test_arcview", coll);
104-
129+
105130
// Not read by ArcView!!!
106131
}
107132

@@ -191,9 +216,9 @@ private static void DoTest(IGeometryCollection geomsWrite, Ordinates ordinates,
191216
}
192217
else if (!gw.EqualsExact(gr))
193218
{
194-
var hsm = new Algorithm.Match.HausdorffSimilarityMeasure().Measure(gw, gr);
195-
var asm = new Algorithm.Match.AreaSimilarityMeasure().Measure(gw, gr);
196-
var smc = Algorithm.Match.SimilarityMeasureCombiner.Combine(hsm, asm);
219+
var hsm = new HausdorffSimilarityMeasure().Measure(gw, gr);
220+
var asm = new AreaSimilarityMeasure().Measure(gw, gr);
221+
var smc = SimilarityMeasureCombiner.Combine(hsm, asm);
197222
if (!gw.EqualsNormalized(gr) || (1d - smc) > 1e-7)
198223
{
199224
Console.WriteLine(string.Format("Geometries don't match at index {0}", i));
@@ -254,7 +279,7 @@ private static bool ArraysEqual(double[] writeZ, double[] readZ)
254279

255280
for (var i = 0; i < writeZ.Length; i++)
256281
if (Math.Abs(writeZ[i] - readZ[i]) > 1E-7) return false;
257-
282+
258283
return true;
259284
}
260285

@@ -263,8 +288,8 @@ private static class ShapeFileShapeFactory
263288
public static IGeometryCollection CreateShapes(OgcGeometryType type, Ordinates ordinates, int number = 50)
264289
{
265290
var empty = new bool[number];
266-
empty[Rnd.Next(2, number/2)] = true;
267-
empty[Rnd.Next(number/2, number)] = true;
291+
empty[Rnd.Next(2, number / 2)] = true;
292+
empty[Rnd.Next(number / 2, number)] = true;
268293

269294
var result = new IGeometry[number];
270295
for (var i = 0; i < number; i++)
@@ -300,14 +325,14 @@ public static IGeometryCollection CreateShapes(OgcGeometryType type, Ordinates o
300325

301326
return Factory.CreateGeometryCollection(result);
302327
}
303-
328+
304329
private static readonly Random Rnd = new Random(9936528);
305330

306331
private static readonly ICoordinateSequenceFactory CsFactory =
307-
NetTopologySuite.Geometries.Implementation.DotSpatialAffineCoordinateSequenceFactory.Instance;
332+
DotSpatialAffineCoordinateSequenceFactory.Instance;
308333

309334
public static readonly IGeometryFactory FactoryRead = new GeometryFactory(new PrecisionModel(PrecisionModels.Floating), 4326, CsFactory);
310-
335+
311336
public static readonly IGeometryFactory Factory = new GeometryFactory(new PrecisionModel(1000), 4326, CsFactory);
312337

313338
private static IGeometry CreatePoint(Ordinates ordinates, bool empty)
@@ -334,7 +359,7 @@ private static IGeometry CreateMultiPoint(Ordinates ordinates, bool empty)
334359
for (var i = 0; i < numPoints; i++)
335360
foreach (var o in OrdinatesUtility.ToOrdinateArray(ordinates))
336361
seq.SetOrdinate(i, o, RandomOrdinate(o, Factory.PrecisionModel));
337-
362+
338363
return Factory.CreateMultiPoint(seq);
339364
}
340365

@@ -371,7 +396,7 @@ private static IGeometry CreateMultiLineString(Ordinates ordinates, bool empty)
371396
{
372397
Factory.CreateMultiLineString(null);
373398
}
374-
399+
375400
var numLineStrings = Rnd.Next(0, 11);
376401
if (numLineStrings <= 2)
377402
numLineStrings = 0;
@@ -407,45 +432,45 @@ private static IGeometry CreatePolygon(Ordinates ordinates, bool empty, int next
407432
var ring = CreateCircleRing(ordinates, x, y, 3 * Rnd.NextDouble());
408433
return Factory.CreatePolygon(ring, null);
409434
case 1: // rectangle
410-
ring = CreateRectangleRing(ordinates, x, y, 6*Rnd.NextDouble(), 3*Rnd.NextDouble());
435+
ring = CreateRectangleRing(ordinates, x, y, 6 * Rnd.NextDouble(), 3 * Rnd.NextDouble());
411436
return Factory.CreatePolygon(ring, null);
412437
case 2: // cirle with hole
413-
var radius = 3*Rnd.NextDouble();
438+
var radius = 3 * Rnd.NextDouble();
414439
var shell = CreateCircleRing(ordinates, x, y, radius);
415440
var hole = CreateCircleRing(ordinates, x, y, 0.66 * radius, true);
416-
return Factory.CreatePolygon(shell, new [] { hole });
441+
return Factory.CreatePolygon(shell, new[] { hole });
417442
case 3: // rectanglee with hole
418-
var width = 6*Rnd.NextDouble();
419-
var height = 3*Rnd.NextDouble();
443+
var width = 6 * Rnd.NextDouble();
444+
var height = 3 * Rnd.NextDouble();
420445
shell = CreateRectangleRing(ordinates, x, y, width, height);
421446
hole = CreateRectangleRing(ordinates, x, y, 0.66 * width, 0.66 * height, true);
422-
return Factory.CreatePolygon(shell, new [] { hole });
447+
return Factory.CreatePolygon(shell, new[] { hole });
423448
case 4: // rectanglee with hole
424-
width = 6*Rnd.NextDouble();
425-
height = 3*Rnd.NextDouble();
449+
width = 6 * Rnd.NextDouble();
450+
height = 3 * Rnd.NextDouble();
426451
shell = CreateRectangleRing(ordinates, x, y, width, height);
427452
hole = CreateCircleRing(ordinates, x, y, 0.33 * Math.Min(width, height), true);
428-
return Factory.CreatePolygon(shell, new [] { hole });
453+
return Factory.CreatePolygon(shell, new[] { hole });
429454
default:
430455
throw new NotSupportedException();
431456
}
432457
}
433458

434459
private static ILinearRing CreateCircleRing(Ordinates ordinates, double x, double y, double radius, bool reverse = false)
435460
{
436-
var seq = CsFactory.Create(4*12 + 1, ordinates);
461+
var seq = CsFactory.Create(4 * 12 + 1, ordinates);
437462
var angle = Math.PI * 2;
438-
const double quandrantStep = Math.PI/2d/12d;
463+
const double quandrantStep = Math.PI / 2d / 12d;
439464
var k = 0;
440465
for (var i = 0; i < 4; i++)
441466
{
442467
for (var j = 0; j < 12; j++)
443468
{
444-
var dx = radius*Math.Cos(angle);
445-
var dy = radius*Math.Sin(angle);
469+
var dx = radius * Math.Cos(angle);
470+
var dy = radius * Math.Sin(angle);
446471
seq.SetOrdinate(k, Ordinate.X, Factory.PrecisionModel.MakePrecise(x + dx));
447472
seq.SetOrdinate(k, Ordinate.Y, Factory.PrecisionModel.MakePrecise(y + dy));
448-
if ((ordinates & Ordinates.Z)==Ordinates.Z)
473+
if ((ordinates & Ordinates.Z) == Ordinates.Z)
449474
seq.SetOrdinate(k, Ordinate.Z, RandomOrdinate(Ordinate.Z, Factory.PrecisionModel));
450475
if ((ordinates & Ordinates.Z) == Ordinates.Z)
451476
seq.SetOrdinate(k, Ordinate.M, RandomOrdinate(Ordinate.M, Factory.PrecisionModel));
@@ -465,8 +490,8 @@ private static ILinearRing CreateCircleRing(Ordinates ordinates, double x, doubl
465490

466491
private static ILinearRing CreateRectangleRing(Ordinates ordinates, double x, double y, double width, double height, bool reverse = false)
467492
{
468-
var dx = Factory.PrecisionModel.MakePrecise(width /2);
469-
var dy = Factory.PrecisionModel.MakePrecise(height /2);
493+
var dx = Factory.PrecisionModel.MakePrecise(width / 2);
494+
var dy = Factory.PrecisionModel.MakePrecise(height / 2);
470495

471496
var seq = CsFactory.Create(5, ordinates);
472497

@@ -499,14 +524,14 @@ private static ILinearRing CreateRectangleRing(Ordinates ordinates, double x, do
499524

500525
return Factory.CreateLinearRing(reverse ? seq.Reversed() : seq);
501526
}
502-
527+
503528
private static IGeometry CreateMultiPolygon(Ordinates ordinates, bool empty)
504529
{
505530
if (empty)
506531
{
507532
Factory.CreateMultiPolygon(null);
508533
}
509-
534+
510535
switch (Rnd.Next(2))
511536
{
512537
case 0:
@@ -515,15 +540,15 @@ private static IGeometry CreateMultiPolygon(Ordinates ordinates, bool empty)
515540
for (var i = 0; i < numPolygons; i++)
516541
polygons[i] = (IPolygon)CreatePolygon(ordinates, false);
517542
return Factory.BuildGeometry(new Collection<IGeometry>(polygons)).Union();
518-
543+
519544
case 1:
520545
polygons = new IPolygon[2];
521-
var radius = 5*Rnd.NextDouble();
546+
var radius = 5 * Rnd.NextDouble();
522547
var x = RandomOrdinate(Ordinate.X, Factory.PrecisionModel);
523548
var y = RandomOrdinate(Ordinate.Y, Factory.PrecisionModel);
524549
var shell = CreateCircleRing(ordinates, x, y, radius);
525550
var hole = CreateCircleRing(ordinates, x, y, 0.66 * radius, true);
526-
polygons[0] = Factory.CreatePolygon(shell, new [] { hole });
551+
polygons[0] = Factory.CreatePolygon(shell, new[] { hole });
527552
shell = CreateCircleRing(ordinates, x, y, 0.5 * radius);
528553
hole = CreateCircleRing(ordinates, x, y, 0.15 * radius, true);
529554
polygons[1] = Factory.CreatePolygon(shell, new[] { hole });
@@ -539,7 +564,7 @@ private static double RandomOrdinate(Ordinate o, IPrecisionModel pm)
539564
switch (o)
540565
{
541566
case Ordinate.X:
542-
return pm.MakePrecise(-180 + 360*Rnd.NextDouble());
567+
return pm.MakePrecise(-180 + 360 * Rnd.NextDouble());
543568
case Ordinate.Y:
544569
return pm.MakePrecise(-90 + 180 * Rnd.NextDouble());
545570
case Ordinate.Z:
@@ -551,7 +576,7 @@ private static double RandomOrdinate(Ordinate o, IPrecisionModel pm)
551576
}
552577
}
553578
}
554-
579+
555580
[Test, ExpectedException(typeof(ArgumentException))]
556581
// see https://code.google.com/p/nettopologysuite/issues/detail?id=146
557582
public void Issue146_ShapeCreationWithInvalidAttributeName()

0 commit comments

Comments
 (0)