在mysql中使用纬度和经度查找两点之间的距离

xmjla07d  于 2022-10-31  发布在  Mysql
关注(0)|答案(8)|浏览(227)

您好,我有以下表格

--------------------------------------------
 |  id  |  city  |  Latitude  |  Longitude  |
 --------------------------------------------
 |  1   |   3    |   34.44444 |   84.3434   |
 --------------------------------------------
 |  2   |   4    | 42.4666667 | 1.4666667   |
 --------------------------------------------
 |  3   |   5    |  32.534167 | 66.078056   |
 --------------------------------------------
 |  4   |   6    |  36.948889 | 66.328611   |
 --------------------------------------------
 |  5   |   7    |  35.088056 | 69.046389   |
 --------------------------------------------
 |  6   |   8    |  36.083056 |   69.0525   |
 --------------------------------------------
 |  7   |   9    |  31.015833 | 61.860278   |
 --------------------------------------------

现在我想得到两点之间的距离。假设一个用户有一个城市3,一个用户有一个城市7。我的场景是一个用户有一个城市和纬度和经度正在搜索其他用户离他的城市的距离。例如,用户有城市3正在搜索。他想得到任何其他城市的用户的距离,假设它是7。我已经搜索并找到了以下查询

SELECT `locations`.`city`, ( 3959 * acos ( cos ( radians(31.589167) ) * cos( radians( Latitude ) ) * cos( radians( Longitude ) - radians(64.363333) ) + sin ( radians(31.589167) ) * sin( radians( Latitude ) ) ) ) AS `distance` FROM `locations` HAVING (distance < 50)

据我所知,这个查询是求从一点到所有其他点的距离。现在我想得到从一点到其他点的距离。
任何指导方针都将不胜感激。

jhdbpxl9

jhdbpxl91#

我想你的问题是说你有两个城市的city值,你想计算这两个城市之间的距离。
下面的查询将为您完成这项工作,生成以km为单位的距离。它使用球面余弦定律公式。
请注意,您将表格与其自身关联,以便可以检索两个坐标对以进行计算。

SELECT a.city AS from_city, b.city AS to_city, 
   111.111 *
    DEGREES(ACOS(LEAST(1.0, COS(RADIANS(a.Latitude))
         * COS(RADIANS(b.Latitude))
         * COS(RADIANS(a.Longitude - b.Longitude))
         + SIN(RADIANS(a.Latitude))
         * SIN(RADIANS(b.Latitude))))) AS distance_in_km
  FROM city AS a
  JOIN city AS b ON a.id <> b.id
 WHERE a.city = 3 AND b.city = 7

请注意,常数111.1111是每度纬度的公里数,它基于拿破仑对米的旧定义,即从赤道到极点的距离的万分之一,这个定义对于定位器来说已经足够接近了。
如果您需要法定英里而不是公里,请使用69.0
http://sqlfiddle.com/#!9/21e06/412/0
如果你正在寻找附近的点,你可能会使用类似这样的子句:

HAVING distance_in_km < 10.0    /* slow ! */
    ORDER BY distance_in_km DESC

这是(正如我们所说的美国马萨诸塞州波士顿附近)邪恶的缓慢。
在这种情况下,你需要使用一个边界框计算。关于如何做到这一点,请参阅本文。http://www.plumislandmedia.net/mysql/haversine-mysql-nearest-loc/
公式中包含一个LEAST()函数。为什么?因为ACOS()函数在其参数稍大于1时就会抛出错误。当两个点非常接近时,由于x1E1F1X,具有x1M5N1X和x1M6N1X计算的表达式有时可产生略大于1的值)。LEAST(1.0, dirty-great-expression)调用解决了这个问题。
有一个更好的方法,formulaThaddeus Vincenty,它使用ATAN2()而不是ACOS(),所以它不太容易受到epsilon问题的影响。

编辑2022(作者:Alexio Vay):到目前为止,现代的解决方案应该是下面的短代码:

select ST_Distance_Sphere(
    point(-87.6770458, 41.9631174),
    point(-73.9898293, 40.7628267))

请查看Naresh Kumar的答案。

b09cbbtk

b09cbbtk2#

您可以使用MySQL内置函数ST_Distance_Sphere(),该函数自MySQL 5.7及以上版本起支持。它可以更有效地计算距离(以米为单位)。

select ST_Distance_Sphere(point(lng, lat), point(lng,lat))

也就是说

select ST_Distance_Sphere(
        point(-87.6770458, 41.9631174),
        point(-73.9898293, 40.7628267)
    )

引用自Calculating distance using MySQL

kr98yfug

kr98yfug3#

这里是MySQL查询和函数,用于获取两个纬度和经度之间的距离,距离将以KM返回。
Mysql查询:-

SELECT (6371 * acos( 
                cos( radians(lat2) ) 
              * cos( radians( lat1 ) ) 
              * cos( radians( lng1 ) - radians(lng2) ) 
              + sin( radians(lat2) ) 
              * sin( radians( lat1 ) )
        ) ) as distance 
FROM your_table;

Mysql函数:-

DELIMITER $$
CREATE FUNCTION `getDistance`(`lat1` VARCHAR(200), `lng1` VARCHAR(200), `lat2` VARCHAR(200), `lng2` VARCHAR(200)) RETURNS varchar(10) CHARSET utf8
begin
declare distance varchar(10);

set distance = (select (6371 * acos( 
                                     cos( radians(lat2) ) 
                                   * cos( radians( lat1 ) ) 
                                   * cos( radians( lng1 ) - radians(lng2) )       
                                   + sin( radians(lat2) ) 
                                   * sin( radians( lat1 ) )
                ) ) as distance); 

