php - 由于地理邻近度公式(商店定位器)而缺少结果

标签 php mysql wordpress geo proximity

好的 - 我已经断断续续地研究了大约 3 个月,因为我已经用尽了所有我遇到的地理接近度公式,而且我离获得正确的结果还差得很远,我想是时候寻求帮助了。

目标

我正在设置一个相当基本的商店定位器实现。用户输入他们的邮政编码并从预定义的搜索半径列表中进行选择。 gmaps API 为该地址生成纬度/经度坐标并将它们传递给 php 脚本。在此脚本中,针对 mysql 数据库表(结构如下)查询用户坐标

post_id int(11)                             
post_type varchar(20)                                
lat   float(10,6)                               
lng   float(10,6)

此查询的结果(帖子 ID)被输入到生成包含 map 标记数据的 XML 的 wordpress 查询中。 (wordpress 查询使用 post__in 和 posts_per_page -1 来显示查询生成的所有 ID 的信息

问题

简而言之,我遇到的每一个 Haversine 公式的实现似乎都会导致标记丢失 - 特别是任何非常接近用户输入坐标的标记(不知道确切但我认为它在大约 500 米以内).这是一个大问题,因为如果用户输入他们的邮政编码并且有一家商店非常靠近他们的位置,它就不会出现。

我已经尝试了大约 8 种不同的 forumla 排列,这些排列是我从各种教程中挖掘出来的,结果相同。下面是我目前在网站上使用的公式,它提供了所有标记,但非常接近用户输入位置的标记除外:

$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'];
}

Mike Pelley 在这里建议了我尝试过的 1 个公式: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

不会对所有项目都有效,但对我的需求来说它是完美的(并且有效!)

最佳答案

编辑 这个位置查找器经常出现,我已经写了一篇关于它的文章。

http://www.plumislandmedia.net/mysql/haversine-mysql-nearest-loc/

原帖

让我们从一劳永逸地处理 haversine 公式开始,将它放入一个存储函数中,这样我们就可以忘记它的粗糙细节。注意:整个解决方案以法定英里为单位。

DELIMITER $$

CREATE
    FUNCTION distance(lat1 FLOAT, long1 FLOAT, lat2 FLOAT, long2 FLOAT)
    RETURNS FLOAT
    DETERMINISTIC NO SQL
    BEGIN
    RETURN (3959 * ACOS(COS(RADIANS(lat1)) 
                 * COS(RADIANS(lat2)) 
                 * COS(RADIANS(long1) - RADIANS(long2)) 
                 + SIN(RADIANS(lat1)) 
                 * SIN(RADIANS(lat2)) 
                )); 
    END$$

DELIMITER ;

现在让我们组合一个在边界框上搜索的查询,然后使用我们的距离函数和按距离排序优化搜索

根据您问题中的 PHP 代码:

假设$radius是你的半径,$center_lat$center_lng是你的引用点。

$sqlsquareradius = "
SELECT post_id, lat, lng
  FROM
(
    SELECT post_id, lat, lng,
           distance(lat, lng, " . $center_lat . "," . $center_lng . ") AS distance
      FROM wp_geodatastore
     WHERE lat >=  " . $center_lat . " -(" . $radius . "/69)
       AND lat <=  " . $center_lat . " +(" . $radius . "/69)
       AND lng >=  " . $center_lng . " -(" . $radius . "/69)
       AND lng <=  " . $center_lng . " +(" . $radius . "/69)
)a
WHERE distance <= " . $radius . "
ORDER BY distance
";

注意一些关于此的事情。

首先,它使用 SQL 而不是 PHP 进行边界框计算。没有充分的理由这样做,除了将所有计算都保存在一个环境中。 (radius/69)radius 法定英里的度数。

其次,它不会根据纬度调整纵向边界框的大小。相反,它使用了一个更简单但稍微太大的边界框。这个边界框捕获了一些额外的记录,但是距离测量去掉了它们。对于典型的邮政编码/商店查找器应用程序,性能差异可以忽略不计。如果您要搜索更多的记录(例如所有电线杆的数据库),它可能不会那么简单。

第三,它使用嵌套查询来进行距离消除,以避免必须为每个项目多次运行距离函数。

第四,它按距离升序排序。这意味着您的零距离结果应该首先出现在结果集中。首先列出最近的事物通常是有意义的。

第五,它始终使用FLOAT 而不是DOUBLE。这是有充分理由的。 haversine 距离公式并不完美,因为它使地球近似为一个完美的球体。该近似值恰好在与 FLOAT 数字的 epsilon 大致相同的精度水平上分解。所以 DOUBLE 是这个问题的欺骗性数字矫枉过正。 (不要使用这个 haversine 公式来做 parking 场排水等土木工程工作,否则你会得到几个 epsilon,几英寸深的大水坑,我保证。)它适用于商店搜索应用程序。

第六,您肯定会想要为您的 lat 列创建索引。如果您的位置表不经常更改,则也有助于为您的 lng 列创建索引。但是您的 lat 索引将为您带来大部分查询性能提升。

最后,我测试了存储过程和 SQL,但没有测试 PHP。

引用:http://www.scribd.com/doc/2569355/Geo-Distance-Search-with-MySQL 还有我在医疗保健设施中使用大量接近探测器的经验。

----------------编辑--------------------

如果您没有可让您定义存储过程的用户界面,那就太麻烦了。无论如何,PHP 允许您在 sprintf 调用中使用带编号的参数,因此您可以像这样生成整个嵌套语句。注意:您可能需要 %$1f 等。您需要对此进行试验。

$sql_stmt = sprintf ("
  SELECT post_id, lat, lng
    FROM
  (
    SELECT post_id, lat, lng,
           (3959 * ACOS(COS(RADIANS(lat)) 
                 * COS(RADIANS(%$1s)) 
                 * COS(RADIANS(lng) - RADIANS(%$2s)) 
                 + SIN(RADIANS(lat)) 
                 * SIN(RADIANS(%$1s)) 
            ))
           AS distance
      FROM wp_geodatastore
     WHERE lat >=  %$1s -(%$3s/69)
       AND lat <=  %$1s +(%$3s/69)
       AND lng >=  %$2s -(%$3s/69)
       AND lng <=  %$2s +(%$3s/69)
  )a
   WHERE distance <= %$3s
   ORDER BY distance
",$center_lat,$center_lng, $radius);

关于php - 由于地理邻近度公式(商店定位器)而缺少结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8727717/

相关文章:

javascript - 使用按钮更改wordpress中的背景颜色

php - MySQL错误代码分类

php - 如何从两端对 PHP 网站进行负载测试

php - 如何延迟 Ajax 请求?

java - 获取 java.sql.SQLException : Operation not allowed after ResultSet closed

html - 无法弄清楚是什么导致网站顶部出现额外间距

php - foreach 循环在达到限制之前中断

php - 使用 pdo 按名称对行组求和

mysql - 如何将位数据类型值分配给变量?

php - Wordpress:CSS 文件不工作?