-
Notifications
You must be signed in to change notification settings - Fork 225
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Translate spatial aggregates (#2398)
Closes #2384
- Loading branch information
Showing
5 changed files
with
135 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
94 changes: 94 additions & 0 deletions
94
...pressionTranslators/Internal/NpgsqlNetTopologySuiteAggregateMethodCallTranslatorPlugin.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
using NetTopologySuite.Algorithm; | ||
using NetTopologySuite.Geometries.Utilities; | ||
using NetTopologySuite.Operation.Union; | ||
|
||
// ReSharper disable once CheckNamespace | ||
namespace Npgsql.EntityFrameworkCore.PostgreSQL.Query.ExpressionTranslators.Internal; | ||
|
||
public class NpgsqlNetTopologySuiteAggregateMethodCallTranslatorPlugin : IAggregateMethodCallTranslatorPlugin | ||
{ | ||
public NpgsqlNetTopologySuiteAggregateMethodCallTranslatorPlugin( | ||
IRelationalTypeMappingSource typeMappingSource, | ||
ISqlExpressionFactory sqlExpressionFactory) | ||
{ | ||
if (sqlExpressionFactory is not NpgsqlSqlExpressionFactory npgsqlSqlExpressionFactory) | ||
{ | ||
throw new ArgumentException($"Must be an {nameof(NpgsqlSqlExpressionFactory)}", nameof(sqlExpressionFactory)); | ||
} | ||
|
||
Translators = new IAggregateMethodCallTranslator[] | ||
{ | ||
new NpgsqlNetTopologySuiteAggregateMethodTranslator(npgsqlSqlExpressionFactory, typeMappingSource) | ||
}; | ||
} | ||
|
||
public virtual IEnumerable<IAggregateMethodCallTranslator> Translators { get; } | ||
} | ||
|
||
public class NpgsqlNetTopologySuiteAggregateMethodTranslator : IAggregateMethodCallTranslator | ||
{ | ||
private static readonly MethodInfo GeometryCombineMethod | ||
= typeof(GeometryCombiner).GetRuntimeMethod(nameof(GeometryCombiner.Combine), new[] { typeof(IEnumerable<Geometry>) })!; | ||
|
||
private static readonly MethodInfo ConvexHullMethod | ||
= typeof(ConvexHull).GetRuntimeMethod(nameof(ConvexHull.Create), new[] { typeof(IEnumerable<Geometry>) })!; | ||
|
||
private static readonly MethodInfo UnionMethod | ||
= typeof(UnaryUnionOp).GetRuntimeMethod(nameof(UnaryUnionOp.Union), new[] { typeof(IEnumerable<Geometry>) })!; | ||
|
||
private readonly NpgsqlSqlExpressionFactory _sqlExpressionFactory; | ||
private readonly IRelationalTypeMappingSource _typeMappingSource; | ||
|
||
public NpgsqlNetTopologySuiteAggregateMethodTranslator( | ||
NpgsqlSqlExpressionFactory sqlExpressionFactory, | ||
IRelationalTypeMappingSource typeMappingSource) | ||
{ | ||
_sqlExpressionFactory = sqlExpressionFactory; | ||
_typeMappingSource = typeMappingSource; | ||
} | ||
|
||
public virtual SqlExpression? Translate( | ||
MethodInfo method, EnumerableExpression source, IReadOnlyList<SqlExpression> arguments, | ||
IDiagnosticsLogger<DbLoggerCategory.Query> logger) | ||
{ | ||
if (source.Selector is not SqlExpression sqlExpression | ||
|| (method != GeometryCombineMethod && method != UnionMethod && method != ConvexHullMethod)) | ||
{ | ||
return null; | ||
} | ||
|
||
var resultTypeMapping = _typeMappingSource.FindMapping(typeof(Geometry), sqlExpression.TypeMapping?.StoreType ?? "geometry"); | ||
|
||
if (method == GeometryCombineMethod || method == UnionMethod) | ||
{ | ||
return _sqlExpressionFactory.AggregateFunction( | ||
method == GeometryCombineMethod ? "ST_Collect" : "ST_Union", | ||
new[] { sqlExpression }, | ||
nullable: true, | ||
argumentsPropagateNullability: new[] { false }, | ||
source, | ||
typeof(Geometry), | ||
resultTypeMapping); | ||
} | ||
|
||
// PostGIS has no built-in aggregate convex hull, but we can simply apply ST_Collect beforehand as recommended in the docs | ||
// https://postgis.net/docs/ST_ConvexHull.html | ||
return _sqlExpressionFactory.Function( | ||
"ST_ConvexHull", | ||
new[] | ||
{ | ||
_sqlExpressionFactory.AggregateFunction( | ||
"ST_Collect", | ||
new[] { sqlExpression }, | ||
nullable: true, | ||
argumentsPropagateNullability: new[] { false }, | ||
source, | ||
typeof(Geometry), | ||
resultTypeMapping) | ||
}, | ||
nullable: true, | ||
argumentsPropagateNullability: new[] { true }, | ||
typeof(Geometry), | ||
resultTypeMapping); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters