两个纬度/经度表之间的距离计算

Posted

技术标签:

【中文标题】两个纬度/经度表之间的距离计算【英文标题】:distance calculation between two tables of lat/lon 【发布时间】:2018-06-11 16:38:36 【问题描述】:

我有以下两张表

城市

id,纬度,经度

id,纬度,经度

SELECT cities.id, 
    (SELECT id FROM mountains 
    WHERE SQRT(POW(69.1 * ( latitude -  cities.lat ) , 2 ) + 
    POW( 69.1 * (cities.lon - longitude ) * 
    COS( latitude / 57.3 ) , 2 ) )<20 LIMIT 1) as mountain_id 
FROM cities

(查询耗时 0.5060 秒。)

为了复杂性,我删除了查询的某些部分(例如 order by、where)。但是它并不真正影响执行时间。

下面的解释

id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY cities ALL NULL NULL NULL NULL 478379
2 DEPENDENT SUBQUERY mountains ALL NULL NULL NULL NULL 15645 Using where

使用 SELECT 本身不是我的问题,但是当我尝试使用给定的结果时......例如

id mountain_id 

588437 NULL
588993 4269
589014 4201
589021 4213
589036 4952
589052 7625
589113 9235
589125 NULL
589176 1184
589210 4317

...更新表一切都变得非常缓慢。我几乎尝试了我所知道的一切。我确实知道依赖子查询不是最优的,但我不知道如何摆脱它。

有什么方法可以改进我的查询。也许将其更改为 JOIN?

这两个表本身没有任何共同点,只是纬度和经度不同,只有在使用计算时才会相互关联。

MariaDB 中的空间距离搜索(公里、英里)似乎尚不可用。

【问题讨论】:

您似乎想要彼此相距 20 英里以内的所有对(城市/山脉)。对吗? 这就是它现在所做的。 20 英里只是为了减少结果量。当我按距离添加顺序时(为了复杂性,我现在省略了)它给了我从 table1 到 table2 的最接近的匹配。 【参考方案1】:

快速进行此类操作的诀窍是避免在每对可能的纬度/经度点上进行所有计算。为此,您应该合并一个边界框操作。

让我们从使用 JOIN 开始。在伪代码中,你想要这样的东西,但如果你捕捉到一些额外的对并不重要,只要它们比其他的相距更远。

    SELECT c.city_id, m.mountain_id
      FROM cities c
      JOIN mountains m ON distance_in_miles(c, m) < 20

因此,我们需要弄清楚如何使 ON 子句快速运行——让它使用索引,而不是在所有城市和山脉中漫无目的(向 Woody Guthrie 道歉)。

让我们在 ON 子句中试试这个。它会在 +/- 20 英里的方形边界框内搜索附近的配对。

    SELECT c.city_id, m.mountain_id
      FROM cities c
      JOIN mountains m
                  ON m.lat BETWEEN c.lat - (20.0 / 69.0)
                               AND c.lat + (20.0 / 69.0)
                 AND  m.lon BETWEEN c.lon - (20.0 / (69.0 * COS(RADIANS(c.lat))))
                                AND c.lon + (20.0 / (69.0 * COS(RADIANS(c.lat))))

在此查询中,20.0 是比较限制半径,69.0 是定义每纬度法定英里数的常数。

然后,在两个表的(lat, lon, id) 上放置复合索引,您的JOIN 操作将能够使用索引范围扫描来提高查询效率。

最后,您可以在伪代码中使用这些子句来扩充查询

       ORDER BY  dist_in_miles (c,m) ASC
          LIMIT  1

这里您实际上需要使用距离公式。您问题中的笛卡尔距离公式是一个近似值,除非您靠近极点,否则效果还不错。您可能想改用大圆公式。这些被称为球余弦定律、半正弦或文森蒂公式。

【讨论】:

提前致谢!我试过你的方法。现在看起来要慢得多,但我还没有添加所有过滤器。如何过滤结果以获得每个 city_id 的结果? LIMIT 影响整个结果集。最后,我希望看到我在原始帖子中看到的列表,其中我得到一个 city_id 和一个 mountain_id。

以上是关于两个纬度/经度表之间的距离计算的主要内容,如果未能解决你的问题,请参考以下文章

计算两个纬度/经度点之间的距离不一致

如何使用它们的经度和纬度值计算两个位置之间的距离

PHP 计算两个坐标之间的距离(纬度,经度)

在 MapKit / CoreLocation 中计算两个纬度/经度之间的距离

两个纬度/经度点之间的谷歌地图距离计算?

怎么知道经纬度算距离,