if(distance is null)
then
 return '';
else 
return distance;
end if;
end$$
DELIMITER ;

如何在PHP代码中使用

SELECT getDistance($lat1,$lng1,$lat2,$lng2) as distance 
FROM your_table.
vsdwdz23

vsdwdz234#

下面是一个MySQL函数,它将获取两个纬度/经度对,并以度为单位给予两点之间的距离。它使用半正矢公式来计算距离。由于地球不是一个完美的球体,因此在两极和赤道附近存在一些误差。

  • 要转换为英里,请乘以3961。
  • 要转换为公里,请乘以6373。
  • 要转换为米,请乘以6373000。
  • 若要转换为英尺,请乘以(3961 * 5280)20914080。
DELIMITER $$

CREATE FUNCTION \`haversine\`(

        lat1 FLOAT, lon1 FLOAT,
        lat2 FLOAT, lon2 FLOAT
     ) RETURNS float
    NO SQL
    DETERMINISTIC
    COMMENT 'Returns the distance in degrees on the Earth between two known points of latitude and longitude. To get miles, multiply by 3961, and km by 6373'

BEGIN

    RETURN DEGREES(ACOS(
              COS(RADIANS(lat1)) *
              COS(RADIANS(lat2)) *
              COS(RADIANS(lon2) - RADIANS(lon1)) +
              SIN(RADIANS(lat1)) * SIN(RADIANS(lat2))
            ));

END;

DELIMITER;
roejwanj

roejwanj5#

不确定您的距离计算是如何进行的,但您需要对表执行self join操作,并相应地执行计算。

select t1.id as userfrom, 
t2.id as userto, 
( 3959 * acos ( cos ( radians(31.589167) ) * cos( radians( t1.Latitude ) ) * 
cos( radians( t1.Longitude ) - radians(64.363333) ) + sin ( radians(31.589167) ) * 
sin( radians( t2.Latitude ) ) ) ) AS `distance` 
from table1 t1 
inner join table1 t2 on t2.city > t1.city
wlzqhblo

wlzqhblo6#

重要!任何使用或复制这些计算的人,在将计算传递给acos()函数时,请确保使用least(1.0, (...))acos()函数不会接受大于1的值,我发现当比较相同的lat/lng值时,有时计算结果会类似于1.000002。这将产生NULL而不是0的距离,并且可能不会返回您要查找的结果,具体取决于您的查询结构!
这是正确的:

select round( 
  ( 3959 * acos( least(1.0,  
    cos( radians(28.4597) ) 
    * cos( radians(lat) ) 
    * cos( radians(lng) - radians(77.0282) ) 
    + sin( radians(28.4597) ) 
    * sin( radians(lat) 
  ) ) ) 
), 1) as distance
from locations having distance <= 60 order by distance

这是错误的:

select round( 
  ( 3959 * acos( 
    cos( radians(28.4597) ) 
    * cos( radians(lat) ) 
    * cos( radians(lng) - radians(77.0282) ) 
    + sin( radians(28.4597) ) 
    * sin( radians(lat) 
  ) ) 
), 1) as distance
from locations having distance <= 60 order by distance

评分最高的回答也谈到了这一点,但我想确保这是非常清楚的,因为我刚刚发现了一个长期存在的错误在我的查询。

izkcnapc

izkcnapc7#

这是我从https://www.geodatasource.com/developers/javascript转换而来的公式
这是一个很好的简洁函数,它以公里为单位计算距离

DELIMITER $$
CREATE DEFINER=`root`@`localhost` FUNCTION `FN_GET_DISTANCE`(
lat1 DOUBLE, lng1 DOUBLE, lat2 DOUBLE, lng2 DOUBLE
) RETURNS double
BEGIN
    DECLARE radlat1 DOUBLE;
    DECLARE radlat2 DOUBLE;
    DECLARE theta DOUBLE;
    DECLARE radtheta DOUBLE;
    DECLARE dist DOUBLE;
    SET radlat1 = PI() * lat1 / 180;
    SET radlat2 = PI() * lat2 / 180;
    SET theta = lng1 - lng2;
    SET radtheta = PI() * theta / 180;
    SET dist = sin(radlat1) * sin(radlat2) + cos(radlat1) * cos(radlat2) * cos(radtheta);
    SET dist = acos(dist);
    SET dist = dist * 180 / PI();
    SET dist = dist * 60 * 1.1515;
    SET dist = dist * 1.609344;
RETURN dist;
END$$
DELIMITER ;

您还可以在网站上找到不同语言的相同功能;

3wabscal

3wabscal8#

也许有人会派上用场,我设法通过FN_GET_DISTANCE函数实现了我的任务:

SELECT SUM (t.distance) as Distance FROM
(SELECT (CASE WHEN (FN_GET_DISTANCE (Latitude, Longitude, @OLDLatitude, @OLDLongitude)) BETWEEN 0.01 AND 2 THEN
FN_GET_DISTANCE (Latitude, Longitude, @OLDLatitude, @OLDLongitude) ELSE 0 END) AS distance,
IF (@OLDLatitude IS NOT NULL, @OLDLatitude: = Latitude, 0),
IF (@OLDLongitude IS NOT NULL, @OLDLongitude: = Longitude, 0)
FROM `data`, (SELECT @OLDLatitude: = 0) var0, (SELECT @OLDLongitude: = 0) var1
WHERE ID_Dev = 1
AND DateTime BETWEEN '2021-05-23 08:00:00' AND '2021-05-23 20:00:00'
ORDER BY ID DESC) t;

相关问题