SQL Server Spatial 中的 STIntersects,可能是一个错误
STIntersects in SQL Server Spatial, probably a bug
经过多次头痛和测试后,我向您展示结论。
如果我们在 SQL Server 2012(也在 2017 中测试)下的任何 SSMS 会话中执行以下脚本:
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()函数。
如有任何回复,我将不胜感激。
在 SSMS 中尝试运行:
SELECT @g UNION ALL SELECT @h
如您在 "Spatial results" 选项卡中所见,该点位于多边形内(无论使用何种投影)。
问题不在于 STIntersects() 方法,而在于 SQL 服务器用于存储地理空间数据的精度,根据 this blog
SQL Server stores geography and geometry coordinates as binary data, adhering to the IEEE-754 standard for binary floating-point arithmetic. Based on this standard, each coordinate is stored as a double-precision floating-point number that is 64 bits (8 bytes) long.
The error tolerance for the geography methods can be as large as 1.0e-7 * extents. The extents refer to the approximate maximal distance between points of the geographyobject.
使用下面的代码你可以看到精度的变化。
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
想了很多,我的结论是投影的问题
在地理中用于操作和图形表示。如果我们用几何来解决问题,结果就不同了。你可以在这里看到:
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
希望对大家有用。
经过多次头痛和测试后,我向您展示结论。
如果我们在 SQL Server 2012(也在 2017 中测试)下的任何 SSMS 会话中执行以下脚本:
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()函数。
如有任何回复,我将不胜感激。
在 SSMS 中尝试运行:
SELECT @g UNION ALL SELECT @h
如您在 "Spatial results" 选项卡中所见,该点位于多边形内(无论使用何种投影)。
问题不在于 STIntersects() 方法,而在于 SQL 服务器用于存储地理空间数据的精度,根据 this blog
SQL Server stores geography and geometry coordinates as binary data, adhering to the IEEE-754 standard for binary floating-point arithmetic. Based on this standard, each coordinate is stored as a double-precision floating-point number that is 64 bits (8 bytes) long.
The error tolerance for the geography methods can be as large as 1.0e-7 * extents. The extents refer to the approximate maximal distance between points of the geographyobject.
使用下面的代码你可以看到精度的变化。
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
想了很多,我的结论是投影的问题 在地理中用于操作和图形表示。如果我们用几何来解决问题,结果就不同了。你可以在这里看到:
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
希望对大家有用。