php mysql比较long和lat,返回10英里以下的
Posted
技术标签:
【中文标题】php mysql比较long和lat,返回10英里以下的【英文标题】:php mysql compare long and lat, return ones under 10 miles 【发布时间】:2010-02-19 14:07:03 【问题描述】:我想使用 lat 和 long 值查找 2 个位置之间的距离(以英里为单位),并检查它们是否在彼此 10 英里的半径范围内。
当用户登录时,他们的纬度/经度值会保存在会话中
$_SESSION['lat']
$_SESSION['long']
我有两个功能
这个计算出以英里为单位的距离并返回一个四舍五入的值
function distance($lat1, $lng1, $lat2, $lng2)
$pi80 = M_PI / 180;
$lat1 *= $pi80;
$lng1 *= $pi80;
$lat2 *= $pi80;
$lng2 *= $pi80;
$r = 6372.797; // mean radius of Earth in km
$dlat = $lat2 - $lat1;
$dlng = $lng2 - $lng1;
$a = sin($dlat / 2) * sin($dlat / 2) + cos($lat1) * cos($lat2) * sin($dlng / 2) * sin($dlng / 2);
$c = 2 * atan2(sqrt($a), sqrt(1 - $a));
$km = $r * $c;
return floor($km * 0.621371192);
如果 2 组 lat 和 long 之间的距离小于 10,则返回布尔值。
function is_within_10_miles($lat1, $lng1, $lat2, $lng2)
$d = distance($lat1, $lng1, $lat2, $lng2);
if( $d <= 10 )
return True;
else
return False;
两个函数都按预期工作,如果我给出 2 组纬度/经度并且它们之间的距离是 20 英里,我的 is_within_10_miles() 函数返回 false。
现在,我有一个“位置”数据库(4 个字段 - ID、姓名、纬度、经度)。
我想查找半径 10 英里内的所有位置。
有什么想法吗?
编辑:我可以像这样遍历所有文件并对其执行 is_within_10_miles()
$query = "SELECT * FROM `locations`";
$result = mysql_query($query);
while($location = mysql_fetch_assoc($result))
echo $location['name']." is ";
echo distance($lat2, $lon2, $location['lat'], $location['lon']);
echo " miles form your house, is it with a 10 mile radius? ";
if( is_within_10_miles($lat2, $lon2, $location['lat'], $location['lon']) )
echo "yeah";
else
echo "no";
echo "<br>";
样本结果是
goodison park is 7 miles form your house, is it with a 10 mile radius? yeah
我需要在我的查询中以某种方式执行 is_within_10_miles 函数。
编辑编辑
这个来自http://www.zcentric.com/blog/2007/03/calculate_distance_in_mysql_wi.html 的传说想出了这个......
SELECT ((ACOS(SIN($lat * PI() / 180) * SIN(lat * PI() / 180) + COS($lat * PI() / 180) * COS(lat * PI() / 180) * COS(($lon - lon) * PI() / 180)) * 180 / PI()) * 60 * 1.1515) AS distance FROM members HAVING distance<='10' ORDER BY distance ASC
它确实有效。问题是我想选择 * 行,而不是一一选择它们。我该怎么做?
【问题讨论】:
查看我的编辑。这是一个 MySQL 函数,可以满足您的需要。 【参考方案1】:您可能不需要在代码中执行此操作,您可以在数据库中执行所有操作。如果您使用spatial index。 MySQL docuemtnation for spatial index
编辑以反映您的编辑:
我想你想要这样的东西:
SELECT *, ((ACOS(SIN($lat * PI() / 180) * SIN(lat * PI() / 180) + COS($lat * PI() / 180) * COS(lat * PI() / 180) * COS(($lon - lon) * PI() / 180)) * 180 / PI()) * 60 * 1.1515) AS distance FROM locations HAVING distance<='10' ORDER BY distance ASC
VIA: http://www.zcentric.com/blog/2007/03/calculate_distance_in_mysql_wi.html:
【讨论】:
是的,这是一种享受,但是我如何选择所有 (*) 行?【参考方案2】:在您查询之前,请计算您的十英里圆圈的最大和最小纬度和经度。这将为您提供四个数字,因此您的 where 子句的第一部分将排除任何超出大约 10 英里单程框的行。
然后复杂的真实距离 where 子句只检查正方形内的那些,看看它们是否也在圆内。
阅读您的 DBMS 的文档,了解惰性评估是否适用,或者您是否需要将其安排为
SELECT *
FROM (SELECT *
FROM table
WHERE (simple rough box clause)
)
WHERE (complex clause)
而不是通常的
SELECT * FROM table WHERE (simple clause) AND (complex clause)
【讨论】:
太好了,2.5 年前的答案中没有评论的 -1。 有一个 +1,因为这不是一个糟糕的第一次削减。 :-)【参考方案3】:在公里中搜索: ($LATITUDE
-> 您的纬度,$LONGITUDE
-> 您的经度,latitud_fieldname
-> 您的纬度数据库字段名,longitude_fieldname
-> 您的经度数据库字段名)
SELECT * FROM (
SELECT *,
(
(
(
acos(
sin(( $LATITUDE * pi() / 180))
*
sin(( `latitud_fieldname` * pi() / 180)) + cos(( $LATITUDE * pi() /180 ))
*
cos(( `latitud_fieldname` * pi() / 180)) * cos((( $LONGITUDE - `longitude_fieldname`) * pi()/180)))
) * 180/pi()
) * 60 * 1.1515 * 1.609344
)
as distance FROM `myTable`
) myTable
WHERE distance <= $DISTANCE_KILOMETERS;
以英里搜索:
SELECT * FROM (
SELECT *,
(
(
(
acos(
sin(( $LATITUDE * pi() / 180))
*
sin(( `latitud_fieldname` * pi() / 180)) + cos(( $LATITUDE * pi() /180 ))
*
cos(( `latitud_fieldname` * pi() / 180)) * cos((( $LONGITUDE - `longitude_fieldname`) * pi()/180)))
) * 180/pi()
) * 60 * 1.1515
)
as distance FROM `myTable`
) myTable
WHERE distance <= $DISTANCE_MILES;
有关完整信息,请点击链接:https://ourcodeworld.com/articles/read/1019/how-to-find-nearest-locations-from-a-collection-of-coordinates-latitude-and-longitude-with-php-mysql
【讨论】:
【参考方案4】:这应该为您指明正确的方向 - 没有双关语。
Mysql 函数使用 lat/long 查找两地之间的距离
有时我们需要找出距中心地点一定半径范围内的地点列表,这些地点的坐标保存在数据库中。现在我们有两个解决方案——要么遍历所有的地方找到到中心点的距离,并保留距离小于或等于半径的地方,或者制作一个 sql 函数来找到两个地方的距离,然后选择距离小于或等于半径的地方。显然第二种选择比第一种要好。所以我写了一个为你工作的 Mysql 函数。 在我的例子中,坐标以“10.1357002, 49.9225563, 0”形式的字符串保存在数据库中。这是许多人使用的标准坐标格式(例如 Google 地图)。第一个元素是经度,第二个是纬度,我们可以忽略第三个(始终为 0)。所以这里是返回 2 个坐标之间的距离的 Mysql 函数。
DELIMITER $$
DROP FUNCTION IF EXISTS `GetDistance`$$
CREATE FUNCTION `GetDistance`(coordinate1 VARCHAR(120), coordinate2 VARCHAR(120))
RETURNS VARCHAR(120)
BEGIN
DECLARE pos_comma1, pos_comma2 INT;
DECLARE lon1, lon2, lat1, lat2, distance DECIMAL(12,8);
select locate(',', coordinate1) into pos_comma1;
select locate(',', coordinate1, pos_comma1+1) into pos_comma2;
select CAST(substring(coordinate1, 1, pos_comma1-1) as DECIMAL(12,8)) into lon1;
select CAST(substring(coordinate1, pos_comma1+1, pos_comma2-pos_comma1-1) as DECIMAL(12,8)) into lat1;
select locate(',', coordinate2) into pos_comma1;
select locate(',', coordinate2, pos_comma1+1) into pos_comma2;
select CAST(substring(coordinate2, 1, pos_comma1-1) as DECIMAL(12,8)) into lon2;
select CAST(substring(coordinate2, pos_comma1+1, pos_comma2-pos_comma1-1) as DECIMAL(12,8)) into lat2;
select ((ACOS(SIN(lat1 * PI() / 180) * SIN(lat2 * PI() / 180) + COS(lat1 * PI() / 180) * COS(lat2 * PI() / 180) * COS((lon1 - lon2) * PI() / 180)) * 180 / PI()) * 60 * 1.1515) into distance;
RETURN distance;
END$$
DELIMITER ;
示例用法:
假设您必须找出坐标为“10.1357002, 49.9225563, 0”的地方半径 40 英里内的所有邮政编码。您有一个 POSTCODES 表,其中包含字段 id、邮政编码、坐标。所以你的 sql 应该是这样的:
select id, postcode from POSTCODES where GetDistance("10.1357002, 49.9225563, 0", coordinates) <= 40;
【讨论】:
这将计算搜索集中每个条目的距离。它会返回正确的结果,但如果搜索集变大,效率会非常低。 我对 SQL 函数非常陌生。这要去哪里?我如何在我的 MySQL 数据库上设置此功能? 好的,我已经添加了这个功能。目前我有 3 个字段(名称、经度、纬度)你可以只使用这 3 个字段来编辑示例吗?【参考方案5】:我将把它留在这里:
https://gist.github.com/899413
这是一个关于如何在整数的正方形(BETWEEN)内查询并使用 SQRT() 减少结果以使输出返回圆形半径结果的示例。 (它还以公里为单位返回距离)
为此,您需要在插入时将所有点转换为公里,以便将它们布置在平面地图上。 (见php函数)
结果是令人难以置信的快速整数查找 - 比在查询中执行所有计算(如此处的每个答案所述)要快得多。
(我是在 10 年前构建的……看起来它仍然是 mysql 最聪明的解决方案)
【讨论】:
【参考方案6】:经纬度的原点
$orig_lat=23.04988655049843;
$orig_lon= 72.51734018325806;
$dist=10; // Radius;
//Latitude = database field name;
//Longitude = database field name;
$query = SELECT *, 3956 * 2 * ASIN(SQRT( POWER(SIN(($orig_lat -abs(dest.Latitude)) * pi()/180 / 2),2) + COS($orig_lat * pi()/180 ) * COS( abs(dest.Latitude) * pi()/180) * POWER(SIN(($orig_lon - dest.Longitude) * pi()/180 / 2), 2) )) as distance FROM tbl_name dest having distance < 10 ORDER BY distance limit 10
找到半径 10 英里内的所有位置。 工作正常!
【讨论】:
实际上你的意思是......你使用的查询中的“<”。以上是关于php mysql比较long和lat,返回10英里以下的的主要内容,如果未能解决你的问题,请参考以下文章
Google Maps Geocoding APi 不返回 lat 和 long
返回要在 sql 查询中使用的用户当前位置的 lat 和 long 值