选择半径范围内的纬度和经度点

时间:2015-05-06 19:22:03

标签: php mysql sql mysqli

我使用forumla在给定位置和半径的情况下选择数据库中的纬度/经度点,但是我只是在它工作的范围内。我唯一可以让查询工作的是当我使用6371km的半径(世界的大小)时,我认为forumla存在问题,但我似乎无法弄清楚在哪里......

这是我的SQL查询

   $latitude = $_POST["latitude"]; // latitude of centre of bounding circle in degrees
   $longitude = $_POST["longitude"];; // longitude of centre of bounding circle in degrees
   $rad = $_POST["radius"]; // radius of bounding circle in kilometers
   $R = 6371;// earth's mean radius, km
   $maxLat = $latitude + rad2deg($rad/$R);
   $minLat = $latitude - rad2deg($rad/$R);
    // compensate for degrees longitude getting smaller with increasing latitude
   $maxLon = $longitude + rad2deg($rad/$R/cos(deg2rad($latitude)));
   $minLon = $longitude - rad2deg($rad/$R/cos(deg2rad($latitude)));



   $con = mysqli_connect("localhost","root","Winter#05");

   if (!$con)
   {
   die('Could not connect: ' . mysql_error());
   }
   mysqli_select_db($con, "appdatabase");


   $result = mysqli_query($con,"select id, PostTitle, SubmitDate, PostVotes, ImagePath, comments, latitude, longitude, acos(sin($latitude)*sin(radians(latitude)) + cos($latitude)*cos(radians(latitude))*cos(radians(longitude)-($longitude))) * $R as D 
   from ( select id, PostTitle, SubmitDate, PostVotes, comments, ImagePath, latitude, longitude 
   from posts where latitude > $minLat and latitude < $maxLat and longitude > $minLon and longitude < $maxLon ) as first_cut 
   where acos(sin($latitude)*sin(radians(latitude)) + cos($latitude)*cos(radians(latitude))*cos(radians(longitude) - ($longitude))) * $R < $rad order by D") or die('Errant query:');

   while($row = mysqli_fetch_assoc($result))
   {
        $output[]=$row;
   }


   print(json_encode($output));

   mysqli_close($con);

2 个答案:

答案 0 :(得分:1)

一个问题是,$latitude$longitude似乎以为单位合并到SQL文本中。公式(因为它在SQL查询中实现)看起来希望这些位置的值在弧度中。

如果我必须阅读/维护这段代码,如果它是这样编写的,那么 更容易理解查询:

SELECT p.id
     , p.PostTitle
     , p.SubmitDate
     , p.PostVotes
     , p.ImagePath
     , p.comments
     , p.latitude
     , p.longitude
     , ACOS( SIN(       (  $latitude ))
           * SIN(RADIANS( p.latitude ))
           + COS(       (  $latitude ))
           * COS(RADIANS( p.latitude ))
           * COS(RADIANS( p.longitude ) -        ( $longitude ))
           ) * $R
       AS D
  FROM posts p
 WHERE p.latitude  > $minLat
   AND p.latitude  < $maxLat 
   AND p.longitude > $minLon
   AND p.longitude < $maxLon 
HAVING D < $rad
 ORDER BY D

大圆距离公式看起来还不错,除了我想知道为什么以弧度为单位提供$latitude$longitude而不是度数。

然后我查看PHP代码,瞧,这些实际上是以度为单位的,我发现转换为弧度失踪了。

表达式中缺少所有返回距离&#34; D&#34;是$latitude$longitude

周围的RADIANS函数

所以这个:

     , ACOS( SIN(       (  $latitude ))
           * SIN(RADIANS( p.latitude ))
           + COS(       (  $latitude ))
           * COS(RADIANS( p.latitude ))
           * COS(RADIANS( p.longitude ) -        ( $longitude ))
           ) * $R
       AS D

替换为

     , ACOS( SIN(RADIANS(  $latitude ))
           * SIN(RADIANS( p.latitude ))
           + COS(RADIANS(  $latitude ))
           * COS(RADIANS( p.latitude ))
           * COS(RADIANS( p.longitude ) - RADIANS( $longitude ))
           ) * $R
       AS D

如果我维持这一点,我也会将其转换为带绑定占位符预备声明。至少,我正确地避免了任何可能不安全的值,这些值被合并到SQL文本中...... mysqli_real_escape_string

e.g。

$sql .= "  + COS(RADIANS( " . mysqli_real_escape_string($con,$latitude) . " ))"

我对$minLat$maxLat$minLon$maxLon的计算表示怀疑。我一定要测试那些。为了测试没有这些的查询,我可以注释掉整个WHERE子句,然后计算表中每个翻转行的距离D,然后过滤那些通过HAVING条款。

我们确实希望在WHERE子句中使用谓词(如果正确完成)将给出一个&#34;边界框&#34;这将限制我们需要通过大圆计算的行数。

消除内联视图应该可以提高性能,因为MySQL不会有实现派生表的开销。

答案 1 :(得分:0)

我目前正在使用以下内容并且对(相对)准确性感到满意。

( 
    3958*3.1415926 * sqrt( 
        ( table.latitude - $latitude ) * ( table.latitude - $latitude ) 
        + cos( table.latitude / 57.29578 ) 
        * cos( $latitude / 57.29578 ) 
        * ( table.longitude - $longitude ) 
        * ( table.longitude - $longitude ) 
    ) / 180
) <= $radius