如何在不使用几何类型的情况下为 MySQL 中的计算距离获得更高的精度?

How to get more precision for a computed distance in MySQL, without using geometric types?

我必须计算一个对象(城市)与我拥有的 MySQL table 中的每个条目(一些餐馆)之间的距离。这个城市和餐厅位于同一个国家。

计算的距离用于显示靠近该城市的所有餐馆;阈值距离是任意的。 此外,这是一个排名列表:最近的餐厅最先显示,最远的餐厅显示在列表末尾。我的问题是关于这个排名。

我现在做了什么

所以我做了一些研究,我成功地计算了这个距离。

    $special_select_distance = "DEGREES(ACOS(COS(RADIANS(" . $oneVilles->__get('latitude')[app::getLang()] . ")) * COS(RADIANS(lat)) * COS(RADIANS(lon) - RADIANS(" . $oneVilles->__get('longitude')[app::getLang()] . ")) + SIN(RADIANS(" . $oneVilles->__get('latitude')[app::getLang()] . ")) * SIN(RADIANS(lat))))";

    $restaurants = $restaurantsDAO->getAll(null, ['distance DESC'] , null, 'HAVING distance < 1.9' , null , '*, ' . $special_select_distance . " AS distance");

... 其中:

  1. ['distance DESC']代表距离排名

  2. 'HAVING distance < 1.9'代表任意阈值

  3. '*, ' . $special_select_distance . " AS distance"是选择器

  4. $oneVilles->__get('latitude')[app::getLang()]$oneVilles->__get('longitude')[app::getLang()]是城市坐标经纬度

  5. latlon 是餐厅的坐标(自动带入我们正在迭代的 table, 即: restaurants table, 因为我们使用餐厅 DAO)

问题

实际和意外结果

对于彼此距离很近的每家餐馆,计算出的与城市的距离保持不变。

示例:假设餐厅 A 和 B 很近。那么,A到城市的距离和B到城市的距离是一样的,这是我实际出乎意料的结果。

这不是我想要的。 事实上,实际上这些餐厅中的一家比另一家离城市最近。我认为 MySQL.

中的精度不够

预期结果

预期结果:根据上班城市的远近对餐厅进行排名。换句话说,获得更精确的计算距离。

示例:假设餐厅 A 和 B 很近。然后,A到城市的距离比B到城市的距离短,这是我的预期结果。

计算距离的示例

  1. 餐厅与城市之间(餐厅远离城市):1.933156948976873

  2. 餐厅A与市区之间(A靠近市区):1.6054631070094885

  3. 餐厅B和城市之间(B靠近A):1.6054631070094885

第2点和第3点的距离是一样的,不正常。我想要更多的数字,以便能够更有效地对我的餐厅进行排名。

约束条件

对于长距离,使用 Haversine 公式来确保准确性。对于短距离,毕达哥拉斯的速度是它的两倍。

16 位有效数字(数据类型DOUBLE)是可笑的。你不需要区分你的狗身上的两种不同的跳蚤。

对于毕达哥拉斯,一定要用经度除以纬度的余弦 -- 赫尔辛基附近的经度是赤道的经度的一半。

这里有更多详细信息:http://mysql.rjweb.org/doc.php/latlng

如果1.6054631070094885是纬度差异,那么这样想:如果你我处在同一经度,但我们的纬度分别是1.605463和1.605464,那么,嗯,我不知道你很好关闭。

在没有软糖因素的情况下比较两个浮点值是不切实际的:

If abs(a-b) < 0.00001, then treat them as equal.

更多

我推荐 FLOAT 用于纬度、经度和距离,因为你在谈论餐馆。如果您说的不是超过 100 英里或 100 公里,那么这个表达式就足够精确了:

SQRT(  ($lat - lat) *
       ($lat - lat) +
      (($lng - lng) * COS(RADIANS(lat))) *
      (($lng - lng) * COS(RADIANS(lat))) )  * $factor

在哪里...

  • latlng 是 table 中 FLOAT 列的名称,以度为单位。
  • $lat$lng 是您起始位置的值,也是以度为单位。 (PHP 使用 $;其他语言使用其他约定。)
  • $factor 是 69.172 英里或 111.325 公里。
  • 我不会显示超过 1 位小数的结果。 (不要显示“12.345678 英里”;“12.3 英里”就足够了。)

毕达哥拉斯与GCD的比较:

             Pyt        GCD
To Rennes:  93.9407    93.6542
To Vannes:  95.6244    95.6241