php mysql比较long和lat,返回10英里以下的

Posted

技术标签:

【中文标题】php mysql比较long和lat,返回10英里以下的【英文标题】:php mysql compare long and lat, return ones under 10 miles 【发布时间】:2010-02-19 14:07:03 【问题描述】:

我想使用 lat 和 long 值查找 2 个位置之间的距离(以英里为单位),并检查它们是否在彼此 10 英里的半径范围内。

当用户登录时,他们的纬度/经度值会保存在会话中

$_SESSION['lat']
$_SESSION['long']

我有两个功能

这个计算出以英里为单位的距离并返回一个四舍五入的值

function distance($lat1, $lng1, $lat2, $lng2)
    $pi80 = M_PI / 180;
    $lat1 *= $pi80;
    $lng1 *= $pi80;
    $lat2 *= $pi80;
    $lng2 *= $pi80;
    $r = 6372.797; // mean radius of Earth in km
    $dlat = $lat2 - $lat1;
    $dlng = $lng2 - $lng1;
    $a = sin($dlat / 2) * sin($dlat / 2) + cos($lat1) * cos($lat2) * sin($dlng / 2) * sin($dlng / 2);
    $c = 2 * atan2(sqrt($a), sqrt(1 - $a));
    $km = $r * $c;
    return floor($km * 0.621371192);

如果 2 组 lat 和 long 之间的距离小于 10,则返回布尔值。

function is_within_10_miles($lat1, $lng1, $lat2, $lng2)
    $d = distance($lat1, $lng1, $lat2, $lng2);
    if( $d <= 10 )
        return True;
    else
        return False;
    

两个函数都按预期工作,如果我给出 2 组纬度/经度并且它们之间的距离是 20 英里,我的 is_within_10_miles() 函数返回 false。

现在,我有一个“位置”数据库(4 个字段 - ID、姓名、纬度、经度)。

我想查找半径 10 英里内的所有位置。

有什么想法吗?

编辑:我可以像这样遍历所有文件并对其执行 is_within_10_miles()

$query = "SELECT * FROM `locations`";
$result = mysql_query($query);

while($location = mysql_fetch_assoc($result))
echo $location['name']." is ";
echo distance($lat2, $lon2, $location['lat'], $location['lon']);
echo " miles form your house, is it with a 10 mile radius? ";
if( is_within_10_miles($lat2, $lon2, $location['lat'], $location['lon']) )
    echo "yeah";
else
    echo "no";

echo "<br>";

样本结果是

goodison park is 7 miles form your house, is it with a 10 mile radius? yeah

我需要在我的查询中以某种方式执行 is_within_10_miles 函数。

编辑编辑

这个来自http://www.zcentric.com/blog/2007/03/calculate_distance_in_mysql_wi.html 的传说想出了这个......

SELECT ((ACOS(SIN($lat * PI() / 180) * SIN(lat * PI() / 180) + COS($lat * PI() / 180) * COS(lat * PI() / 180) * COS(($lon - lon) * PI() / 180)) * 180 / PI()) * 60 * 1.1515) AS distance FROM members HAVING distance<='10' ORDER BY distance ASC

它确实有效。问题是我想选择 * 行,而不是一一选择它们。我该怎么做?

【问题讨论】:

查看我的编辑。这是一个 MySQL 函数,可以满足您的需要。 【参考方案1】:

您可能不需要在代码中执行此操作,您可以在数据库中执行所有操作。如果您使用spatial index。 MySQL docuemtnation for spatial index

编辑以反映您的编辑:

我想你想要这样的东西:

SELECT *, ((ACOS(SIN($lat * PI() / 180) * SIN(lat * PI() / 180) + COS($lat * PI() / 180) * COS(lat * PI() / 180) * COS(($lon - lon) * PI() / 180)) * 180 / PI()) * 60 * 1.1515) AS distance FROM locations HAVING distance<='10' ORDER BY distance ASC

