diff --git a/src/Microsoft.SqlServer.Types.Tests/Geography/WktTests.cs b/src/Microsoft.SqlServer.Types.Tests/Geography/WktTests.cs index 5b19e78..5d649df 100644 --- a/src/Microsoft.SqlServer.Types.Tests/Geography/WktTests.cs +++ b/src/Microsoft.SqlServer.Types.Tests/Geography/WktTests.cs @@ -111,5 +111,49 @@ public void TestToString() var value = SqlGeography.STGeomFromText(new SqlChars(new SqlString(wkt)), 4326); Assert.AreEqual(wkt, value.ToString()); } + + [TestMethod] + [TestCategory("SqlGeography")] + public void ReadGeometryCollection() + { + var p = SqlGeography.Parse("GEOMETRYCOLLECTION (POINT(10 11), LINESTRING(20 30, 20 40), POLYGON EMPTY, GEOMETRYCOLLECTION(POINT(30 31)))"); + Assert.IsNotNull(p); + Assert.AreEqual("GeometryCollection", p.STGeometryType()); + Assert.AreEqual(4, p.STNumGeometries()); + var g1 = p.STGeometryN(1); + Assert.AreEqual("Point", g1.STGeometryType()); + Assert.AreEqual(10d, g1.Long.Value); + Assert.AreEqual(11d, g1.Lat.Value); + Assert.IsFalse(g1.HasZ); + Assert.IsFalse(g1.HasM); + + var g2 = p.STGeometryN(2); + Assert.AreEqual("LineString", g2.STGeometryType()); + Assert.AreEqual(2, g2.STNumPoints()); + Assert.AreEqual(20, g2.STPointN(1).Long); + Assert.AreEqual(30, g2.STPointN(1).Lat); + Assert.AreEqual(20, g2.STPointN(2).Long); + Assert.AreEqual(40, g2.STPointN(2).Lat); + + var g3 = p.STGeometryN(3); + Assert.AreEqual("Polygon", g3.STGeometryType()); + Assert.IsTrue(g3.STIsEmpty().Value); + + var g4 = p.STGeometryN(4); + Assert.AreEqual("GeometryCollection", g4.STGeometryType()); + Assert.AreEqual(1, g4.STNumGeometries()); + var g4_1 = g4.STGeometryN(1); + Assert.AreEqual("Point", g4_1.STGeometryType()); + Assert.AreEqual(30d, g4_1.Long.Value); + Assert.AreEqual(31d, g4_1.Lat.Value); + } + + [TestMethod] + public void ReadPolygonWithEmptyRing() + { + AssertEx.ThrowsException(() => + SqlGeography.Parse("POLYGON ((10 20, 15 25, 20 30, 10 20), (15 25, 20 30, 25 35, 15 25), EMPTY, (5 5, 6 6, 7 7, 5 5))"), + typeof(System.FormatException), "24305: The Polygon input is not valid because the ring number 3 does not have enough points. Each ring of a polygon must contain at least four points."); + } } } diff --git a/src/Microsoft.SqlServer.Types.Tests/Geometry/WktTests.cs b/src/Microsoft.SqlServer.Types.Tests/Geometry/WktTests.cs index 5593de7..835b2cd 100644 --- a/src/Microsoft.SqlServer.Types.Tests/Geometry/WktTests.cs +++ b/src/Microsoft.SqlServer.Types.Tests/Geometry/WktTests.cs @@ -1,200 +1,265 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Microsoft.SqlServer.Types.Tests.Geometry -{ - [TestClass] - [TestCategory("SqlGeometry")] - [TestCategory("WKT")] - public class WktTests - { - [TestMethod] - public void NullToString() - { - var str = SqlGeometry.Null.ToString(); - Assert.AreEqual("Null", str); - } - - [TestMethod] - public void PointToString() - { - var point = Tests.StreamExtensions.CreateBytes(4326, (byte)0x01, (byte)0x0C, 5d, 10d); - var g = Microsoft.SqlServer.Types.SqlGeometry.Deserialize(new System.Data.SqlTypes.SqlBytes(point)); - var str = g.ToString(); - Assert.AreEqual("POINT (5 10)", str); - } - - [TestMethod] - public void PointFromString() - { - var g = Microsoft.SqlServer.Types.SqlGeometry.Parse(new System.Data.SqlTypes.SqlString("POINT (5 10)")); - Assert.AreEqual(0, g.STSrid.Value); - Assert.AreEqual(5, g.STX.Value); - Assert.AreEqual(10, g.STY.Value); - Assert.IsFalse(g.HasZ); - Assert.IsFalse(g.HasM); - } - - [TestMethod] - public void LineStringToString() - { - var line = Tests.StreamExtensions.CreateBytes(4326, (byte)0x01, (byte)0x05, - 3, 0d, 1d, 3d, 2d, 4d, 5d, 1d, 2d, double.NaN, //vertices - 1, (byte)0x01, 0, //figures - 1, -1, 0, (byte)0x02 //shapes - ); - var g = Microsoft.SqlServer.Types.SqlGeometry.Deserialize(new System.Data.SqlTypes.SqlBytes(line)); - var str = g.ToString(); - Assert.AreEqual("LINESTRING (0 1 1, 3 2 2, 4 5)", str); - } - - [TestMethod] - public void LineStringFromString() - { - var g = SqlGeometry.Parse("LINESTRING (0 1 1, 3 2 2, 4 5)"); - Assert.IsFalse(g.IsNull); - Assert.AreEqual("LineString", g.STGeometryType().Value); - Assert.AreEqual(0, g.STSrid.Value); - Assert.IsTrue(g.STX.IsNull); - Assert.IsTrue(g.STY.IsNull); - Assert.AreEqual(3, g.STNumPoints().Value); - Assert.IsTrue(g.HasZ); - Assert.IsFalse(g.HasM); - Assert.AreEqual(1, g.STNumGeometries().Value); - - Assert.AreEqual(0d, g.STPointN(1).STX.Value); - Assert.AreEqual(1d, g.STPointN(1).STY.Value); - Assert.AreEqual(1d, g.STPointN(1).Z.Value); - Assert.IsTrue(g.STPointN(1).M.IsNull); - - Assert.AreEqual(3d, g.STPointN(2).STX.Value); - Assert.AreEqual(2d, g.STPointN(2).STY.Value); - Assert.AreEqual(2d, g.STPointN(2).Z.Value); - Assert.IsTrue(g.STPointN(2).M.IsNull); - - var p3 = g.STPointN(3); - Assert.AreEqual(4d, p3.STX.Value); - Assert.AreEqual(5d, p3.STY.Value); - Assert.IsFalse(p3.HasZ); - Assert.IsTrue(p3.Z.IsNull); //3rd vertex is NaN and should therefore return Null here - Assert.IsFalse(p3.HasM); - Assert.IsTrue(p3.M.IsNull); +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Microsoft.SqlServer.Types.Tests.Geometry +{ + [TestClass] + [TestCategory("SqlGeometry")] + [TestCategory("WKT")] + public class WktTests + { + [TestMethod] + public void NullToString() + { + var str = SqlGeometry.Null.ToString(); + Assert.AreEqual("Null", str); } + [TestMethod] + public void PointToString() + { + var point = Tests.StreamExtensions.CreateBytes(4326, (byte)0x01, (byte)0x0C, 5d, 10d); + var g = Microsoft.SqlServer.Types.SqlGeometry.Deserialize(new System.Data.SqlTypes.SqlBytes(point)); + var str = g.ToString(); + Assert.AreEqual("POINT (5 10)", str); + } + + [TestMethod] + public void PointFromString() + { + var g = Microsoft.SqlServer.Types.SqlGeometry.Parse(new System.Data.SqlTypes.SqlString("POINT (5 10)")); + Assert.AreEqual(0, g.STSrid.Value); + Assert.AreEqual(5, g.STX.Value); + Assert.AreEqual(10, g.STY.Value); + Assert.IsFalse(g.HasZ); + Assert.IsFalse(g.HasM); + } - [DataTestMethod] - [DataRow("POINT")] - [DataRow("MULTIPOINT")] - [DataRow("LINESTRING")] - [DataRow("MULTILINESTRING")] - [DataRow("POLYGON")] - [DataRow("MULTIPOLYGON")] - [DataRow("GEOMETRYCOLLECTION")] - public void EmptyGeometriesFromString(string parameter) - { - var g = SqlGeometry.Parse(parameter + " EMPTY"); + [TestMethod] + public void LineStringToString() + { + var line = Tests.StreamExtensions.CreateBytes(4326, (byte)0x01, (byte)0x05, + 3, 0d, 1d, 3d, 2d, 4d, 5d, 1d, 2d, double.NaN, //vertices + 1, (byte)0x01, 0, //figures + 1, -1, 0, (byte)0x02 //shapes + ); + var g = Microsoft.SqlServer.Types.SqlGeometry.Deserialize(new System.Data.SqlTypes.SqlBytes(line)); + var str = g.ToString(); + Assert.AreEqual("LINESTRING (0 1 1, 3 2 2, 4 5)", str); + } + + [TestMethod] + public void LineStringFromString() + { + var g = SqlGeometry.Parse("LINESTRING (0 1 1, 3 2 2, 4 5)"); + Assert.IsFalse(g.IsNull); + Assert.AreEqual("LineString", g.STGeometryType().Value); + Assert.AreEqual(0, g.STSrid.Value); + Assert.IsTrue(g.STX.IsNull); + Assert.IsTrue(g.STY.IsNull); + Assert.AreEqual(3, g.STNumPoints().Value); + Assert.IsTrue(g.HasZ); + Assert.IsFalse(g.HasM); + Assert.AreEqual(1, g.STNumGeometries().Value); + + Assert.AreEqual(0d, g.STPointN(1).STX.Value); + Assert.AreEqual(1d, g.STPointN(1).STY.Value); + Assert.AreEqual(1d, g.STPointN(1).Z.Value); + Assert.IsTrue(g.STPointN(1).M.IsNull); + + Assert.AreEqual(3d, g.STPointN(2).STX.Value); + Assert.AreEqual(2d, g.STPointN(2).STY.Value); + Assert.AreEqual(2d, g.STPointN(2).Z.Value); + Assert.IsTrue(g.STPointN(2).M.IsNull); + + var p3 = g.STPointN(3); + Assert.AreEqual(4d, p3.STX.Value); + Assert.AreEqual(5d, p3.STY.Value); + Assert.IsFalse(p3.HasZ); + Assert.IsTrue(p3.Z.IsNull); //3rd vertex is NaN and should therefore return Null here + Assert.IsFalse(p3.HasM); + Assert.IsTrue(p3.M.IsNull); + } + + + [DataTestMethod] + [DataRow("POINT")] + [DataRow("MULTIPOINT")] + [DataRow("LINESTRING")] + [DataRow("MULTILINESTRING")] + [DataRow("POLYGON")] + [DataRow("MULTIPOLYGON")] + [DataRow("GEOMETRYCOLLECTION")] + public void EmptyGeometriesFromString(string parameter) + { + var g = SqlGeometry.Parse(parameter + " EMPTY"); Assert.IsFalse(g.IsNull); Assert.AreEqual(parameter, g.STGeometryType().Value.ToUpper()); Assert.IsTrue(g.STIsEmpty().Value, "STIsEmpty"); Assert.AreEqual(0, g.STNumGeometries(), "STNumGeometries"); Assert.AreEqual(0, g.STNumPoints(), "STNumPoints"); - if(parameter == "POLYGON") + if (parameter == "POLYGON") Assert.AreEqual(0, g.STNumInteriorRing().Value, "STNumInteriorRing"); else Assert.IsTrue(g.STNumInteriorRing().IsNull, "STNumInteriorRing"); } - [TestMethod] - public void MultiLineStringFromString() + [TestMethod] + public void MultiLineStringFromString() { - using (var conn = new System.Data.SqlClient.SqlConnection(DBTests.ConnectionString)) - { - conn.Open(); - using (var cmd = conn.CreateCommand()) - { - cmd.CommandText = "SELECT geometry::Parse('MULTILINESTRING((-10 11, 13 14, 15 16), (20 21, 22 23, 24 25, 26 27))')"; - var geom = cmd.ExecuteScalar(); - //Assert.AreEqual(id.ToString(), geom.ToString()); - } - } - - - var g = SqlGeometry.Parse("MULTILINESTRING ((-10 11, 13 14, 15 16), (20 21, 22 23, 24 25, 26 27))"); - Assert.IsFalse(g.IsNull); - Assert.AreEqual("MultiLineString", g.STGeometryType().Value); - Assert.AreEqual(0, g.STSrid.Value); - Assert.IsTrue(g.STX.IsNull); - Assert.IsTrue(g.STY.IsNull); - Assert.IsFalse(g.HasZ); - Assert.IsFalse(g.HasM); - Assert.AreEqual(2, g.STNumGeometries().Value); - - var part1 = g.STGeometryN(1); - var part2 = g.STGeometryN(2); - Assert.AreEqual(3, part1.STNumPoints()); - Assert.AreEqual(4, part2.STNumPoints()); - - Assert.AreEqual(-10d, part1.STPointN(1).STX.Value); - Assert.AreEqual(11d, part1.STPointN(1).STY.Value); - Assert.AreEqual(3, part1.STNumPoints().Value); - Assert.IsTrue(part2.STPointN(1).Z.IsNull); + using (var conn = new System.Data.SqlClient.SqlConnection(DBTests.ConnectionString)) + { + conn.Open(); + using (var cmd = conn.CreateCommand()) + { + cmd.CommandText = "SELECT geometry::Parse('MULTILINESTRING((-10 11, 13 14, 15 16), (20 21, 22 23, 24 25, 26 27))')"; + var geom = cmd.ExecuteScalar(); + //Assert.AreEqual(id.ToString(), geom.ToString()); + } + } + + + var g = SqlGeometry.Parse("MULTILINESTRING ((-10 11, 13 14, 15 16), (20 21, 22 23, 24 25, 26 27))"); + Assert.IsFalse(g.IsNull); + Assert.AreEqual("MultiLineString", g.STGeometryType().Value); + Assert.AreEqual(0, g.STSrid.Value); + Assert.IsTrue(g.STX.IsNull); + Assert.IsTrue(g.STY.IsNull); + Assert.IsFalse(g.HasZ); + Assert.IsFalse(g.HasM); + Assert.AreEqual(2, g.STNumGeometries().Value); + + var part1 = g.STGeometryN(1); + var part2 = g.STGeometryN(2); + Assert.AreEqual(3, part1.STNumPoints()); + Assert.AreEqual(4, part2.STNumPoints()); + + Assert.AreEqual(-10d, part1.STPointN(1).STX.Value); + Assert.AreEqual(11d, part1.STPointN(1).STY.Value); + Assert.AreEqual(3, part1.STNumPoints().Value); + Assert.IsTrue(part2.STPointN(1).Z.IsNull); Assert.IsTrue(part2.STPointN(1).M.IsNull); Assert.AreEqual(4, part2.STNumPoints().Value); } - [TestMethod] - public void PolygonFromString() + [TestMethod] + public void PolygonFromString() + { + var g = SqlGeometry.Parse("POLYGON((-122.358 47.653, -122.348 47.649, -122.348 47.658, -122.358 47.658, -122.358 47.653))"); + Assert.IsFalse(g.IsNull); + Assert.AreEqual("Polygon", g.STGeometryType().Value); + Assert.AreEqual(5, g.STNumPoints().Value); + Assert.IsFalse(g.HasZ); + Assert.IsFalse(g.HasM); + Assert.AreEqual(1, g.STNumGeometries().Value); + } + + [TestMethod] + public void MultiPolygonFromString() { - var g = SqlGeometry.Parse("POLYGON((-122.358 47.653, -122.348 47.649, -122.348 47.658, -122.358 47.658, -122.358 47.653))"); - Assert.IsFalse(g.IsNull); - Assert.AreEqual("Polygon", g.STGeometryType().Value); - Assert.AreEqual(5, g.STNumPoints().Value); - Assert.IsFalse(g.HasZ); - Assert.IsFalse(g.HasM); - Assert.AreEqual(1, g.STNumGeometries().Value); - } - - [TestMethod] - public void MultiPolygonFromString() + var g = SqlGeometry.Parse("MULTIPOLYGON(((-122.358 47.653, -122.348 47.649, -122.358 47.658, -122.358 47.653)), ((-122.341 47.656, -122.341 47.661, -122.351 47.661, -122.341 47.656)))"); + Assert.IsFalse(g.IsNull); + Assert.AreEqual("MultiPolygon", g.STGeometryType().Value); + Assert.AreEqual(8, g.STNumPoints().Value); + Assert.IsFalse(g.HasZ); + Assert.IsFalse(g.HasM); + Assert.AreEqual(2, g.STNumGeometries().Value); + } + + [TestMethod] + public void GeometryCollectionFromString() { - var g = SqlGeometry.Parse("MULTIPOLYGON(((-122.358 47.653, -122.348 47.649, -122.358 47.658, -122.358 47.653)), ((-122.341 47.656, -122.341 47.661, -122.351 47.661, -122.341 47.656)))"); - Assert.IsFalse(g.IsNull); - Assert.AreEqual("MultiPolygon", g.STGeometryType().Value); - Assert.AreEqual(8, g.STNumPoints().Value); - Assert.IsFalse(g.HasZ); - Assert.IsFalse(g.HasM); - Assert.AreEqual(2, g.STNumGeometries().Value); - } - - [TestMethod] - public void GeometryCollectionFromString() + var g = SqlGeometry.Parse("GEOMETRYCOLLECTION ( POINT(-122.34900 47.65100), LINESTRING(-122.360 47.656, -122.343 47.656))"); + Assert.IsFalse(g.IsNull); + Assert.AreEqual("GeometryCollection", g.STGeometryType().Value); + Assert.AreEqual(2, g.STNumGeometries().Value); + var g1 = g.STGeometryN(1); + var g2 = g.STGeometryN(2); + Assert.AreEqual("Point", g1.STGeometryType()); + Assert.AreEqual("LineString", g2.STGeometryType()); + } + + [TestMethod] + [WorkItem(13)] + public void UserSubmittedIssue_WKT1() + { + using (var conn = new System.Data.SqlClient.SqlConnection(DBTests.ConnectionString)) + { + conn.Open(); + var id = SqlGeometry.Parse("LINESTRING (100 100, 20 180, 180 180)"); + using (var cmd = conn.CreateCommand()) + { + cmd.CommandText = "SELECT @p"; + var p = cmd.Parameters.Add("@p", System.Data.SqlDbType.Udt); + p.UdtTypeName = "geometry"; + p.Value = id; + Assert.AreEqual(id.ToString(), cmd.ExecuteScalar().ToString()); + } + } + } + + [TestMethod] + [TestCategory("SqlGeometry")] + public void ReadGeometryCollection() { - var g = SqlGeometry.Parse("GEOMETRYCOLLECTION ( POINT(-122.34900 47.65100), LINESTRING(-122.360 47.656, -122.343 47.656))"); - Assert.IsFalse(g.IsNull); - Assert.AreEqual("GeometryCollection", g.STGeometryType().Value); - Assert.AreEqual(2, g.STNumGeometries().Value); - var g1 = g.STGeometryN(1); - var g2 = g.STGeometryN(2); - Assert.AreEqual("Point", g1.STGeometryType()); - Assert.AreEqual("LineString", g2.STGeometryType()); - } - - [TestMethod] - [WorkItem(13)] - public void UserSubmittedIssue_WKT1() - { - using (var conn = new System.Data.SqlClient.SqlConnection(DBTests.ConnectionString)) - { - conn.Open(); - var id = SqlGeometry.Parse("LINESTRING (100 100, 20 180, 180 180)"); - using (var cmd = conn.CreateCommand()) - { - cmd.CommandText = "SELECT @p"; - var p =cmd.Parameters.Add("@p", System.Data.SqlDbType.Udt); - p.UdtTypeName = "geometry"; - p.Value = id; - Assert.AreEqual(id.ToString(), cmd.ExecuteScalar().ToString()); - } - } - } - } -} + var p = SqlGeometry.Parse("GEOMETRYCOLLECTION (POINT(10 11), LINESTRING(20 30, 20 40), POLYGON EMPTY, GEOMETRYCOLLECTION(POINT(30 31), POINT(40 41)))"); + Assert.IsNotNull(p); + Assert.AreEqual("GeometryCollection", p.STGeometryType()); + Assert.AreEqual(4, p.STNumGeometries()); + var g1 = p.STGeometryN(1); + Assert.AreEqual("Point", g1.STGeometryType()); + Assert.AreEqual(10d, g1.STX.Value); + Assert.AreEqual(11d, g1.STY.Value); + Assert.IsFalse(g1.HasZ); + Assert.IsFalse(g1.HasM); + + var g2 = p.STGeometryN(2); + Assert.AreEqual("LineString", g2.STGeometryType()); + Assert.AreEqual(2, g2.STNumPoints()); + Assert.AreEqual(20, g2.STPointN(1).STX); + Assert.AreEqual(30, g2.STPointN(1).STY); + Assert.AreEqual(20, g2.STPointN(2).STX); + Assert.AreEqual(40, g2.STPointN(2).STY); + + var g3 = p.STGeometryN(3); + Assert.AreEqual("Polygon", g3.STGeometryType()); + Assert.IsTrue(g3.STIsEmpty().Value); + + var g4 = p.STGeometryN(4); + Assert.AreEqual("GeometryCollection", g4.STGeometryType()); + Assert.AreEqual(2, g4.STNumGeometries()); + var g4_1 = g4.STGeometryN(1); + Assert.AreEqual("Point", g4_1.STGeometryType()); + Assert.AreEqual(30d, g4_1.STX.Value); + Assert.AreEqual(31d, g4_1.STY.Value); + var g4_2 = g4.STGeometryN(2); + Assert.AreEqual("Point", g4_2.STGeometryType()); + Assert.AreEqual(40d, g4_2.STX.Value); + Assert.AreEqual(41d, g4_2.STY.Value); + } + + [TestMethod] + public void ReadPolygonWithEmptyRing() + { + AssertEx.ThrowsException(() => + SqlGeometry.Parse("POLYGON ((10 20, 15 25, 20 30, 10 20), (15 25, 20 30, 25 35, 15 25), EMPTY, (5 5, 6 6, 7 7, 5 5))"), + typeof(FormatException), "24120: The Polygon input is not valid because the interior ring number 2 does not have enough points. Each ring of a polygon must contain at least four points."); + } + + [TestMethod] + public void ReadPolygonWith3PtRing() + { + AssertEx.ThrowsException(() => + SqlGeometry.Parse("POLYGON ((10 20, 15 25, 20 30, 10 20), (15 25, 20 30, 15 25))"), + typeof(FormatException), "24120: The Polygon input is not valid because the interior ring number 1 does not have enough points. Each ring of a polygon must contain at least four points."); + } + + [TestMethod] + public void ReadPolygonEmpty() + { + var g = SqlGeometry.Parse("POLYGON EMPTY"); + Assert.AreEqual("Polygon", g.STGeometryType()); + Assert.IsTrue((bool)g.STIsEmpty()); + } + } +} diff --git a/src/Microsoft.SqlServer.Types/ShapeData.cs b/src/Microsoft.SqlServer.Types/ShapeData.cs index c422b63..d00a5c1 100644 --- a/src/Microsoft.SqlServer.Types/ShapeData.cs +++ b/src/Microsoft.SqlServer.Types/ShapeData.cs @@ -256,42 +256,54 @@ private ShapeData ShapeToGeometry(int shapeIndex) if (_shapes[nextShape].ParentOffset == shape.ParentOffset) break; } + Figure[]? figures = null; + Point[]? vertices = null; + double[]? zvalues = null; + double[]? mvalues = null; - List shapes = new List(nextShape - shapeIndex); - List
figures = new List
(); - List vertices = new List(); - List? zvalues = _zValues == null ? null : new List(); - List? mvalues = _mValues == null ? null : new List(); - for (int i = shapeIndex; i < nextShape; i++) + var figureStart = shape.FigureOffset; + if (figureStart > -1 && _figures != null) { - var s = _shapes[i]; - var nextFigure = i + 1 < _shapes.Length ? _shapes[i + 1].FigureOffset : (_figures?.Length ?? 0); - var figureOffset = figures.Count; - for (int j = s.FigureOffset; j < nextFigure; j++) + var figureEnd = _shapes.Length > nextShape ? _shapes[nextShape].FigureOffset : _figures.Length; + int i = nextShape; + while (figureEnd == -1) // Skip empty figures { - var f = _figures![j]; - figures.Add(new Figure() { FigureAttribute = f.FigureAttribute, VertexOffset = vertices.Count }); - var nextFigureVertexOffset = (j + 1 < _figures.Length ) ? _figures[j + 1].VertexOffset : (_vertices?.Length ?? 0); - vertices.AddRange(_vertices.Skip(f.VertexOffset).Take(nextFigureVertexOffset - f.VertexOffset)); - if (zvalues != null) - zvalues.AddRange(_zValues.Skip(f.VertexOffset).Take(nextFigureVertexOffset - f.VertexOffset)); - if (mvalues != null) - mvalues.AddRange(_mValues.Skip(f.VertexOffset).Take(nextFigureVertexOffset - f.VertexOffset)); + i++; + figureEnd = _shapes.Length > i ? _shapes[i].FigureOffset : _figures.Length; + } + var vertexStart = _figures[figureStart].VertexOffset; + var vertexEnd = _figures.Length > figureEnd ? _figures[figureEnd].VertexOffset : _vertices?.Length ?? 0; + vertices = _vertices.Skip(vertexStart).Take(vertexEnd - vertexStart).ToArray(); + zvalues = _zValues == null ? null : _zValues.Skip(vertexStart).Take(vertexEnd - vertexStart).ToArray(); + mvalues = _mValues == null ? null : _mValues.Skip(vertexStart).Take(vertexEnd - vertexStart).ToArray(); + figures = new Figure[figureEnd - figureStart]; + for (int f = figureStart; f < figureEnd; f++) + { + var figure = _figures[f]; + figures[f - figureStart] = new Figure() { FigureAttribute = figure.FigureAttribute, VertexOffset = figure.VertexOffset - vertexStart }; } - shapes.Add(new Shape() { type = s.type, ParentOffset = shape.ParentOffset - s.ParentOffset - 1, FigureOffset = figureOffset }); } - geoDatum._shapes = shapes.ToArray(); - geoDatum._figures = figures != null || figures.Any() ? figures?.ToArray() : null; - if(vertices.Count == 1 && double.IsNaN(vertices[0].X) && double.IsNaN(vertices[0].Y)) //Empty point + Shape[] shapes = new Shape[nextShape - shapeIndex]; + for (int s = shapeIndex; s < nextShape; s++) { - + var sh = _shapes[s]; + shapes[s - shapeIndex] = new Shape() { type = sh.type, FigureOffset = sh.FigureOffset == -1 ? -1 : (sh.FigureOffset - figureStart), ParentOffset = Math.Max(-1, sh.ParentOffset - shapeIndex) }; + } + if (vertices != null && vertices.Length == 1 && double.IsNaN(vertices[0].X) && double.IsNaN(vertices[0].Y)) //Empty point + { + geoDatum._figures = null; + geoDatum._vertices = null; + geoDatum._zValues = null; + geoDatum._mValues = null; } else { - geoDatum._vertices = vertices.ToArray(); - geoDatum._zValues = zvalues?.ToArray(); - geoDatum._mValues = mvalues?.ToArray(); + geoDatum._figures = figures; + geoDatum._vertices = vertices; + geoDatum._zValues = zvalues; + geoDatum._mValues = mvalues; } + geoDatum._shapes = shapes; geoDatum._isLargerThanAHemisphere = this._isLargerThanAHemisphere; //TODO: Segments geoDatum._isValid = _isValid; diff --git a/src/Microsoft.SqlServer.Types/Wkt/WktReader.cs b/src/Microsoft.SqlServer.Types/Wkt/WktReader.cs index c7ac335..00dc971 100644 --- a/src/Microsoft.SqlServer.Types/Wkt/WktReader.cs +++ b/src/Microsoft.SqlServer.Types/Wkt/WktReader.cs @@ -198,17 +198,24 @@ private void ReadPolygon(int parentOffset = -1) return; } _shapes.Add(new Shape() { type = OGCGeometryType.Polygon, FigureOffset = _figures.Count, ParentOffset = parentOffset }); + int ringStart = _figures.Count; _figures.Add(new Figure() { FigureAttribute = FigureAttributes.ExteriorRing, VertexOffset = _vertices.Count }); ReadToken(PARAN_START); ReadCoordinateCollection(); //Exterior ring + if (_figures[_figures.Count - 1].VertexOffset + 4 > _vertices.Count) + { + throw new FormatException($"24305: The Polygon input is not valid because the ring number {_figures.Count - ringStart} does not have enough points. Each ring of a polygon must contain at least four points."); + } while (ReadOptionalChar(COMMA)) //Interior rings { _figures.Add(new Figure() { FigureAttribute = FigureAttributes.InteriorRing, VertexOffset = _vertices.Count }); ReadCoordinateCollection(); - if(_figures[_figures.Count-1].VertexOffset == _vertices.Count) + if (_figures[_figures.Count - 1].VertexOffset + 4 > _vertices.Count) { - // Remove empty interior rings - _figures.RemoveAt(_figures.Count - 1); + if (_order == CoordinateOrder.LatLong) + throw new FormatException($"24305: The Polygon input is not valid because the ring number {_figures.Count - ringStart} does not have enough points. Each ring of a polygon must contain at least four points."); + else + throw new FormatException($"24120: The Polygon input is not valid because the interior ring number {_figures.Count - ringStart - 1} does not have enough points. Each ring of a polygon must contain at least four points."); } } ReadToken(PARAN_END);