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

时间:2018-04-04 10:07:48

标签: sql sql-server tsql spatial

经过多次头痛和考验后,我向您展示了结论。

如果我们在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服务器中存在错误的可能性。这让我非常不信任我在代码中无数个地方使用的STIntersects()函数。

我很感激任何回应。

3 个答案:

答案 0 :(得分: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

我希望它对某人有用。

答案 1 :(得分:0)

尝试在SSMS中运行它:

SELECT @g UNION ALL SELECT @h

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

答案 2 :(得分:0)

问题不在于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