DbGeography 相交方法不起作用
DbGeography Intersects method not working
System.Data.Spatial.DbGeography.Intersects 方法对我来说似乎总是 return 正确。我不确定为什么会这样。我在下面创建了一个简单的命令行片段,导致下面的控制台输出
Intersects
Intersects
该点显然不在边界附近,因此不应相交。
DbGeography bounds = DbGeography.PolygonFromText("POLYGON ((146 -20,148 -20,148 -22,146 -22,146 -20))", 4326);
DbGeography point = DbGeography.PointFromText("POINT (0 0)", 4326);
if (point.Intersects(bounds) == true)
Console.WriteLine("Intersects");
else
Console.WriteLine("Does NOT intersect");
if (bounds.Intersects(point) == true)
Console.WriteLine("Intersects");
else
Console.WriteLine("Does NOT intersect");
The point clearly is nowhere near the bounds, and therefore should not intersect.
有个规矩:一说"clearly",就准备说错。 :)
开个玩笑,你有一个戒指方向问题。也就是说,指定点的顺序很重要。当您指定角落时,您已经定义了一个区域,该区域是整个地球,其中有一个非常小的洞。试试用这个代替:
POLYGON ((146 -20,146 -22,148 -22,148 -20,146 -20))
那么,您如何从本质上知道您有定向问题?我喜欢使用的一种启发式方法是,如果对象的包络角很大(90 度 = 一个半球),那么您指定的顺序不正确。数据库引擎中的 Geography 数据类型有一个 EnvelopeAngle
方法(但在 C# 中的 DbGeography class 中似乎没有)来确定这一点。还有一个方便的方法(再次确定在 DB 但不是在 C# 中)用于重新定向调用的环,不足为奇 ReorientObject
.
如前所述,这是环方向错误。对于那些使用 SQL Server
的人 - 它使用 left-handed 方向,这意味着如果你沿着多边形的周边行走,你的左手应该在多边形的内侧,你的右手应该在多边形的内侧外部(counter-clockwise 或 anti-clockwise)。我什至从政府数据中得到了“环方向”错误,因为多边形是在相反方向绘制的(顺时针,或 right-handed),这意味着 SQL 服务器正在处理地球的整个表面 EXCEPT FOR 我的多边形作为多边形的面积。
这是一种自动修复此问题的方法:
public bool IsInside(DbGeography polygon, double longitude, double latitude)
{
DbGeography point = DbGeography.FromText(string.Format("POINT({1} {0})", latitude.ToString().Replace(',', '.'), longitude.ToString().Replace(',', '.')), DbGeography.DefaultCoordinateSystemId);
var wellKnownText = polygon.AsText();
var sqlGeography =
SqlGeography.STGeomFromText(new SqlChars(wellKnownText), DbGeography.DefaultCoordinateSystemId)
.MakeValid();
//Now get the inversion of the above area
var invertedSqlGeography = sqlGeography.ReorientObject();
//Whichever of these is smaller is the enclosed polygon, so we use that one.
if (sqlGeography.STArea() > invertedSqlGeography.STArea())
{
sqlGeography = invertedSqlGeography;
}
polygon = DbSpatialServices.Default.GeographyFromProviderValue(sqlGeography);
return point.Intersects(polygon);
}
对于那些使用 Entity Framework 5<:
我使用此扩展方法检查每个 Polygon
和 Multipolygon
,然后再将它们保存到数据库。
public static DbGeography MakePolygonValid(this DbGeography geom)
{
var wellKnownText = geom.AsText();
//First, get the area defined by the well-known text using left-hand rule
var sqlGeography =
SqlGeography.STGeomFromText(new SqlChars(wellKnownText), DbGeography.DefaultCoordinateSystemId)
.MakeValid();
//Now get the inversion of the above area
var invertedSqlGeography = sqlGeography.ReorientObject();
//Whichever of these is smaller is the enclosed polygon, so we use that one.
if (sqlGeography.STArea() > invertedSqlGeography.STArea())
{
sqlGeography = invertedSqlGeography;
}
return DbSpatialServices.Default.GeographyFromProviderValue(sqlGeography);
}
然后我可以使用这样的方法在数据库级别检查 Intersects
。
public static class GeoHelper
{
public const int SridGoogleMaps = 4326;
public const int SridCustomMap = 3857;
public static DbGeography PointFromLatLng(double lat, double lng)
{
return DbGeography.PointFromText(
"POINT("
+ lng.ToString(CultureInfo.InvariantCulture) + " "
+ lat.ToString(CultureInfo.InvariantCulture) + ")",
SridGoogleMaps);
}
}
public County GetCurrentCounty(double latitude, double longitude)
{
var point = GeoHelper.PointFromLatLng(latitude, longitude);
var county = db.Counties.FirstOrDefault(x =>
x.Area.Intersects(point));
return county;
}
T-SQL 由 Entity Framework:
生成
SELECT TOP (1)
[Extent1].[Id] AS [Id],
[Extent1].[Name] AS [Name],
[Extent1].[Code] AS [Code],
[Extent1].[Area] AS [Area]
FROM [Election].[County] AS [Extent1]
WHERE ([Extent1].[Area].STIntersects(@p__linq__0)) = 1
-- p__linq__0: 'POINT (10.0000000 32.0000000)' (Type = Object)
可以这样手动测试:
declare @p__linq__0 varchar(max)
set @p__linq__0 = 'POINT (10.0000000 32.0000000)'
SELECT TOP (1)
[Extent1].[Id] AS [Id],
[Extent1].[Name] AS [Name],
[Extent1].[Code] AS [Code],
[Extent1].[Area] AS [Area]
FROM [Election].[County] AS [Extent1]
WHERE ([Extent1].[Area].STIntersects(@p__linq__0)) = 1
可在此处找到更多信息:
https://docs.microsoft.com/en-us/sql/t-sql/spatial-geometry/stintersects-geometry-data-type
来自该线程的来源:
Check if dbgeometry dbgeometry/dbgeography point is within a polygon
System.Data.Spatial.DbGeography.Intersects 方法对我来说似乎总是 return 正确。我不确定为什么会这样。我在下面创建了一个简单的命令行片段,导致下面的控制台输出
Intersects
Intersects
该点显然不在边界附近,因此不应相交。
DbGeography bounds = DbGeography.PolygonFromText("POLYGON ((146 -20,148 -20,148 -22,146 -22,146 -20))", 4326);
DbGeography point = DbGeography.PointFromText("POINT (0 0)", 4326);
if (point.Intersects(bounds) == true)
Console.WriteLine("Intersects");
else
Console.WriteLine("Does NOT intersect");
if (bounds.Intersects(point) == true)
Console.WriteLine("Intersects");
else
Console.WriteLine("Does NOT intersect");
The point clearly is nowhere near the bounds, and therefore should not intersect.
有个规矩:一说"clearly",就准备说错。 :)
开个玩笑,你有一个戒指方向问题。也就是说,指定点的顺序很重要。当您指定角落时,您已经定义了一个区域,该区域是整个地球,其中有一个非常小的洞。试试用这个代替:
POLYGON ((146 -20,146 -22,148 -22,148 -20,146 -20))
那么,您如何从本质上知道您有定向问题?我喜欢使用的一种启发式方法是,如果对象的包络角很大(90 度 = 一个半球),那么您指定的顺序不正确。数据库引擎中的 Geography 数据类型有一个 EnvelopeAngle
方法(但在 C# 中的 DbGeography class 中似乎没有)来确定这一点。还有一个方便的方法(再次确定在 DB 但不是在 C# 中)用于重新定向调用的环,不足为奇 ReorientObject
.
如前所述,这是环方向错误。对于那些使用 SQL Server
的人 - 它使用 left-handed 方向,这意味着如果你沿着多边形的周边行走,你的左手应该在多边形的内侧,你的右手应该在多边形的内侧外部(counter-clockwise 或 anti-clockwise)。我什至从政府数据中得到了“环方向”错误,因为多边形是在相反方向绘制的(顺时针,或 right-handed),这意味着 SQL 服务器正在处理地球的整个表面 EXCEPT FOR 我的多边形作为多边形的面积。
这是一种自动修复此问题的方法:
public bool IsInside(DbGeography polygon, double longitude, double latitude)
{
DbGeography point = DbGeography.FromText(string.Format("POINT({1} {0})", latitude.ToString().Replace(',', '.'), longitude.ToString().Replace(',', '.')), DbGeography.DefaultCoordinateSystemId);
var wellKnownText = polygon.AsText();
var sqlGeography =
SqlGeography.STGeomFromText(new SqlChars(wellKnownText), DbGeography.DefaultCoordinateSystemId)
.MakeValid();
//Now get the inversion of the above area
var invertedSqlGeography = sqlGeography.ReorientObject();
//Whichever of these is smaller is the enclosed polygon, so we use that one.
if (sqlGeography.STArea() > invertedSqlGeography.STArea())
{
sqlGeography = invertedSqlGeography;
}
polygon = DbSpatialServices.Default.GeographyFromProviderValue(sqlGeography);
return point.Intersects(polygon);
}
对于那些使用 Entity Framework 5<:
我使用此扩展方法检查每个 Polygon
和 Multipolygon
,然后再将它们保存到数据库。
public static DbGeography MakePolygonValid(this DbGeography geom)
{
var wellKnownText = geom.AsText();
//First, get the area defined by the well-known text using left-hand rule
var sqlGeography =
SqlGeography.STGeomFromText(new SqlChars(wellKnownText), DbGeography.DefaultCoordinateSystemId)
.MakeValid();
//Now get the inversion of the above area
var invertedSqlGeography = sqlGeography.ReorientObject();
//Whichever of these is smaller is the enclosed polygon, so we use that one.
if (sqlGeography.STArea() > invertedSqlGeography.STArea())
{
sqlGeography = invertedSqlGeography;
}
return DbSpatialServices.Default.GeographyFromProviderValue(sqlGeography);
}
然后我可以使用这样的方法在数据库级别检查 Intersects
。
public static class GeoHelper
{
public const int SridGoogleMaps = 4326;
public const int SridCustomMap = 3857;
public static DbGeography PointFromLatLng(double lat, double lng)
{
return DbGeography.PointFromText(
"POINT("
+ lng.ToString(CultureInfo.InvariantCulture) + " "
+ lat.ToString(CultureInfo.InvariantCulture) + ")",
SridGoogleMaps);
}
}
public County GetCurrentCounty(double latitude, double longitude)
{
var point = GeoHelper.PointFromLatLng(latitude, longitude);
var county = db.Counties.FirstOrDefault(x =>
x.Area.Intersects(point));
return county;
}
T-SQL 由 Entity Framework:
生成SELECT TOP (1)
[Extent1].[Id] AS [Id],
[Extent1].[Name] AS [Name],
[Extent1].[Code] AS [Code],
[Extent1].[Area] AS [Area]
FROM [Election].[County] AS [Extent1]
WHERE ([Extent1].[Area].STIntersects(@p__linq__0)) = 1
-- p__linq__0: 'POINT (10.0000000 32.0000000)' (Type = Object)
可以这样手动测试:
declare @p__linq__0 varchar(max)
set @p__linq__0 = 'POINT (10.0000000 32.0000000)'
SELECT TOP (1)
[Extent1].[Id] AS [Id],
[Extent1].[Name] AS [Name],
[Extent1].[Code] AS [Code],
[Extent1].[Area] AS [Area]
FROM [Election].[County] AS [Extent1]
WHERE ([Extent1].[Area].STIntersects(@p__linq__0)) = 1
可在此处找到更多信息:
https://docs.microsoft.com/en-us/sql/t-sql/spatial-geometry/stintersects-geometry-data-type
来自该线程的来源:
Check if dbgeometry dbgeometry/dbgeography point is within a polygon