使用边界圆作为';第一次剪切';在MySQL中


Return results within a given radius using a bounding circle as a 'First Cut' in MySQL

我使用Chris Veness的脚本http://www.movable-type.co.uk我试图使用他的Bounding Circle脚本,对MySQL数据库运行一个查询,只返回位于给定半径内的行。如下:

<?php 
require 'inc/dbparams.inc.php';  // defines $dsn, $username, $password
$db = new PDO($dsn, $username, $password);
$db->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);
$lat = $_GET['lat']; // latitude of centre of bounding circle in degrees
$lon = $_GET['lon']; // longitude of centre of bounding circle in degrees
$rad = $_GET['rad']; // radius of bounding circle in kilometers
$R = 6371;  // earth's mean radius, km
// 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)));
$sql = "Select Id, Postcode, Lat, Lon,
            acos(sin(:lat)*sin(radians(Lat)) + cos(:lat)*cos(radians(Lat))*cos(radians(Lon)-:lon)) * :R As D
        From (
            Select Id, Postcode, Lat, Lon
            From MyTable
            Where Lat Between :minLat And :maxLat
              And Lon Between :minLon And :maxLon
        ) As FirstCut
        Where acos(sin(:lat)*sin(radians(Lat)) + cos(:lat)*cos(radians(Lat))*cos(radians(Lon)-:lon)) * :R < :rad
        Order by D";
$params = array(
    'lat'    => deg2rad($lat),
    'lon'    => deg2rad($lon),
    'minLat' => $minLat,
    'minLon' => $minLon,
    'maxLat' => $maxLat,
    'maxLon' => $maxLon,
    'rad'    => $rad,
    'R'      => $R,
);
$points = $db->prepare($sql);
$points->execute($params);
?>
<html>
<table>
<? foreach ($points as $point): ?>
<tr>
    <td><?= $point->Postcode ?></td>
    <td><?= number_format($point->D,1) ?></td>
    <td><?= number_format($point->Lat,3) ?></td>
    <td><?= number_format($point->Lon,3) ?></td>
</tr>
<? endforeach ?>
</table>
</html>

我重命名了数据库中现有的列,以匹配Chris Veness使用的列。我没有使用$_GET值,而是输入了的一些静态值

  • 拉特=51.552971553688500美元
  • $lon=-3.028690575475280
  • $rad=25

这不起作用。。。此外,我找不到为什么不起作用的解决方案,确切地说。。。尽管我认为@dan08在下面的回答中很有意思。他比我更了解这些东西。

尽管如此,我终于有了一个可行的解决方案!请看下面我的答案。

Google Devs的那些人是一群聪明的人!

下面是我找到解决方案的链接:用PHP、MySQL和MySQL创建一个商店定位器;谷歌地图

尽管本教程旨在使用Google Maps API,但本教程的前半部分侧重于使用PHP查询数据库并创建边界圆,以便在给定半径内搜索匹配项,并仅返回匹配的结果。

在本教程中,查询是超快速的,并以XML输出结果,这对于集成到API非常有用。我不需要这个功能,所以我稍微简化了一下。

以下是我所拥有的,它非常适合我的需求:

创建一个名为:phpsqlsearch_dbinfo.php的页面

<?
$username="Your_Database_Username";
$password="Your_Database_Password";
$database="The_Name_of_Your_Database";
?>

创建一个名为:phpsqlsearch_genxml.php的新页面

当访问此页面时,我们将一些值传递给它,因为我们使用$_GET来收集"lat"、"ng"answers"radius"值。

例如phpsqlsearch_genxml.php?lat=37&lng=-122&半径=25

<?php  
require("phpsqlsearch_dbinfo.php");
// Get parameters from URL
$center_lat = $_GET["lat"];
$center_lng = $_GET["lng"];
$radius = $_GET["radius"];
// Opens a connection to a mySQL server
$connection=mysql_connect (localhost, $username, $password);
if (!$connection) {
  die("Not connected : " . mysql_error());
}
// Set the active mySQL database
$db_selected = mysql_select_db($database, $connection);
if (!$db_selected) {
  die ("Can''t use db : " . mysql_error());
}
// Search the rows in the markers table
$query = sprintf("SELECT id, address, name, lat, lng, ( 3959 * acos( cos( radians('%s') ) * cos( radians( lat ) ) * cos( radians( lng ) - radians('%s') ) + sin( radians('%s') ) * sin( radians( lat ) ) ) ) AS distance FROM candidates HAVING distance < '%s' ORDER BY distance LIMIT 0 , 20",
  mysql_real_escape_string($center_lat),
  mysql_real_escape_string($center_lng),
  mysql_real_escape_string($center_lat),
  mysql_real_escape_string($radius));
