我数据库中的每个用户都将他们的纬度和经度存储在两个字段(纬度,经度)中
每个字段的格式为:
lon | -1.403976
lat | 53.428691
如果用户在 100 英里范围内搜索其他用户,我会执行以下操作以计算适当的纬度/经度范围($lat 和 $lon 是当前用户值)
$R = 3960; // earth's mean radius
$rad = '100';
// first-cut bounding box (in degrees)
$maxLat = $lat + rad2deg($rad/$R);
$minLat = $lat - rad2deg($rad/$R);
// compensate for degrees longitude getting smaller with increasing latitude
$maxLon = $lon + rad2deg($rad/$R/cos(deg2rad($lat)));
$minLon = $lon - rad2deg($rad/$R/cos(deg2rad($lat)));
$maxLat=number_format((float)$maxLat, 6, '.', '');
$minLat=number_format((float)$minLat, 6, '.', '');
$maxLon=number_format((float)$maxLon, 6, '.', '');
$minLon=number_format((float)$minLon, 6, '.', '');
然后我可以执行如下查询:
$query = "SELECT * FROM table WHERE lon BETWEEN '$minLon' AND '$maxLon' AND lat BETWEEN '$minLat' AND '$maxLat'";
这很好用,我在输出阶段使用一个函数来计算和显示用户之间的实际距离,但我希望能够在查询阶段通过减少或增加距离来对结果进行排序。
有什么办法吗?
最佳答案
还记得毕达哥拉斯吗?
$sql = "SELECT * FROM table
WHERE lon BETWEEN '$minLon' AND '$maxLon'
AND lat BETWEEN '$minLat' AND '$maxLat'
ORDER BY (POW((lon-$lon),2) + POW((lat-$lat),2))";
从技术上讲,这是距离的平方,而不是实际距离,但因为您只是将它用于排序,所以没关系。
这使用了平面距离公式,这应该适用于小距离。
但是:
如果您想更精确或使用更长的距离,请使用 this formula for great circle distances in radians :
dist = acos[ sin(lat1)*sin(lat2)+cos(lat1)*cos(lat2)*cos(lng1-lng2) ]
(要以实际单位而不是弧度获得距离,请将其乘以地球的半径。不过,这对于排序目的不是必需的。)
MySQL 计算引擎假定纬度和经度以弧度为单位,因此如果它以度为单位(可能是),您必须将每个值乘以 pi/180,大约为 0.01745:
$sf = 3.14159 / 180; // scaling factor
$sql = "SELECT * FROM table
WHERE lon BETWEEN '$minLon' AND '$maxLon'
AND lat BETWEEN '$minLat' AND '$maxLat'
ORDER BY ACOS(SIN(lat*$sf)*SIN($lat*$sf) + COS(lat*$sf)*COS($lat*$sf)*COS((lon-$lon)*$sf))";
甚至:
$sf = 3.14159 / 180; // scaling factor
$er = 6350; // earth radius in miles, approximate
$mr = 100; // max radius
$sql = "SELECT * FROM table
WHERE $mr >= $er * ACOS(SIN(lat*$sf)*SIN($lat*$sf) + COS(lat*$sf)*COS($lat*$sf)*COS((lon-$lon)*$sf))
ORDER BY ACOS(SIN(lat*$sf)*SIN($lat*$sf) + COS(lat*$sf)*COS($lat*$sf)*COS((lon-$lon)*$sf))";
关于php - 按纬度/经度对 MySQL 查询进行排序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16465779/