VIA: http://www.zcentric.com/blog/2007/03/calculate_distance_in_mysql_wi.html:

【讨论】:

是的,这是一种享受,但是我如何选择所有 (*) 行?【参考方案2】:

在您查询之前,请计算您的十英里圆圈的最大和最小纬度和经度。这将为您提供四个数字,因此您的 where 子句的第一部分将排除任何超出大约 10 英里单程框的行。

然后复杂的真实距离 where 子句只检查正方形内的那些,看看它们是否也在圆内。

阅读您的 DBMS 的文档,了解惰性评估是否适用,或者您是否需要将其安排为

SELECT *
FROM (SELECT *
      FROM table
      WHERE (simple rough box clause)
     )
WHERE (complex clause)

而不是通常的

SELECT * FROM table WHERE (simple clause) AND (complex clause)

【讨论】:

太好了,2.5 年前的答案中没有评论的 -1。 有一个 +1,因为这不是一个糟糕的第一次削减。 :-)【参考方案3】:

在公里中搜索: ($LATITUDE -> 您的纬度,$LONGITUDE -> 您的经度,latitud_fieldname -> 您的纬度数据库字段名,longitude_fieldname -> 您的经度数据库字段名)

SELECT * FROM (
    SELECT *, 
        (
            (
                (
                    acos(
                        sin(( $LATITUDE * pi() / 180))
                        *
                        sin(( `latitud_fieldname` * pi() / 180)) + cos(( $LATITUDE * pi() /180 ))
                        *
                        cos(( `latitud_fieldname` * pi() / 180)) * cos((( $LONGITUDE - `longitude_fieldname`) * pi()/180)))
                ) * 180/pi()
            ) * 60 * 1.1515 * 1.609344
        )
    as distance FROM `myTable`
) myTable
WHERE distance <= $DISTANCE_KILOMETERS;

以英里搜索:

SELECT * FROM (
    SELECT *, 
        (
            (
                (
                    acos(
                        sin(( $LATITUDE * pi() / 180))
                        *
                        sin(( `latitud_fieldname` * pi() / 180)) + cos(( $LATITUDE * pi() /180 ))
                        *
                        cos(( `latitud_fieldname` * pi() / 180)) * cos((( $LONGITUDE - `longitude_fieldname`) * pi()/180)))
                ) * 180/pi()
            ) * 60 * 1.1515
        )
    as distance FROM `myTable`
) myTable
WHERE distance <= $DISTANCE_MILES;

有关完整信息,请点击链接:https://ourcodeworld.com/articles/read/1019/how-to-find-nearest-locations-from-a-collection-of-coordinates-latitude-and-longitude-with-php-mysql

【讨论】:

【参考方案4】:

这应该为您指明正确的方向 - 没有双关语。

Mysql 函数使用 lat/long 查找两地之间的距离

有时我们需要找出距中心地点一定半径范围内的地点列表,这些地点的坐标保存在数据库中。现在我们有两个解决方案——要么遍历所有的地方找到到中心点的距离,并保留距离小于或等于半径的地方,或者制作一个 sql 函数来找到两个地方的距离,然后选择距离小于或等于半径的地方。显然第二种选择比第一种要好。所以我写了一个为你工作的 Mysql 函数。 在我的例子中,坐标以“10.1357002, 49.9225563, 0”形式的字符串保存在数据库中。这是许多人使用的标准坐标格式(例如 Google 地图)。第一个元素是经度,第二个是纬度,我们可以忽略第三个(始终为 0)。所以这里是返回 2 个坐标之间的距离的 Mysql 函数。

DELIMITER $$

DROP FUNCTION IF EXISTS `GetDistance`$$

CREATE FUNCTION `GetDistance`(coordinate1 VARCHAR(120), coordinate2 VARCHAR(120))
    RETURNS VARCHAR(120)
