【发布时间】:2012-02-02 09:42:45
【问题描述】:
好的 - 我断断续续地为此苦苦挣扎了大约 3 个月,因为我已经用尽了我遇到的所有地理邻近度公式,而且我离得到正确的结果还差得远是时候寻求帮助了。
目标
我正在设置一个相当基本的商店定位器实现。用户输入他们的邮政编码并从预定义的搜索半径列表中进行选择。 gmaps API 为该地址生成纬度/经度坐标并将它们传递给 php 脚本。在此脚本中,针对 mysql 数据库表(结构如下)查询用户坐标
post_id int(11)
post_type varchar(20)
lat float(10,6)
lng float(10,6)
此查询的结果(帖子 ID)被输入到 wordpress 查询中,该查询生成包含地图标记数据的 XML。 (wordpress 查询使用 post__in 和 posts_per_page -1 显示查询生成的所有 ID 的信息
问题
简而言之,我遇到的 Haversine 公式的每个实现似乎都会导致缺少标记 - 特别是任何非常接近用户输入坐标的标记(不确切知道,但我认为它在大约 500m 以内)。这是一个大问题,如果用户输入他们的邮政编码,并且在他们的位置附近有一家商店,它就不会出现。
我已经尝试了大约 8 种不同的论坛排列组合,这些排列组合是我从各种教程中挖掘出来的,结果相同。以下是我目前在网站上使用的公式,它提供了所有标记,除了那些非常接近用户输入位置的标记:
$center_lat = $_GET["lat"];
$center_lng = $_GET["lng"];
$radius = $_GET["radius"];
// Calculate square radius search
$lat1 = (float) $center_lat - ( (int) $radius / 69 );
$lat2 = (float) $center_lat + ( (int) $radius / 69 );
$lng1 = (float) $center_lng - (int) $radius / abs( cos( deg2rad( (float) $center_lat ) ) * 69 );
$lng2 = (float) $center_lng + (int) $radius / abs( cos( deg2rad( (float) $center_lat ) ) * 69 );
$sqlsquareradius = "
SELECT
post_id, lat, lng
FROM
wp_geodatastore
WHERE
lat BETWEEN ".$lat1." AND ".$lat2."
AND
lng BETWEEN ".$lng1." AND ".$lng2."
"; // End $sqlsquareradius
// Create sql for circle radius check
$sqlcircleradius = "
SELECT
t.post_id,
3956 * 2 * ASIN(
SQRT(
POWER(
SIN(
( ".(float) $center_lat." - abs(t.lat) ) * pi() / 180 / 2
), 2
) + COS(
".(float) $center_lat." * pi() / 180
) * COS(
abs(t.lat) * pi() / 180
) * POWER(
SIN(
( ".(float) $center_lng." - t.lng ) * pi() / 180 / 2
), 2
)
)
) AS distance
FROM
(".$sqlsquareradius.") AS t
HAVING
distance <= ".(int) $radius."
ORDER BY distance
"; // End $sqlcircleradius
$result = mysql_query($sqlcircleradius);
$row = mysql_fetch_array( $result );
while($row = mysql_fetch_array( $result )) {
// the contents of each row
$post_ids[] = $row['post_id'];
}
我尝试了 1 个公式,这里是 Mike Pelley 建议的:Geolocation SQL query not finding exact location
这个公式似乎显示了非常接近用户输入位置的标记,但错过了应该在给定半径内显示的其他标记。为了消除任何混淆,这是我使用的代码:
$center_lat = $_GET["lat"];
$center_lng = $_GET["lng"];
$radius = $_GET["radius"];
$sql = "
SELECT post_id, lat, lng,
truncate((degrees(acos( sin(radians(lat))
* sin(radians(".$center_lat."))
+ cos(radians(lat))
* cos(radians(".$center_lat."))
* cos(radians(".$center_lng." - lng) ) ) )
* 69.09*1.6),1) as distance
FROM wp_geodatastore HAVING distance <= ".$radius." ORDER BY distance desc
"; // End $sqlcircleradius
$result = mysql_query($sql);
$row = mysql_fetch_array( $result );
while($row = mysql_fetch_array( $result )) {
// Print out the contents of each row
$post_ids[] = $row['post_id'];
}
请求
基本上我想知道为什么这些代码块都没有显示正确的标记。如果有人可以建议对代码进行任何改进,或者可以向我指出一些我可能错过的资源,那就太好了
编辑
以为我的伪答案有效,但事实证明仍然存在问题。我现在最终选择了一种非常不同的方法,我正在使用一个非常好的 jquery 商店定位器,可以在这里找到:http://www.bjornblog.com/web/jquery-store-locator-plugin
不适用于所有项目,但对于我的需要,它是完美的(并且有效!)
【问题讨论】:
-
您不使用 MySQL 的内置地理空间功能是否有原因? dev.mysql.com/doc/refman/5.0/en/…
-
我很难理解您代码中的一些内容。为什么
HAVING而不是WHERE?(float) $center_lat - ( (int) $radius / 69 );中的int和其他查询中的truncate是怎么回事?应用公式时请记住这一事实:赤道的一分钟经度定义为海里。整数度数是 60 海里。最后,尝试摆脱BETWEEN并改用 'WHERE a>=lat1 AND a -
@Kenneth - 我不使用地理空间查询有几个原因。首先,我使用geo data store plugin 创建和维护我的标记数据表。这个插件生成我上面展示的表结构。其次,我遇到的用于创建商店定位器类型地图的绝大多数教程似乎都推荐一种类似于所列表结构的表结构。也许这些不是最好的理由,但我已经用当前的设置走了这么远,我很确定我想做的事情应该是可能的。
-
@OllieJones 欣赏这些提示。
HAVING(而不是WHERE似乎出现在我遇到的几乎所有公式中(包括 google maps tutorial 之一。int的东西可能不需要在那里 - 我是变得如此沮丧以至于我开始留下一些可能已经从我发现的公式中删除的东西(无论哪种方式似乎都没有对结果产生任何影响。我尝试了你的BETWEEN/WHERE建议,但我'恐怕它破坏了查询 - 请参阅下一条评论了解我的尝试: -
WHERE lat >=".$lat1." AND lat<=".$lat2." AND lng >=".$lng1." AND lat<=".$lng2."
标签: php mysql wordpress geo proximity