【问题标题】:PHP MySQL get locations in radius user's location from GPSPHP MySQL从GPS获取半径用户位置的位置
【发布时间】:2010-07-28 03:54:19
【问题描述】:

例如,我的数据库中有汽车事故。这些事件有经纬度。在使用 GPS 的手机上,我通过他的坐标获取用户的位置。如果周围有事故,用户可以选择他想知道的半径。所以假设他想知道他周围 2 英里的事件。

所以我从手机向网络服务发送用户的纬度、经度和他选择的半径。我需要进行 SQL 查询以获取用户周围 2 英里的事件。

你知道怎么做吗?

【问题讨论】:

    标签: php mysql location coordinates


    【解决方案1】:

    正如其他人所说,计算距离的计算成本非常高。返回庞大的数据集也不是一个好主意 - 特别是考虑到 PHP 的性能并不是那么好。

    我会使用启发式方法,例如通过简单的加减法来近似距离。

    1 分钟 = 1.86 公里 = 1.15 里程

    只需在数据库中搜索该范围内的事件(实际上是正方形,而不是圆形),然后您就可以使用 PHP 处理这些事件。


    编辑:这是另一种选择;计算成本更低的近似值:

    以英里为单位的大致距离:

    sqrt(x * x + y * y)
    
    where x = 69.1 * (lat2 - lat1) 
    and y = 53.0 * (lon2 - lon1) 
    

    您可以通过添加余弦数学函数来提高这种近似距离计算的准确性:

    改进的近似距离(以英里为单位):

    sqrt(x * x + y * y)
    
    where x = 69.1 * (lat2 - lat1) 
    and y = 69.1 * (lon2 - lon1) * cos(lat1/57.3) 
    

    来源:http://www.meridianworlddata.com/Distance-Calculation.asp


    编辑 2:我用随机生成的数据集运行了一堆测试。

    • 3 种算法的准确度差异很小,尤其是在短距离内
    • 最慢的算法(具有一大堆三角函数的算法)比其他两个慢 4 倍。

    绝对不值得。只用一个近似值。

    代码在这里:http://pastebin.org/424186

    【讨论】:

    • 另一个很好的启发式方法。使用一分钟(或其相关细分)可能的最小距离,并使用它来排除任何明显超过两英里的候选点。当然,您可以对此更加聪明,并根据用户的位置使用各种近似值。
    【解决方案2】:
    function distance($lat1,$lon1,$lat2,$lon2,$unit)
      {
      $theta=$lon1-$lon2;
      $dist=sin(deg2rad($lat1))*sin(deg2rad($lat2))+cos(deg2rad($lat1))*cos(deg2rad($lat2))*cos(deg2rad($theta));
      $dist=acos($dist);
      $dist=rad2deg($dist);
      $miles=$dist*60*1.1515;
      $unit=strtoupper($unit);
      if ($unit=="K")
        {
        return ($miles*1.609344);
        }
        else if ($unit=="N")
        {
        return ($miles*0.8684);
        }
        else
        {
        return $miles;
        }
      } // end function
    
    $x_lat=center_of_serach;
    $x_lon=center_of_serach;
    $_distance=some_distance_in_miles;
    $query1 = "SELECT * FROM `location_table` WHERE somefield=somefilter";
    $result=mysql_db_query($db_conn, $query1);
    $max_rows=mysql_num_rows($result); 
    if ($max_rows>0)
      {
    while ( $data1=mysql_fetch_assoc($result) )
      {
      if ( distance($x_lat,$x_lon,$data1['lat'],$data1['lng'],'m')<$_distance )
        {
        //do stuff
        }
      }
    

    获取所有数据并通过函数运行它更快,而不是在数据库不太大时使用查询。

    它也适用于公斤和海里。 ;)

    【讨论】:

    • LOL 我刚刚注意到你的链接.. 是的,那行得通。 ;)
    【解决方案3】:

    有一个公式可以计算两个纬度/经度坐标之间的距离。不过要当心——它的计算成本相当高,所以如果你有很多事件,你会想要聪明一点。首先,read about the maths involved

    至于 PHP 代码,一个快速的 google 出现了this link,看起来它可能有效。

    现在,您可能希望使用一些更有效的方法将事件点分为两组:可能在范围内的点(希望是较小的集合),以及可以打折的点完全。检查几十个以上的事件坐标可能是一个性能问题。

    我对此没有任何特别的见解,但如果没有其他人提出聪明的想法,我会在以后尝试自己想出一些东西,如果时间允许的话。

    【讨论】:

    • 我也推荐这个。我会考虑对数据集进行两次传递。第一遍将仅检查事件经度是否在位置经度的 X 英里范围内,以及事件纬度是否在位置纬度的 X 英里范围内。这是一个非常简单的查询,它将返回一个较小的数据集以供使用。使用这个较小的数据集,开始做一些三角函数来确定事件到用户位置的线性距离,这需要更复杂的数学运算,但您现在至少使用的是较小的数据集。
    • @Jakobud 您可以在同一查询中运行该过滤器:WHERE lat&lt;='".$_distance."' AND lng&lt;='".$_distance."'";
    • 是的,类似的。为了可视化 Jackobud 的方法,您首先检查是否有任何候选点位于 4 平方英里(16 平方英里)的区域内,用户坐标位于中心。如果是,那么您进行更昂贵的计算来确定它是否与该正方形内的圆圈 [en.wikipedia.org/wiki/Inscribed_circle]
    • @Talvi Watia - 嗯?这似乎不对——单位不匹配。
    • @timedev 哇。是的。我发布的太快了..添加$distance/69.1以大致转换为度数..
    【解决方案4】:

    我进行了快速搜索并找到了this blog post,它提供了很好的解释和 SQL 来选择给定半径内的记录。

    在 cmets 中,他建议“为了提高大型数据集的速度,您可能希望先在原点周围抓取一个正方形块,方法是在原点和纬度/经度之间添加一英里左右,然后使用上述作为原点subselect to work from the middle out”这听起来像是要走的路。

    【讨论】:

    【解决方案5】:
     SELECT 3963 * ACOS(
        SIN(RADIANS($pointAlat)) * SIN(RADIANS($pointAlat)) + COS(RADIANS($pointAlat))  * COS(RADIANS($pointBlat)) * COS(RADIANS($pointAlong) - RADIANS($pointBlong)))
     AS
     distance;
    

    此外,如果您正在寻找关于此的良好阅读/教程.. 请点击此处 http://www.phpfreaks.com/forums/index.php/topic,208965.0.html

    【讨论】:

    • 没用,因为你不能在 WHERE 子句中使用别名——这会迫使他计算一堆超越函数两次。哎哟。在一个潜在的巨大数据集上。双重哎哟。
    猜你喜欢
    • 1970-01-01
    • 2013-08-30
    • 1970-01-01
    • 1970-01-01
    • 2015-05-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多