我使用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);
答案 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
所以这个:
, 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