SQL Server Spatial 中的 STIntersects,可能是一个错误

Posted

技术标签:

【中文标题】SQL Server Spatial 中的 STIntersects,可能是一个错误【英文标题】:STIntersects in SQL Server Spatial, probably a bug 【发布时间】:2018-04-04 10:07:48 【问题描述】:

经过多次头痛和测试,我向您展示了结论。

如果我们在 SQL Server 2012 下的任何 SSMS 会话中执行以下脚本(也在 2017 年测试过):

DECLARE @g geography;
DECLARE @h geography;
SET @g = geography::STPolyFromText('POLYGON ((-2.2141931466053393 36.848142880725426, -2.1632066297350296 36.864255247830073, 3.0526676842932088 39.266689645726004, 3.168352172454167 39.329935703712941, 3.2286305251469463 39.370418565526464, 3.2322979289615716 39.374091534163213, 3.2372882795963895 39.379457236292687, 3.2583498367577581 39.409984643625563, 3.3506438583660594 39.556107032723332, 3.4300534340816529 39.699235599046659, 3.2674327158297669 42.289964571287427, 3.1599698848294775 42.435144600606137, 0.79329389164208441 42.99441296630598, -7.6685442882152826 43.77780739075277, -8.0476876236197672 43.711457921649725, -9.2115225666636089 43.160194190763086, -9.2984566034556444 43.054102468725411, -9.2715498704469024 42.881894543711283, -7.5169998811285126 37.556527784974641, -7.4389506340710883 37.345028476444256, -7.4300533647696216 37.33933723597093, -7.426814647077407 37.337972629773219, -7.4171018386796526 37.336039770123939, -7.36341431379385 37.33029040340196, -4.5559445740609537 37.01597060730704, -2.2141931466053393 36.848142880725426))', 4326);
SET @h = geography::STPointFromText('POINT (-5.7805724666961673 43.604738856455796)', 4326);
select @g.STIntersects(@h)

我们得到结果 1。这意味着几何相交。这是不正确的,如果我们在 GIS 工具(例如 ArcMap、QGIS)中表示几何图形,或者在 https://clydedacruz.github.io/openstreetmap-wkt-playground/ 等网站中进行可视化,您可以清楚地看到。

这不仅仅发生在这一点上,他们可以与其他人一起测试,例如:

POINT (-5.7808907869201684 43.607612302768608)
POINT (-5.7867532730156022 43.607109291914668)
POINT (-5.7910420343533673 43.607409757130171)
POINT (-5.7962209295114038 43.605527381457819)
POINT (-5.8379991303640395 43.609944466702757)
POINT (-5.8372379698022909 43.613519832305009)
POINT (-5.8339925740272829 43.616976768767834)
POINT (-5.832657139630153 43.620206197447274)
POINT (-5.827899502105284 43.624465756821465)
POINT (-5.8230287455979495 43.6276474699738)

澄清这不是环的方向问题。其他内部点显示为相交。

我没有找到解释,只是 SQL server 中存在错误的可能性。这让我非常不信任我在代码中无数地方使用的 STIntersects() 函数。

如有任何回应,我将不胜感激。

【问题讨论】:

由于点如此接近多边形的 ,您必须考虑多边形的连续点之间的线是否是“直线”(无论何时可能意味着我们正在处理一个球体)或一个大圆的弧。 也许差异(与您正在使用的其他工具)来自不同的 SRID? 我对这个问题做了很多修改,但没有比@Damien_The_Unbeliever 给出的更好的解释。据我通过空间查询得知,这些点距离多边形的边界大约 5 公里。 @RazvanSocol:我上面提到的部分修改是解释 SQL 知道的所有 SRID 中的所有点和多边形。它们显示交集。但伟大的思想是一样的! :) 【参考方案1】:

经过深思熟虑,我的结论是问题出在投影上 在地理中用于操作和图形表示。如果我们用几何来解决问题,结果就会不同。你可以在这里看到:

