【问题标题】:Inaccuracies identifying a marker inside a polygon using Google Maps and SQL Server使用 Google 地图和 SQL Server 识别多边形内的标记不准确
【发布时间】:2020-04-03 15:00:08
【问题描述】:

我在谷歌地图上画了一个多边形

new google.maps.Polygon({
    paths: [
        { lat: 52.474617867242515, lng: -1.8111903062499923 },
        { lat: 51.527748727453975, lng: -2.7340418687499923 },
        { lat: 51.41825758811742, lng: -0.40494030624999233 },
    ],
    fillColor: '#FFC107',
}).setMap(map);

我还可以单击在地图上放置新标记,将坐标传递给服务器,然后显示一个彩色标记:如果在多边形内部,则为绿色,如果在外部则为红色。

google.maps.event.addListener(drawingManager, 'overlaycomplete', (e) => {
    axios.post('/home/testmarker', e.overlay.getPosition()).then((response) => {
        e.overlay.setIcon('http://maps.google.com/mapfiles/ms/icons/' + response.data + '-dot.png');
    });
});

在我的服务器上,我检查新标记是否落在我的多边形内。最终会有很多多边形需要检查,现在我已经在存储过程中硬编码了一个多边形的副本

CREATE PROCEDURE [dbo].[TestMarker]
    @lat varchar(20),
    @lng varchar(20)
AS

DECLARE @g geography;  
DECLARE @h geography;  
SET @g = geography::STGeomFromText('POLYGON((-1.8111903062499923 52.474617867242515, -2.7340418687499923 51.527748727453975, -0.40494030624999233 51.41825758811742, -1.8111903062499923 52.474617867242515))', 4326);
SET @h = geography::STGeomFromText('POINT(' + @lng + ' ' + @lat + ')', 4326);

SELECT @g.STContains(@h);

在拐角处,我的标记被准确地识别为多边形内部或外部。

但是,双方是另一回事。在每条边的中间选取一个点,标记不再被准确地识别为多边形内部或外部。

我也尝试过使用几何数据类型,所以我的 SP 看起来像

...
DECLARE @g geometry;  
DECLARE @h geometry;  
SET @g = geometry::STGeomFromText('POLYGON((-1.8111903062499923 52.474617867242515, -2.7340418687499923 51.527748727453975, -0.40494030624999233 51.41825758811742, -1.8111903062499923 52.474617867242515))', 0);
SET @h = geometry::STGeomFromText('POINT(' + @lng + ' ' + @lat + ')', 0);
...

注意,我已将 SRID 更改为 0

边角和以前一样准确,但侧面的准确度发生了变化,底部边缘的准确度发生了显着变化

在第一张和第三张图片中,顶行标记是使用“地理”识别的先前标记,底行是使用“几何”计算的新标记。

有谁知道需要做什么来解决这个问题?

我怀疑这与带有地理的 SRID 以及 Google 地图使用的方法有关。在我的第一个示例中,我使用了似乎被广泛推荐的 4326。 I found that Google should be using this as well,虽然他们也使用名为 3857 的东西,但我在 SQL Server 中似乎没有——至少默认情况下没有。我没有以前存储的数据,因此如果需要,我可以轻松更改存储和比较数据的方式来完成这项工作。

【问题讨论】:

  • 尚未检查您的代码,但您不能在前端使用几何库和containsLocation method 进行检查吗?
  • 并在后端释放所有功能,包括地理空间索引?
  • @TomTom 他在前端使用 API 来创建或渲染标记和多边形,并且相同的 API 有一种方法来检查一个点是否位于多边形内(这似乎成为OP想要做的)。这与在后端丢失任何东西有什么关系?
  • @MrUpsidown 最终目标类似于商店定位器,因此将传入一个点(例如邮政编码),可以将其与大量可能的多边形进行比较以找到“覆盖”的记录'那一点。我认为将所有数据传回以使浏览器能够解决问题是不可行的。
  • 在您的测试中,您似乎在干扰一堆点以查看它横向移动的位置。对于误报,@g.STDistance(@h) 给出了什么?

标签: sql-server google-maps geospatial


【解决方案1】:

并且标记不再被准确识别为内部或 多边形外

...稍微缓冲多边形?

DECLARE @g geometry = 'POLYGON((1 1, 1 5, 5 5, 5 1, 1 1))'; 
SELECT @g.STBuffer(0.1).STDifference(@g);

他们还使用一种叫做 3857 的东西

declare @g geometry = geometry::STGeomFromText('POLYGON((1 1, 1 5, 5 5, 5 1, 1 1))', 3857)
select @g, @g.STSrid


declare @g geometry = 'POLYGON((1 1, 1 5, 5 5, 5 1, 1 1))', @p geometry = 'POINT(0.9999999 2.1)';
select @g.STDistance(@p), case when @g.STDistance(@p) <= 0.000001 then 1 else 0 end as tolerancecontainsYN, @g.STIntersects(@p) as intersectsYN, @g.STContains(@p) as containsYN

【讨论】:

  • 我认为这不是一个可行的解决方案,因为我似乎与有时在多边形外检测到的标记不一致(当它们不应该出现时),有时反之亦然。固定缓冲区只会导致多边形外部出现更多不正确的匹配,或者如果我做了一个负缓冲区,我可能会从多边形内部错过更多。
  • 也许使用距离而不是包含/相交的一些公差?
【解决方案2】:

原因是GEOGRAPHY类型的球面模型与谷歌地图的平面模型不一致。您在 SQLServer 中使用GEOGRAPHY 类型,它是球形的。它仅支持 SRID 4326GEOGRAPHY 也使用测地线边缘 - A 和 B 之间的线遵循 地球 上的最短路径,而不是 地图 上的直线。

首先你应该决定你真正想要的语义是什么?

如果您确实想要具有测地线边缘的多边形,则应保留GEOGRAPHY,但也应在 Google 地图中使用测地线边缘。只需将 geodesic: true 添加到 Polygon 参数即可。多边形的边缘看起来会略微弯曲,但大部分与 SQLServer 边界相匹配。我认为由于 SQLServer 和 Google 地图中的椭圆体与球体计算,可能仍存在细微差异。

如果您确实想要一个边在地图上看起来笔直的多边形,则不需要地理类型,但应该使用GEOMETRY SQLServer 类型。这应该让您可以访问地图上的各种 SRID 和平面边缘,但我不确定如何使它们与 Google 地图匹配。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-15
    相关资源
    最近更新 更多