我有一个问题,我希望你们能帮助我解决。
我有一个DbGeometry点(或DbGeography,我可以同时使用),我想检查,如果这是一个DbGeometry多边形(或者一个DbGeography)内。
我目前做这个:
var dbZones = new List<WasteManager.Database.Zone>();
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实体),并检查这一点我已经是这个边界内。
问题是,它不返回任何结果,但我知道这一点是边界内,因为我手动创建边界和点是边界内。
谁能告诉我,如果我在做什么是错的,如果有另一种方式做到这一点或任何其他?
非常感谢。
曼努埃尔
我想添加评论尼克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可能会导致geometry实例的点略微偏移。
但是,如果我们把这个不会发生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);
}
对于那些使用实体框架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:
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