DECLARE @g geometry;
DECLARE @h geometry;
SET @g = geometry::STPolyFromText('POLYGON ((-2.2141931466053393 36.848142880725426, -2.1632066297350296 36.864255247830073, 3.0526676842932088 39.266689645726004, 3.168352172454167 39.329935703712941, 3.2286305251469463 39.370418565526464, 3.2322979289615716 39.374091534163213, 3.2372882795963895 39.379457236292687, 3.2583498367577581 39.409984643625563, 3.3506438583660594 39.556107032723332, 3.4300534340816529 39.699235599046659, 3.2674327158297669 42.289964571287427, 3.1599698848294775 42.435144600606137, 0.79329389164208441 42.99441296630598, -7.6685442882152826 43.77780739075277, -8.0476876236197672 43.711457921649725, -9.2115225666636089 43.160194190763086, -9.2984566034556444 43.054102468725411, -9.2715498704469024 42.881894543711283, -7.5169998811285126 37.556527784974641, -7.4389506340710883 37.345028476444256, -7.4300533647696216 37.33933723597093, -7.426814647077407 37.337972629773219, -7.4171018386796526 37.336039770123939, -7.36341431379385 37.33029040340196, -4.5559445740609537 37.01597060730704, -2.2141931466053393 36.848142880725426))', 4326);
SET @h = geometry::STPointFromText('POINT (-5.7805724666961673 43.604738856455796)', 4326);
select @g.STIntersects(@h)
select @g union all select @h

我希望它对某人有用。

【讨论】:

【参考方案2】:

问题不在于 STIntersects() 方法,而在于 SQL Server 用于存储地理空间数据的精度,this blog

SQL Server 将地理和几何坐标存储为二进制数据,遵循二进制浮点运算的 IEEE-754 标准。基于此标准,每个坐标都存储为一个 64 位(8 字节)长的双精度浮点数。

根据Microsoft Docs

地理方法的误差容限可以大到 1.0e-7 * 范围。范围是指地理对象的点之间的近似最大距离。

使用下面的代码,您可以看到精度变化。

DECLARE @p1 geography;
DECLARE @p2 geography;
DECLARE @h geography;
SET @p1 = geography::STPointFromText('POINT (-7.6685442882152826 43.77780739075277)', 4326);
SET @p2 = geography::STPointFromText('POINT (0.79329389164208441 42.99441296630598)', 4326);
SET @h = geography::STPointFromText('POINT (-5.7805724666961673 43.604738856455796)', 4326);

select @p1.Lat, @p1.Long
select @p2.Lat, @p2.Long
select @h.Lat, @h.Long

【讨论】:

您已经做了一个很好的观察,但是以这种精度,该点继续在外面,STIntersects 表明它在里面。该精度的公差低于厘米,并且该点在米之外。【参考方案3】:

尝试在 SSMS 中运行:

SELECT @g UNION ALL SELECT @h

正如您在“空间结果”选项卡中看到的那样,该点位于多边形内部(与使用的投影无关)。

【讨论】:

这就是问题所在。实际上,该点不在多边形内部。您可以在clydedacruz.github.io/openstreetmap-wkt-playground 或QGIS 等其他地方绘制形状,您可以看到它实际上不在里面。您可以使用下一个 WKT 来查看它,使用 GEOMETRYCOLLECTION(POLYGON(...),POINT()): 我会说这是 clydedacruz 网站中的一个错误。试试这个:gcmap.com/… 它也在 QGIS 和 ArcGIS Pro 中进行了测试。它看起来更像是 SQL Server 中的一个错误。

以上是关于SQL Server Spatial 中的 STIntersects,可能是一个错误的主要内容,如果未能解决你的问题,请参考以下文章

将 sql server spatial 和 NHibernate 与 CreateSqlQuery 相结合

将有效的几何形状保存到 Sql Server 2008 地理列中

SQL Server笔记2

sql spatial.sql

sql oracle spatial表供geoserver使用

NHibernate.Spatial 和 Sql 2008 地理类型 - 如何配置