diff --git a/src/Microsoft.SqlServer.Types.RefTests/Microsoft.SqlServer.Types.RefTests.csproj b/src/Microsoft.SqlServer.Types.RefTests/Microsoft.SqlServer.Types.RefTests.csproj index 5c5b728..004322b 100644 --- a/src/Microsoft.SqlServer.Types.RefTests/Microsoft.SqlServer.Types.RefTests.csproj +++ b/src/Microsoft.SqlServer.Types.RefTests/Microsoft.SqlServer.Types.RefTests.csproj @@ -49,6 +49,9 @@ AssemblyLoader.cs + + Geography\WktTests.cs + Geometry\DBTests.cs @@ -96,9 +99,7 @@ 1.3.2 - - - + \ No newline at end of file diff --git a/src/Microsoft.SqlServer.Types.Tests/Geography/WktTests.cs b/src/Microsoft.SqlServer.Types.Tests/Geography/WktTests.cs new file mode 100644 index 0000000..4e5ef0b --- /dev/null +++ b/src/Microsoft.SqlServer.Types.Tests/Geography/WktTests.cs @@ -0,0 +1,78 @@ +using Microsoft.SqlServer.Types.Tests.Geometry; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Microsoft.SqlServer.Types.Tests.Geography +{ + [TestClass] + [TestCategory("SqlGeography")] + [TestCategory("WKT")] + public class WktTests + { + + [TestMethod] + [WorkItem(13)] + public void UserSubmittedIssue_WKT2() + { + using (var conn = new System.Data.SqlClient.SqlConnection(DBTests.ConnectionString)) + { + conn.Open(); + var id = SqlGeography.Parse("LINESTRING (-122.36 47.656, -122.343 47.656)"); + + using (var cmd = conn.CreateCommand()) + { + cmd.CommandText = "SELECT @p"; + var p = cmd.CreateParameter(); + cmd.Parameters.Add(p); + + p.UdtTypeName = "geography"; + p.ParameterName = "@p"; + p.Value = id; + + Assert.AreEqual(id.ToString(), cmd.ExecuteScalar().ToString()); + } + } + } + + [TestMethod] + [WorkItem(13)] + public void UserSubmittedIssue_WKT3() + { + using (var conn = new System.Data.SqlClient.SqlConnection(DBTests.ConnectionString)) + { + conn.Open(); + var id = SqlGeography.Parse("LINESTRING (-122.36 47.656, -122.343 47.656)"); + var l = id.STPointN(1).Long; + using (var cmd = conn.CreateCommand()) + { + cmd.CommandText = "SELECT Cast(geography::STGeomFromText('LINESTRING(-122.360 47.656, -122.343 47.656)', 4326) as geography)"; + + Assert.AreEqual(id.ToString(), cmd.ExecuteScalar().ToString()); + } + } + } + + [TestMethod] + [WorkItem(14)] + public void UserSubmittedIssue_WKT4() + { + using (var conn = new System.Data.SqlClient.SqlConnection(DBTests.ConnectionString)) + { + conn.Open(); + using (var cmd = conn.CreateCommand()) + { + cmd.CommandText = "SELECT Cast(geography::STGeomFromText('LINESTRING(-122.360 47.656, -122.343 47.656)', 4326) as geography)"; + var result = cmd.ExecuteScalar(); + Assert.IsInstanceOfType(result, typeof(SqlGeography)); + SqlGeography g = (SqlGeography)result; + Assert.AreEqual("LineString", g.STGeometryType()); + Assert.AreEqual(2, g.STNumPoints()); + Assert.AreEqual(47.656, g.STPointN(1).Lat.Value); + Assert.AreEqual(-122.360, g.STPointN(1).Long.Value); + Assert.AreEqual(47.656, g.STPointN(2).Lat.Value); + Assert.AreEqual(-122.343, g.STPointN(2).Long.Value); + Assert.AreEqual("LINESTRING (-122.36 47.656, -122.343 47.656)", cmd.ExecuteScalar().ToString()); + } + } + } + } +} diff --git a/src/Microsoft.SqlServer.Types.Tests/Geometry/DBTests.cs b/src/Microsoft.SqlServer.Types.Tests/Geometry/DBTests.cs index 5876af3..47b8a56 100644 --- a/src/Microsoft.SqlServer.Types.Tests/Geometry/DBTests.cs +++ b/src/Microsoft.SqlServer.Types.Tests/Geometry/DBTests.cs @@ -22,29 +22,38 @@ public class DBTests : IDisposable private System.Data.SqlClient.SqlConnection conn; private static string path; + private static object lockObj = new object(); static DBTests() { - path = Path.Combine(new FileInfo(typeof(DBTests).Assembly.Location).Directory.FullName, "UnitTestData.mdf"); - CreateSqlDatabase(path); - using (var conn = new System.Data.SqlClient.SqlConnection(connstr + path)) + Init(); + } + public static void Init() + { + lock(lockObj) + if(path == null) { - conn.Open(); - var cmd = conn.CreateCommand(); - cmd.CommandText = OgcConformanceMap.DropTables; - cmd.ExecuteNonQuery(); - cmd.CommandText = OgcConformanceMap.CreateTables; - cmd.ExecuteNonQuery(); - cmd.CommandText = OgcConformanceMap.CreateRows; - cmd.ExecuteNonQuery(); - conn.Close(); + path = Path.Combine(new FileInfo(typeof(DBTests).Assembly.Location).Directory.FullName, "UnitTestData.mdf"); + CreateSqlDatabase(path); + using (var conn = new System.Data.SqlClient.SqlConnection(connstr + path)) + { + conn.Open(); + var cmd = conn.CreateCommand(); + cmd.CommandText = OgcConformanceMap.DropTables; + cmd.ExecuteNonQuery(); + cmd.CommandText = OgcConformanceMap.CreateTables; + cmd.ExecuteNonQuery(); + cmd.CommandText = OgcConformanceMap.CreateRows; + cmd.ExecuteNonQuery(); + conn.Close(); + } } } public static string ConnectionString => connstr + path; - public DBTests() { + Init(); conn = new System.Data.SqlClient.SqlConnection(ConnectionString); conn.Open(); } diff --git a/src/Microsoft.SqlServer.Types.Tests/Geometry/WktTests.cs b/src/Microsoft.SqlServer.Types.Tests/Geometry/WktTests.cs index 55cd8fa..79af238 100644 --- a/src/Microsoft.SqlServer.Types.Tests/Geometry/WktTests.cs +++ b/src/Microsoft.SqlServer.Types.Tests/Geometry/WktTests.cs @@ -3,10 +3,10 @@ namespace Microsoft.SqlServer.Types.Tests.Geometry { [TestClass] + [TestCategory("SqlGeometry")] + [TestCategory("WKT")] public class WktTests { - public object StreamExtensions { get; private set; } - [TestMethod] public void TestNullToString() { @@ -45,6 +45,7 @@ public void TestLineStringToString() var str = g.ToString(); Assert.AreEqual("LINESTRING (0 1 1, 3 2 2, 4 5)", str); } + [TestMethod] public void TestLineStringFromString() { @@ -78,6 +79,23 @@ public void TestLineStringFromString() Assert.IsTrue(p3.M.IsNull); } - + [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()); + } + } + } } } diff --git a/src/Microsoft.SqlServer.Types.Tests/Microsoft.SqlServer.Types.Tests.csproj b/src/Microsoft.SqlServer.Types.Tests/Microsoft.SqlServer.Types.Tests.csproj index b3bbe29..04df44b 100644 --- a/src/Microsoft.SqlServer.Types.Tests/Microsoft.SqlServer.Types.Tests.csproj +++ b/src/Microsoft.SqlServer.Types.Tests/Microsoft.SqlServer.Types.Tests.csproj @@ -27,8 +27,4 @@ - - - - diff --git a/src/Microsoft.SqlServer.Types/SqlGeography.cs b/src/Microsoft.SqlServer.Types/SqlGeography.cs index fd7a010..a1cfeeb 100644 --- a/src/Microsoft.SqlServer.Types/SqlGeography.cs +++ b/src/Microsoft.SqlServer.Types/SqlGeography.cs @@ -416,7 +416,7 @@ public static SqlGeography STGeomFromText(SqlChars geometryTaggedText, int srid) { if (geometryTaggedText.IsNull) return SqlGeography.Null; - var data = Wkt.WktReader.Parse(geometryTaggedText.ToString(), Wkt.WktReader.CoordinateOrder.LatLong); + var data = Wkt.WktReader.Parse(geometryTaggedText.ToString(), Wkt.CoordinateOrder.LatLong); return new SqlGeography(data, srid); } @@ -433,7 +433,7 @@ public static SqlGeography Parse(SqlString s) { if (s.IsNull) return SqlGeography.Null; - var data = Wkt.WktReader.Parse(s.ToString(), Wkt.WktReader.CoordinateOrder.LatLong); + var data = Wkt.WktReader.Parse(s.ToString(), Wkt.CoordinateOrder.LatLong); return new SqlGeography(data, 4326); } @@ -495,6 +495,6 @@ public SqlBytes Serialize() } } - public override string ToString() => WktWriter.Write(_geometry); + public override string ToString() => Wkt.WktWriter.Write(_geometry, Wkt.CoordinateOrder.LatLong); } } \ No newline at end of file diff --git a/src/Microsoft.SqlServer.Types/SqlGeometry.cs b/src/Microsoft.SqlServer.Types/SqlGeometry.cs index 3c20f98..807ca0a 100644 --- a/src/Microsoft.SqlServer.Types/SqlGeometry.cs +++ b/src/Microsoft.SqlServer.Types/SqlGeometry.cs @@ -412,7 +412,7 @@ public static SqlGeometry STGeomFromText(SqlChars geometryTaggedText, int srid) { if (geometryTaggedText.IsNull) return SqlGeometry.Null; - var data = Wkt.WktReader.Parse(geometryTaggedText.ToString(), Wkt.WktReader.CoordinateOrder.XY); + var data = Wkt.WktReader.Parse(geometryTaggedText.ToString(), Wkt.CoordinateOrder.XY); return new SqlGeometry(data, srid); } @@ -432,7 +432,7 @@ public static SqlGeometry Parse(SqlString s) { if (s.IsNull) return SqlGeometry.Null; - var data = Wkt.WktReader.Parse(s.ToString(), Wkt.WktReader.CoordinateOrder.XY); + var data = Wkt.WktReader.Parse(s.ToString(), Wkt.CoordinateOrder.XY); return new SqlGeometry(data, 0); } @@ -449,6 +449,6 @@ public void Write(BinaryWriter w) _geometry.Write(w); } - public override string ToString() => WktWriter.Write(_geometry); + public override string ToString() => Wkt.WktWriter.Write(_geometry, Wkt.CoordinateOrder.XY); } } diff --git a/src/Microsoft.SqlServer.Types/Wkt/WktReader.cs b/src/Microsoft.SqlServer.Types/Wkt/WktReader.cs index 3dd9ebf..cdf0659 100644 --- a/src/Microsoft.SqlServer.Types/Wkt/WktReader.cs +++ b/src/Microsoft.SqlServer.Types/Wkt/WktReader.cs @@ -5,6 +5,12 @@ namespace Microsoft.SqlServer.Types.Wkt { + internal enum CoordinateOrder + { + XY, + LatLong + } + internal class WktReader { private int length = 0; @@ -23,12 +29,6 @@ internal class WktReader List _shapes; CoordinateOrder _order; - public enum CoordinateOrder - { - XY, - LatLong - } - private WktReader(string str) { if (string.IsNullOrEmpty(str)) diff --git a/src/Microsoft.SqlServer.Types/Wkt/WktWriter.cs b/src/Microsoft.SqlServer.Types/Wkt/WktWriter.cs index a2a4f08..110b593 100644 --- a/src/Microsoft.SqlServer.Types/Wkt/WktWriter.cs +++ b/src/Microsoft.SqlServer.Types/Wkt/WktWriter.cs @@ -4,7 +4,7 @@ using System.Text; using System.Globalization; -namespace Microsoft.SqlServer.Types +namespace Microsoft.SqlServer.Types.Wkt { /// /// Converts geometries to and from Well-Known Text. @@ -17,11 +17,11 @@ internal static class WktWriter /// /// Geometry /// Well-Known Text - public static string Write(ShapeData g) + public static string Write(ShapeData g, CoordinateOrder order) { if (g.IsNull) return "Null"; - return Write(g, g.HasZ, g.HasM); + return Write(g, g.HasZ, g.HasM, order); } /// @@ -31,14 +31,14 @@ public static string Write(ShapeData g) /// Include Z values /// Include M values /// Well-Known Text - public static string Write(ShapeData g, bool includeZ, bool includeM) + public static string Write(ShapeData g, bool includeZ, bool includeM, CoordinateOrder order) { StringBuilder sb = new StringBuilder(); - WriteGeometry(g, sb, includeZ, includeM); + WriteGeometry(g, sb, includeZ, includeM, order); return sb.ToString(); } - private static void WriteGeometry(ShapeData geometry, StringBuilder sb, bool includeZ, bool includeM) + private static void WriteGeometry(ShapeData geometry, StringBuilder sb, bool includeZ, bool includeM, CoordinateOrder order) { var type = geometry.Type; sb.Append(type.ToString().ToUpperInvariant()); @@ -49,57 +49,57 @@ private static void WriteGeometry(ShapeData geometry, StringBuilder sb, bool inc } else sb.Append(" ("); if (type == OGCGeometryType.Point) - WritePoint(geometry, sb, includeZ, includeM); + WritePoint(geometry, sb, includeZ, includeM, order); else if (type == OGCGeometryType.LineString) - WriteLineString(geometry, sb, includeZ, includeM); + WriteLineString(geometry, sb, includeZ, includeM, order); else if (type == OGCGeometryType.Polygon) - WritePolygon(geometry, sb, includeZ, includeM); + WritePolygon(geometry, sb, includeZ, includeM, order); else if (type == OGCGeometryType.MultiPoint) - WriteMultiPoint(geometry, sb, includeZ, includeM); + WriteMultiPoint(geometry, sb, includeZ, includeM, order); else if (type == OGCGeometryType.MultiLineString) - WriteMultiLineString(geometry, sb, includeZ, includeM); + WriteMultiLineString(geometry, sb, includeZ, includeM, order); else if (type == OGCGeometryType.MultiPolygon) - WriteMultiPolygon(geometry, sb, includeZ, includeM); + WriteMultiPolygon(geometry, sb, includeZ, includeM, order); else if (type == OGCGeometryType.GeometryCollection) - WriteGeometryCollection(geometry, sb, includeZ, includeM); + WriteGeometryCollection(geometry, sb, includeZ, includeM, order); else throw new ArgumentException("Invalid Geometry Type"); sb.Append(")"); } - private static void WritePoint(ShapeData point, StringBuilder sb, bool includeZ, bool includeM) + private static void WritePoint(ShapeData point, StringBuilder sb, bool includeZ, bool includeM, CoordinateOrder order) { - WriteCoordinate(point.GetPointN(1), sb, includeZ, includeM); + WriteCoordinate(point.GetPointN(1), sb, includeZ, includeM, order); } - private static void WriteMultiPoint(ShapeData points, StringBuilder sb, bool includeZ, bool includeM) + private static void WriteMultiPoint(ShapeData points, StringBuilder sb, bool includeZ, bool includeM, CoordinateOrder order) { for (int i = 0; i < points.NumPoints; i++) { if (i > 0) sb.Append(","); - WriteCoordinate(points.GetPointN(i+1), sb, includeZ, includeM); + WriteCoordinate(points.GetPointN(i+1), sb, includeZ, includeM, order); } } - private static void WriteLineString(ShapeData line, StringBuilder sb, bool includeZ, bool includeM) + private static void WriteLineString(ShapeData line, StringBuilder sb, bool includeZ, bool includeM, CoordinateOrder order) { - WriteCoordinateCollection(GetVertices(line), sb, includeZ, includeM); + WriteCoordinateCollection(GetVertices(line), sb, includeZ, includeM, order); } - private static void WriteMultiLineString(ShapeData lines, StringBuilder sb, bool includeZ, bool includeM) + private static void WriteMultiLineString(ShapeData lines, StringBuilder sb, bool includeZ, bool includeM, CoordinateOrder order) { sb.Append('('); for (int i = 0; i < lines.NumGeometries; i++) { if (i > 0) sb.Append("),("); - WriteCoordinateCollection(GetVertices(lines.GetRing(i)), sb, includeZ, includeM); + WriteCoordinateCollection(GetVertices(lines.GetRing(i)), sb, includeZ, includeM, order); } sb.Append(")"); } - private static void WritePolygon(ShapeData polygon, StringBuilder sb, bool includeZ, bool includeM) + private static void WritePolygon(ShapeData polygon, StringBuilder sb, bool includeZ, bool includeM, CoordinateOrder order) { - WritePolygonContents(polygon, sb, includeZ, includeM); + WritePolygonContents(polygon, sb, includeZ, includeM, order); } private static IEnumerable GetVertices(ShapeData g) @@ -107,52 +107,55 @@ private static IEnumerable GetVertices(ShapeData g) return Enumerable.Range(1, g.NumPoints).Select(s => g.GetPointN(s)); } - private static void WritePolygonContents(ShapeData polygon, StringBuilder sb, bool includeZ, bool includeM) + private static void WritePolygonContents(ShapeData polygon, StringBuilder sb, bool includeZ, bool includeM, CoordinateOrder order) { sb.Append('('); - WriteCoordinateCollection(GetVertices(polygon.GetRing(0)), sb, includeZ, includeM); + WriteCoordinateCollection(GetVertices(polygon.GetRing(0)), sb, includeZ, includeM, order); for (int i = 0; i < polygon.NumInteriorRing; i++) { sb.Append("),("); - WriteCoordinateCollection(GetVertices(polygon.GetRing(i + 1)), sb, includeZ, includeM); + WriteCoordinateCollection(GetVertices(polygon.GetRing(i + 1)), sb, includeZ, includeM, order); } sb.Append(')'); } - private static void WriteMultiPolygon(ShapeData polys, StringBuilder sb, bool includeZ, bool includeM) + private static void WriteMultiPolygon(ShapeData polys, StringBuilder sb, bool includeZ, bool includeM, CoordinateOrder order) { sb.Append('('); for (int i = 0; i < polys.NumGeometries; i++) { if (i > 0) sb.Append("),("); - WritePolygonContents(polys.GetGeometryN(i+1), sb, includeZ, includeM); + WritePolygonContents(polys.GetGeometryN(i+1), sb, includeZ, includeM, order); } sb.Append(')'); } - private static void WriteGeometryCollection(ShapeData geoms, StringBuilder sb, bool includeZ, bool includeM) + private static void WriteGeometryCollection(ShapeData geoms, StringBuilder sb, bool includeZ, bool includeM, CoordinateOrder order) { for (int i = 0; i < geoms.NumGeometries; i++) { if (i > 0) sb.Append(","); - WriteGeometry(geoms.GetGeometryN(i+1), sb, includeZ, includeM); + WriteGeometry(geoms.GetGeometryN(i+1), sb, includeZ, includeM, order); } } - private static void WriteCoordinateCollection(IEnumerable coords, StringBuilder sb, bool includeZ, bool includeM) + private static void WriteCoordinateCollection(IEnumerable coords, StringBuilder sb, bool includeZ, bool includeM, CoordinateOrder order) { bool firstItem = true; foreach(var c in coords) { if (firstItem) firstItem = false; else sb.Append(", "); - WriteCoordinate(c, sb, includeZ, includeM); + WriteCoordinate(c, sb, includeZ, includeM, order); } } - private static void WriteCoordinate(PointZM coord, StringBuilder sb, bool includeZ, bool includeM) + private static void WriteCoordinate(PointZM coord, StringBuilder sb, bool includeZ, bool includeM, CoordinateOrder order) { - sb.AppendFormat(CultureInfo.InvariantCulture, "{0} {1}", coord.X, coord.Y); + if(order == CoordinateOrder.XY) + sb.AppendFormat(CultureInfo.InvariantCulture, "{0} {1}", coord.X, coord.Y); + else + sb.AppendFormat(CultureInfo.InvariantCulture, "{0} {1}", coord.Y, coord.X); if (includeZ) { if (!double.IsNaN(coord.Z))