【问题标题】:Queries to find places within a given lat/lng查询以查找给定 lat/lng 内的地点
【发布时间】:2011-07-05 08:43:17
【问题描述】:

所以我试图显示给定纬度/经度范围内的地点列表。我对此没有任何问题:

一英里以内的地方 (地点列表...)

使用类似的东西

SELECT * FROM places WHERE lat < $latmax AND lat > $latmin AND lng < $lngmax AND lng > $lngmin

然后我想列出两英里内的地点,但不是在一英里内——也就是说,我不想重复第一次查询的结果。

这是我尝试过的一个版本:

$milesperdegree = 0.868976242 / 60.0 * 1.2;

// 1 mile -- this works
$degrees = $milesperdegree * 1;
$latmin = $lat - $degrees;
$latmax = $lat + $degrees;
$lngmin = $lng - $degrees;
$lngmax = $lng + $degrees;

$query = "SELECT * FROM places WHERE lat < $latmax AND lat > $latmin AND lng < $lngmax AND lng > $lngmin";

// 2 miles -- this doesn't work
$degrees_2 = $milesperdegree * 2;
$latmin_2 = $lat - $degrees_2;
$latmax_2 = $lat + $degrees_2;
$lngmin_2 = $lat - $degrees_2;
$lngmax_2 = $lat + $degrees_2;

$query = "SELECT * FROM places WHERE ";
$query .= "lat BETWEEN $latmax AND $latmax_2 AND lng BETWEEN $lngmax AND $lngmax_2 OR ";
$query .= "lat BETWEEN $latmin AND $latmin_2 AND lng BETWEEN $lngmin AND $lngmin_2 OR ";
$query .= "lat BETWEEN $latmax AND $latmax_2 AND lng BETWEEN $lngmin AND $lngmin_2 OR ";
$query .= "lat BETWEEN $latmin AND $latmin_2 AND lng BETWEEN $lngmax AND $lngmax_2";

那不是这样做的。我猜这只是周日下午我无法理解的一些逻辑,但我可能也做错了其他事情。非常感谢任何输入。

【问题讨论】:

  • 您是否意识到这个脚本只是围绕该点创建一个框(而不是一个圆圈)?此外,$milesperdegree 是纬度的函数(从数学上讲)。有一个很好的脚本 - 尽管在 JavaScript - link
  • 我没有意识到圆形/方形位。我猜经度变量是 $lng +/- x 英里,而不是 $milesperdegree?
  • 由于地球不是一个完美的球体,度数的长度是不固定的。如果您的脚本仅在一个小区域(如城市)上运行,您可以使用该区域的平均值。否则需要更多的数学来提高精度。请参阅维基百科中的 latitudelongitude
  • 至于圆形/方形部分——勾股定理的简单应用就可以解决这个问题。
  • 顺便说一句,BETWEEN ... AND +1,不知道那个 ;)

标签: php mysql sql distance latitude-longitude


【解决方案1】:

我们实现它或多或少类似于下面的代码(免责声明:我从文件中剪掉了它并删除了与手头问题无关的代码。我没有运行它,但你应该能够得到这个想法。

$maxLat = $city->latitude + ($max_distance / 69); // 69 Miles/Degree
$minLat = $city->latitude - ($max_distance / 69);

$maxLon = $city->longitude + ($max_distance / (69.172 * cos($city->latitude * 0.0174533)));
$minLon = $city->longitude - ($max_distance / (69.172 * cos($city->latitude * 0.0174533)));

// Simplify terms to speed query
$originLatRadCos = cos($city->latitude * 0.0174533);
$originLatRadSin = sin($city->latitude * 0.0174533);
$originLonRad = $city->longitude * 0.0174533;

$city_distance_query = "
SELECT city_id, 
  3963 * acos(($originLatRadSin * sin( latitude * 0.0174533)) + 
  ($originLatRadCos * cos(latitude * 0.0174533) * cos((longitude * 0.0174533) -
  $originLonRad))) AS distanceFromOrigin
FROM cities
WHERE
 latitude < $maxLat AND latitude > $minLat AND longitude < $maxLon AND longitude > $minLon";

查询的其余部分

SELECT cities.city_name, CityDistance.distanceFromOrigin,
FROM cities 
INNER JOIN ($city_distance_query) AS CityDistance ON CityDistance.city_id=cities.city_id
WHERE (distanceFromOrigin < $distance OR distanceFromOrigin IS NULL) 

【讨论】:

  • 当然不会猜到。谢谢大家。
  • 非常感谢您的帖子。救了我很多!非常容易适应和清晰。
【解决方案2】:

我认为您缺少一些括号并且逻辑运算符有点混淆。这个怎么样。

$query  = "SELECT * FROM places WHERE ";
$query .= "((lat BETWEEN $latmin_2 AND $latmax_2) AND NOT (lat BETWEEN $latmin AND $latmax)) AND ";
$query .= "((lng BETWEEN $lngmin_2 AND $lngmax_2) AND NOT (lng BETWEEN $lngmin AND $lngmax)) AND ";

编辑

解决圆形/正方形问题:

$query  = "SELECT * FROM places WHERE ";
$query .= "(POW((lat - $lat) * $avgMilesPerLatDeg,2) + ".
           "POW((lng - $lng) * $avgMilesPerLngDeg,2) BETWEEN 1 AND 4)";
// the four at the end is 2 squared

如果您的应用不是大规模的(从地理上讲)并且平均值产生可接受的结果,我建议使用这种方法。计算 real 距离需要更长的时间来计算,并且差异可能不会那么大。这取决于您和您的应用程序的目标。

【讨论】:

  • 到达那里!但我不得不承认我不太明白发生了什么,我猜至少在数学上是这样。查询两到三英里之间的地方会是什么?
  • 三英里外的地方是我在这个应用程序中能走的最远的地方——仅仅在附近就足够了。虽然我很欣赏这些知识被丢弃以供将来使用。
  • @nickfindley,这实际上只是一个毕达哥拉斯a^2 + b^2 = c^2,在我们的例子中是n &lt;= (x-a)^2 + (y-b)^2 &lt;= m。所以对于两到三英里之间的地方,你需要... BETWEEN POW(2,2) AND POW(3,2),它相当于... BETWEEN 4 AND 9
【解决方案3】:

这是我认为您需要的:Calculate distance in MySQL using lat/lng。这将为您提供一个圈子,以及您需要的排除能力。

按照帖子中描述的步骤进行操作,而不是

HAVING `distance`<= 10

你需要放

HAVING `distance` BETWEEN 1 AND 2

这会给你范围内的东西。

PS:如果您有一个包含大量记录的数据库 - 您需要对其性能进行基准测试,并进行一些优化(如果性能不可接受)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-06-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-23
    • 2011-06-29
    相关资源
    最近更新 更多