diff --git a/src/EFCore.PG.NTS/Query/ExpressionTranslators/Internal/NpgsqlNetTopologySuiteMemberTranslatorPlugin.cs b/src/EFCore.PG.NTS/Query/ExpressionTranslators/Internal/NpgsqlNetTopologySuiteMemberTranslatorPlugin.cs index 7bb1d0959..473b95cb5 100644 --- a/src/EFCore.PG.NTS/Query/ExpressionTranslators/Internal/NpgsqlNetTopologySuiteMemberTranslatorPlugin.cs +++ b/src/EFCore.PG.NTS/Query/ExpressionTranslators/Internal/NpgsqlNetTopologySuiteMemberTranslatorPlugin.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq.Expressions; using GeoAPI.Geometries; using Microsoft.EntityFrameworkCore.Query.Expressions; @@ -18,6 +18,24 @@ public class NpgsqlNetTopologySuiteMemberTranslatorPlugin : IMemberTranslatorPlu public class NpgsqlGeometryMemberTranslator : IMemberTranslator { + static readonly CaseWhenClause[] _ogcGeometryTypeWhenThenList = new[] + { + new CaseWhenClause(Expression.Constant("ST_CircularString"), Expression.Constant(OgcGeometryType.CircularString)), + new CaseWhenClause(Expression.Constant("ST_CompoundCurve"), Expression.Constant(OgcGeometryType.CompoundCurve)), + new CaseWhenClause(Expression.Constant("ST_CurvePolygon"), Expression.Constant(OgcGeometryType.CurvePolygon)), + new CaseWhenClause(Expression.Constant("ST_GeometryCollection"), Expression.Constant(OgcGeometryType.GeometryCollection)), + new CaseWhenClause(Expression.Constant("ST_LineString"), Expression.Constant(OgcGeometryType.LineString)), + new CaseWhenClause(Expression.Constant("ST_MultiCurve"), Expression.Constant(OgcGeometryType.MultiCurve)), + new CaseWhenClause(Expression.Constant("ST_MultiLineString"), Expression.Constant(OgcGeometryType.MultiLineString)), + new CaseWhenClause(Expression.Constant("ST_MultiPoint"), Expression.Constant(OgcGeometryType.MultiPoint)), + new CaseWhenClause(Expression.Constant("ST_MultiPolygon"), Expression.Constant(OgcGeometryType.MultiPolygon)), + new CaseWhenClause(Expression.Constant("ST_MultiSurface"), Expression.Constant(OgcGeometryType.MultiSurface)), + new CaseWhenClause(Expression.Constant("ST_Point"), Expression.Constant(OgcGeometryType.Point)), + new CaseWhenClause(Expression.Constant("ST_Polygon"), Expression.Constant(OgcGeometryType.Polygon)), + new CaseWhenClause(Expression.Constant("ST_PolyhedralSurface"), Expression.Constant(OgcGeometryType.PolyhedralSurface)), + new CaseWhenClause(Expression.Constant("ST_Tin"), Expression.Constant(OgcGeometryType.TIN)) + }; + public Expression Translate(MemberExpression e) { var declaringType = e.Member.DeclaringType; @@ -86,8 +104,13 @@ public Expression Translate(MemberExpression e) return new SqlFunctionExpression("ST_NumInteriorRings", typeof(int), new[] { e.Expression }); case "NumPoints": return new SqlFunctionExpression("ST_NumPoints", typeof(int), new[] { e.Expression }); + case "OgcGeometryType": + return new CaseExpression( + new SqlFunctionExpression("ST_GeometryType", typeof(string), new[] { e.Expression }), + _ogcGeometryTypeWhenThenList); case "PointOnSurface": - return new SqlFunctionExpression("ST_PointOnSurface", typeof(IPoint), new[] { e.Expression }); + case "InteriorPoint": + return new SqlFunctionExpression("ST_PointOnSurface", typeof(IGeometry), new[] { e.Expression }); case "SRID": return new SqlFunctionExpression("ST_SRID", typeof(int), new[] { e.Expression }); case "StartPoint": diff --git a/src/EFCore.PG.NTS/Query/ExpressionTranslators/Internal/NpgsqlNetTopologySuiteMethodCallTranslatorPlugin.cs b/src/EFCore.PG.NTS/Query/ExpressionTranslators/Internal/NpgsqlNetTopologySuiteMethodCallTranslatorPlugin.cs index 1d3a030cc..91de01ce0 100644 --- a/src/EFCore.PG.NTS/Query/ExpressionTranslators/Internal/NpgsqlNetTopologySuiteMethodCallTranslatorPlugin.cs +++ b/src/EFCore.PG.NTS/Query/ExpressionTranslators/Internal/NpgsqlNetTopologySuiteMethodCallTranslatorPlugin.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Linq.Expressions; using System.Reflection; using GeoAPI.Geometries; @@ -43,7 +44,7 @@ public virtual Expression Translate(MethodCallExpression e, IDiagnosticsLogger Task.CompletedTask; public override Task IsValid(bool isAsync) => Task.CompletedTask; public override Task Item(bool isAsync) => Task.CompletedTask; + public override Task InteriorPoint(bool isAsync) => Task.CompletedTask; public override Task LineString_Count(bool isAsync) => Task.CompletedTask; public override Task M(bool isAsync) => Task.CompletedTask; public override Task NumGeometries(bool isAsync) => Task.CompletedTask; public override Task NumInteriorRings(bool isAsync) => Task.CompletedTask; public override Task NumPoints(bool isAsync) => Task.CompletedTask; + public override Task OgcGeometryType(bool isAsync) => Task.CompletedTask; public override Task Overlaps(bool isAsync) => Task.CompletedTask; public override Task PointOnSurface(bool isAsync) => Task.CompletedTask; public override Task Relate(bool isAsync) => Task.CompletedTask; diff --git a/test/EFCore.PG.FunctionalTests/Query/SpatialQueryNpgsqlGeometryTest.cs b/test/EFCore.PG.FunctionalTests/Query/SpatialQueryNpgsqlGeometryTest.cs index a133f4c63..a53e23c72 100644 --- a/test/EFCore.PG.FunctionalTests/Query/SpatialQueryNpgsqlGeometryTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/SpatialQueryNpgsqlGeometryTest.cs @@ -59,6 +59,15 @@ public override async Task Buffer(bool isAsync) FROM ""PolygonEntity"" AS e"); } + public override async Task Buffer_quadrantSegments(bool isAsync) + { + await base.Buffer_quadrantSegments(isAsync); + + AssertSql( + @"SELECT e.""Id"", ST_Buffer(e.""Polygon"", 1.0, 8) AS ""Buffer"" +FROM ""PolygonEntity"" AS e"); + } + public override async Task Centroid(bool isAsync) { await base.Centroid(isAsync); @@ -248,6 +257,15 @@ public override async Task GetPointN(bool isAsync) FROM ""LineStringEntity"" AS e"); } + public override async Task InteriorPoint(bool isAsync) + { + await base.InteriorPoint(isAsync); + + AssertSql( + @"SELECT e.""Id"", ST_PointOnSurface(e.""Polygon"") AS ""InteriorPoint"", e.""Polygon"" +FROM ""PolygonEntity"" AS e"); + } + public override async Task Intersection(bool isAsync) { await base.Intersection(isAsync); @@ -393,6 +411,30 @@ public override async Task NumPoints(bool isAsync) FROM ""LineStringEntity"" AS e"); } + public override async Task OgcGeometryType(bool isAsync) + { + await base.OgcGeometryType(isAsync); + + AssertSql( + @"SELECT e.""Id"", CASE ST_GeometryType(e.""Point"") + WHEN 'ST_CircularString' THEN 8 + WHEN 'ST_CompoundCurve' THEN 9 + WHEN 'ST_CurvePolygon' THEN 10 + WHEN 'ST_GeometryCollection' THEN 7 + WHEN 'ST_LineString' THEN 2 + WHEN 'ST_MultiCurve' THEN 11 + WHEN 'ST_MultiLineString' THEN 5 + WHEN 'ST_MultiPoint' THEN 4 + WHEN 'ST_MultiPolygon' THEN 6 + WHEN 'ST_MultiSurface' THEN 12 + WHEN 'ST_Point' THEN 1 + WHEN 'ST_Polygon' THEN 3 + WHEN 'ST_PolyhedralSurface' THEN 15 + WHEN 'ST_Tin' THEN 16 +END AS ""OgcGeometryType"" +FROM ""PointEntity"" AS e"); + } + public override async Task Overlaps(bool isAsync) { await base.Overlaps(isAsync); @@ -508,8 +550,14 @@ 1 1 FROM ""PolygonEntity"" AS e"); } - [ConditionalTheory(Skip="ST_Union() with only one parameter is an aggregate function in PostGIS")] - public override Task Union_void(bool isAsync) => null; + public override async Task Union_void(bool isAsync) + { + await base.Union_void(isAsync); + + AssertSql( + @"SELECT e.""Id"", ST_UnaryUnion(e.""MultiLineString"") AS ""Union"" +FROM ""MultiLineStringEntity"" AS e"); + } public override async Task Within(bool isAsync) {