$result = mysql_query($query);
if (!$result) {
  die("Invalid query: " . mysql_error());
}
while ($row = @mysql_fetch_assoc($result)){
  $ID = mysql_real_escape_string($row['id']);
  $name = mysql_real_escape_string($row['name']);
  $address = mysql_real_escape_string($row['address']);
  $lat = mysql_real_escape_string($row['lat']);
  $lng = mysql_real_escape_string($row['lng']);
  $distance = mysql_real_escape_string($row['distance']);
  echo $name .", ". $address .", Latitude:". $lat .", Longitude:". $lng .", Distance From Home = ". round($distance)." Miles <br /><br />";
   // I then insert these matches into a new table for later use. 
   $new = "INSERT INTO matrix (Marker_ID, Cand_Name, Distance)
    VALUES ('$ID', '$name', '$distance')";
   $resulting = mysql_query($new);
   if (!$resulting) {
     die("Invalid query: " . mysql_error());
   }   
}
?>

最后,为了让这个例子发挥作用,您需要设置数据库。如果您不能访问phpMyAdmin,或者更喜欢使用SQL命令,下面是创建表的SQL语句:

CREATE TABLE `markers` (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`name` VARCHAR( 60 ) NOT NULL ,
`address` VARCHAR( 80 ) NOT NULL ,
`lat` FLOAT( 10, 6 ) NOT NULL ,
`lng` FLOAT( 10, 6 ) NOT NULL
) ENGINE = MYISAM ;

现在,对于要填充表的示例数据:单击此处-此示例数据集总共包含169行。如果你按照上面的链接,你可以复制以下格式的完整数据集:

INSERT INTO `markers` (`name`, `address`, `lat`, `lng`) VALUES ('Frankie Johnnie & Luigo Too','939 W El Camino Real, Mountain View, CA','37.386339','-122.085823');
INSERT INTO `markers` (`name`, `address`, `lat`, `lng`) VALUES ('Amici''s East Coast Pizzeria','790 Castro St, Mountain View, CA','37.38714','-122.083235');
INSERT INTO `markers` (`name`, `address`, `lat`, `lng`) VALUES ('Kapp''s Pizza Bar & Grill','191 Castro St, Mountain View, CA','37.393885','-122.078916');
INSERT INTO `markers` (`name`, `address`, `lat`, `lng`) VALUES ('Round Table Pizza: Mountain View','570 N Shoreline Blvd, Mountain View, CA','37.402653','-122.079354');
INSERT INTO `markers` (`name`, `address`, `lat`, `lng`) VALUES ('Tony & Alba''s Pizza & Pasta','619 Escuela Ave, Mountain View, CA','37.394011','-122.095528');
INSERT INTO `markers` (`name`, `address`, `lat`, `lng`) VALUES ('Oregano''s Wood-Fired Pizza','4546 El Camino Real, Los Altos, CA','37.401724','-122.114646');
INSERT INTO `markers` (`name`, `address`, `lat`, `lng`) VALUES ('Round Table Pizza: Sunnyvale-Mary-Central Expy','415 N Mary Ave, Sunnyvale, CA','37.390038','-122.042034');
INSERT INTO `markers` (`name`, `address`, `lat`, `lng`) VALUES ('Giordano''s','730 N Rush St, Chicago, IL','41.895729','-87.625411');
INSERT INTO `markers` (`name`, `address`, `lat`, `lng`) VALUES ('Filippi''s Pizza Grotto','1747 India St, San Diego, CA','32.723831','-117.168326');
INSERT INTO `markers` (`name`, `address`, `lat`, `lng`) VALUES ('Lou Malnati''s Pizzeria','439 N Wells St, Chicago, IL','41.890346','-87.633927');
etc...
etc...

我希望这能帮助到像我一样苦苦挣扎的人。只要对PHP和MySQL有基本的了解,您就可以很快启动并运行它。

祝你好运!

我认为罪魁祸首在于$sql函数内部的计算。

正确。但这不是你想的。

您的主要问题是在select子句中使用占位符lie :lat。这个评论很好地解释了这个问题。但最重要的是,不能在SELECT和FROM子句中绑定参数,因为它们是在添加参数之前解析的。

使用PDO或MySQLi不会改变这一点。因此,要将这些变量添加到SELECT子句中,请对它们进行适当的清理,并将它们作为字符串插入查询中。

你正在使用我最喜欢的PDO功能之一,命名占位符。所以我建议坚持PDO。

若要调试500错误,您需要检查服务器日志。如果你有一个典型的LAMP堆栈,可能是/var/log/apache2/error.log