mysql中的ST_Distance_Sphere没有给出两个位置之间的准确距离

Posted

技术标签:

【中文标题】mysql中的ST_Distance_Sphere没有给出两个位置之间的准确距离【英文标题】:ST_Distance_Sphere in mysql not giving accurate distance between two locations 【发布时间】:2016-06-26 16:14:18 【问题描述】:

我的requirement 是使用mysql 在给定map 上计算两个locations 之间的distance。我在mysql 中找到了一个名为ST_Distance_Sphere 的函数,它返回一个球体上两个位置和/或多个位置之间的最小球面距离(以米为单位)。

当我使用ST_Distance_Sphere 和lat_lng_distance 函数计算两个位置之间的距离时,我发现ST_Distance_Sphere 给出的距离与lat_lng_distance 函数的距离不同。

我的lat_lng_distance功能码如下

CREATE FUNCTION `lat_lng_distance` (lat1 FLOAT, lng1 FLOAT, lat2 FLOAT, lng2 FLOAT)
RETURNS FLOAT
DETERMINISTIC
BEGIN
    RETURN 6371 * 2 * ASIN(SQRT(
        POWER(SIN((lat1 - abs(lat2)) * pi()/180 / 2),
        2) + COS(lat1 * pi()/180 ) * COS(abs(lat2) *
        pi()/180) * POWER(SIN((lng1 - lng2) *
        pi()/180 / 2), 2) ));
END

传递给ST_Distance_Sphere和lat_lng_distance函数的两个位置((38.898556,-77.037852),(38.897147,-77.043934))如下

SET @pt1 = ST_GeomFromText('POINT (38.898556 -77.037852)');
SET @pt2 = ST_GeomFromText('POINT (38.897147 -77.043934 )');

SELECT ST_Distance_Sphere(@pt1, @pt2)/1000,lat_lng_distance(38.898556,-77.037852,38.897147,-77.043934 );

得到的结果如下

我在google maps上查看了两个位置之间的距离,发现lat_lng_distance与两个位置之间的实际距离很接近。有人可以告诉我为什么ST_Distance_Sphere 没有给出两个位置之间的准确距离吗?

【问题讨论】:

【参考方案1】:

ST_DISTANCE_SPHERE 要求将点表示为POINT(longitude, latitude),您在代码中将它们颠倒了

set @lat1 = 38.898556;
set @lon1 = -77.037852;
set @lat2 = 38.897147;
set @lon2 = -77.043934;
SET @pt1 = point(@lon1, @lat1);
SET @pt2 = point(@lon2, @lat2);
SELECT ST_Distance_Sphere(@pt1, @pt2)/1000,
  lat_lng_distance(@lat1,@lon1,@lat2,@lon2);

+-------------------------------------+-------------------------------------------+
| ST_Distance_Sphere(@pt1, @pt2)/1000 | lat_lng_distance(@lat1,@lon1,@lat2,@lon2) |
+-------------------------------------+-------------------------------------------+
|                   0.549154584458455 |                        0.5496311783790588 |
+-------------------------------------+-------------------------------------------+

这会得到更接近函数返回值的结果。

【讨论】:

谢谢没有注意到我的错误【参考方案2】:

首先,您不能使用默认的 SRID 0 来进行任何计算。当您使用文本函数中的几何图形时,您必须提供 4326(SRID 即度数),因为这是您的输入格式。 MYSQL 可能不关心它,但它应该这样做,因为每个严肃的 GIS 数据库都关心并要求指定输入 SRID。

第二个经度是 X,纬度是 Y(不是其他方式)

SET @pt1 = ST_GeomFromText('POINT (-77.037852 38.898556 )', 4326);
SET @pt2 = ST_GeomFromText('POINT (-77.043934 38.897147  )',4326);

最后但并非最不重要的一点是,当您计算距离时,您必须将坐标转换为您所在地区可用的本地最精确的 SRID。

例如,SRID 2877 用于美国(根据您所在的坐标)。

MYSQL ST_Distance_Sphere 函数不关心输入 SRID 并且总是以米为单位返回结果。

但这通常是不正确的,所有其他数据库都使用适用于它的指定 SRID 度量单位。

Bellow 我正在尝试做正确的事情并将 SRID 转换为 2877,如果我们将所有内容都保留为 4326(谷歌墨卡托),即使 MYSQL 也会以相同的方式工作。

对于 2877,PostGRES 将以英尺为单位返回相同查询的结果,但 MYSQL 仍然返回米。所以输出除以 1609,我们得到了大约 0.34 英里的正确结果。这是一个正确的值,因为使用不同的方法测试过

SELECT ST_Distance_Sphere(ST_GeomFromText(ST_AsText(@pt1),2877), ST_GeomFromText(ST_AsText(@pt2),2877))/1609.344;

【讨论】:

【参考方案3】:

致所有使用 MYSQL 8 的人:

对于所有 mysql 地理定位功能,必须使用正确的 SRID,否则您将无法获得正确的结果。

最常用的是 SRID 4326(GPS 坐标、Google 地球)和 SRID 3857(用于 Google 地图、OpenStreetMap 和大多数其他网络地图)。

两点之间的正确距离计算示例:

SELECT ST_Distance(ST_GeomFromText('POINT(51.513 -0.08)', 4326), ST_GeomFromText('POINT(37.745 -122.4383)', 4326)) / 1000 AS km;

下面是对这个话题的一个很好的解释: https://medium.com/maatwebsite/the-best-way-to-locate-in-mysql-8-e47a59892443

mysqlserverteam 有一些很好的解释:https://mysqlserverteam.com/spatial-reference-systems-in-mysql-8-0/ https://mysqlserverteam.com/geography-in-mysql-8-0/

【讨论】:

【参考方案4】:

顺便说一句,MySQL 在内部使用一个晦涩且过时的常量来实现这一点。所以这真的取决于你对准确的定义。

ST_Distance_Sphere

因此,从本质上讲,MySQL 中的半径是从 PostGIS 的一个惰性复制作业中提取的,该作业将半径从英里转换为米,从一个随机的 20 年前的 PostgreSQL 模块中的一个不起眼的常数。

【讨论】:

以上是关于mysql中的ST_Distance_Sphere没有给出两个位置之间的准确距离的主要内容,如果未能解决你的问题,请参考以下文章

在 MySQL 8 中使用点数据类型和 st_distance_sphere 查找最近的地方

Python中的ST_Distance_Sphere()?

PostGIS中的ST是啥?

ST_Distance_Spheroid 的 MySQL GeoSpatial 函数?返回 GLength 类型中使用的度量标准?

laravel 有:找不到列

postgrep sql几何量测量函数