我正在使用 forumla 在给定位置和半径的数据库中选择纬度/经度点,但是我的工作还不够。我唯一能让查询正常工作的时间是当我使用 6371 公里的半径(世界的大小)时,我认为 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);
最佳答案
一个问题是 $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 代码,你瞧,它们实际上是以度为单位的,我发现缺少到弧度的转换。
返回距离“D”的表达式中缺少的只是围绕 $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
如果我要维护它,我也会将其转换为带有绑定(bind)占位符 的准备好的语句。至少,我会正确地转义任何被合并到 SQL 文本中的潜在不安全值...... mysqli_real_escape_string
例如
$sql .= " + COS(RADIANS( " . mysqli_real_escape_string($con,$latitude) . " ))"
我怀疑 $minLat
, $maxLat
, $minLon
, $maxLon
的计算.我一定会测试那些。为了仅测试没有这些的查询,我可以注释掉整个 WHERE
子句,然后为 every 中的翻转行计算距离 D
表,然后通过 HAVING
子句过滤这些。
我们确实希望在 WHERE
子句中包含谓词(如果正确完成)会提供一个“边界框”,它会限制我们需要通过大圆计算进行的行数。
消除内联 View 应该会提高性能,因为 MySQL 不会有具体化派生表的开销。
关于php - 选择半径内的纬度和经度点,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30085759/