检查dbgeometry dbgeometry / dbgeography点是否在多边形内

我有一个问题,希望你们能帮助我解决。

我有一个DbGeometry点(或DbGeography,我可以使用两者),我想检查它是否在DbGeometry Polygon(或者DbGeography)中。

我现在这样做:

var dbZones = new List(); foreach(var zone in zones) { var res = from z in DatabaseContext.Zones let boundary = !z.BoundaryGeometry.IsValid ? SqlSpatialFunctions.MakeValid(z.BoundaryGeometry) : z.BoundaryGeometry where z.ID == zone.ID && point.Within(boundary) select z; if(res.FirstOrDefault() != null) dbZones.Add(res.FirstOrDefault()); } 

所以我遍历区域(我的数据库的EF实体)并检查我的这个点是否在这个边界内。

问题是它没有返回任何结果,但我知道那个点在该边界内,因为我手动创建了边界,并且该点位于该边界内。

任何人都可以告诉我,如果有其他方法可以做到这一点或其他什么,我做错了什么?

非常感谢。

曼努埃尔

我想对Nick Strupat添加评论。

你应该小心戒指。 SQL Server使用左手方向,这意味着如果沿着多边形的周边行走,左手应位于多边形的内侧,右手应位于外侧(逆时针或逆时针)。 我得到了“环形方向”错误,因为我在相反方向(顺时针或右手)绘制了多边形,这意味着SQL Server正在处理地球的整个表面,除了我的多边形作为多边形的区域。

要检查点是否在多边形中,您应该始终使用point.Intersects(polygon)而不是!point.Intersects(polygon)

有一个解决方案,通过检查区域的大小来检查您的多边形是否正常,有关更多信息,请访问:

https://blog.falafel.com/ring-orientation-sql-spatial/

这是我基于博客解释的代码:

  private 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); // If the polygon area is larger than an earth hemisphere (510 Trillion m2 / 2), we know it needs to be fixed if (polygon.Area.HasValue && polygon.Area.Value > 255000000000000L) { // Convert our DbGeography polygon into a SqlGeography object for the ReorientObject() call SqlGeography sqlPolygon = SqlGeography.STGeomFromWKB(new System.Data.SqlTypes.SqlBytes(polygon.AsBinary()), DbGeography.DefaultCoordinateSystemId); // ReorientObject will flip the polygon so the outside becomes the inside sqlPolygon = sqlPolygon.ReorientObject(); // Convert the SqlGeography object back into DbGeography polygon = DbGeography.FromBinary(sqlPolygon.STAsBinary().Value); } return point.Intersects(polygon); } 

实际上这很容易。

 bool isInside(DbGeometry polygon, double longitude, double latitude) //or DbGeography in your case { DbGeometry point = DbGeometry.FromText(string.Format("POINT({0} {1})",longitude, latitude), 4326); return polygon.Contains(point); } 

我还想对@BenoitGlaizette添加评论。

代码polygon.Area.HasValue可能会为某些Multipolygons引发以下错误。

ArgumentException:24144:由于实例无效,无法完成此操作。 使用MakeValid将实例转换为有效的实例。 请注意,MakeValid可能会导致几何实例的点稍微移动。

但是,如果我们直接转换为SqlGeography则不会发生这种情况。

 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的人<:

我使用此扩展方法检查每个PolygonMultipolygon然后将它们保存到数据库。

 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; } 

entity framework生成的T-SQL:

 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