BEGIN
    DECLARE pos_comma1, pos_comma2 INT;
    DECLARE lon1, lon2, lat1, lat2, distance DECIMAL(12,8);

    select locate(',', coordinate1) into pos_comma1;
    select locate(',', coordinate1, pos_comma1+1) into pos_comma2;
    select CAST(substring(coordinate1, 1, pos_comma1-1) as DECIMAL(12,8)) into lon1;
    select CAST(substring(coordinate1, pos_comma1+1, pos_comma2-pos_comma1-1) as DECIMAL(12,8)) into lat1;

    select locate(',', coordinate2) into pos_comma1;
    select locate(',', coordinate2, pos_comma1+1) into pos_comma2;
    select CAST(substring(coordinate2, 1, pos_comma1-1) as DECIMAL(12,8)) into lon2;
    select CAST(substring(coordinate2, pos_comma1+1, pos_comma2-pos_comma1-1) as DECIMAL(12,8)) into lat2;

        select ((ACOS(SIN(lat1 * PI() / 180) * SIN(lat2 * PI() / 180) + COS(lat1 * PI() / 180) * COS(lat2 * PI() / 180) * COS((lon1 - lon2) * PI() / 180)) * 180 / PI()) * 60 * 1.1515) into distance;
    RETURN distance;

END$$

DELIMITER ;

示例用法:

假设您必须找出坐标为“10.1357002, 49.9225563, 0”的地方半径 40 英里内的所有邮政编码。您有一个 POSTCODES 表,其中包含字段 id、邮政编码、坐标。所以你的 sql 应该是这样的:

  select id, postcode from POSTCODES where GetDistance("10.1357002, 49.9225563, 0", coordinates) <= 40;

【讨论】:

这将计算搜索集中每个条目的距离。它会返回正确的结果,但如果搜索集变大,效率会非常低。 我对 SQL 函数非常陌生。这要去哪里?我如何在我的 MySQL 数据库上设置此功能? 好的,我已经添加了这个功能。目前我有 3 个字段(名称、经度、纬度)你可以只使用这 3 个字段来编辑示例吗?【参考方案5】:

我将把它留在这里:

https://gist.github.com/899413

这是一个关于如何在整数的正方形(BETWEEN)内查询并使用 SQRT() 减少结果以使输出返回圆形半径结果的示例。 (它还以公里为单位返回距离)

为此,您需要在插入时将所有点转换为公里,以便将它们布置在平面地图上。 (见php函数)

结果是令人难以置信的快速整数查找 - 比在查询中执行所有计算(如此处的每个答案所述)要快得多。

(我是在 10 年前构建的……看起来它仍然是 mysql 最聪明的解决方案)

【讨论】:

【参考方案6】:

经纬度的原点

$orig_lat=23.04988655049843;
$orig_lon= 72.51734018325806;
$dist=10;  // Radius;

//Latitude = database field name;
//Longitude = database field name;

$query = SELECT *, 3956 * 2 * ASIN(SQRT( POWER(SIN(($orig_lat -abs(dest.Latitude)) * pi()/180 / 2),2) + COS($orig_lat * pi()/180 ) * COS( abs(dest.Latitude) *  pi()/180) * POWER(SIN(($orig_lon - dest.Longitude) *  pi()/180 / 2), 2) )) as distance FROM tbl_name dest having distance < 10 ORDER BY distance limit 10

找到半径 10 英里内的所有位置。 工作正常!

【讨论】:

实际上你的意思是......你使用的查询中的“&lt”。

以上是关于php mysql比较long和lat,返回10英里以下的的主要内容,如果未能解决你的问题,请参考以下文章

如何计算具有 lat+long 信息的集合的地理空间距离?

Google Maps Geocoding APi 不返回 lat 和 long

返回要在 sql 查询中使用的用户当前位置的 lat 和 long 值

如何在 php 中获取当前位置 lat -long 值以便在 android 中使用

php Lat Long转换为导入

php 获得LAT / LONG的证明。安德烈...