【问题标题】:Return results within a given radius using a bounding circle as a 'First Cut' in MySQL在 MySQL 中使用边界圆作为“First Cut”返回给定半径内的结果
【发布时间】:2016-04-29 21:51:56
【问题描述】:

我正在使用来自http://www.movable-type.co.uk 的 Chris Veness 的脚本我试图使用他的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 值,而是输入了一些静态值

  • $lat = 51.5529715536​​88500;
  • $lon = -3.028690575475280;
  • $rad = 25;

这不起作用...而且,我无法找到为什么它不起作用的解决方案,确切地说...尽管我认为@dan08 非常关注与他的回答如下。他比我更了解这些东西。

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

【问题讨论】:

  • mysqli 和 pdo 只是与 mysql 对话。实际查询本身并没有真正改变(除了您在准备好的语句中使用的特定类型的占位符)。绝对没有理由 select * from foo 在两个库中的行为会有所不同,除非“元”级别的某些内容不同,例如不同的账户。但是,您正在重复使用占位符名称,这是不允许的。
  • 错误 500,检查您的日志。
  • 我觉得不能在SELECT子句(或者FROM子句)中放参数,因为参数是在制定查询计划之后添加的,查询需要SELECT和FROM子句计划,不能有占位符。 This guy explains it well
  • 到目前为止,感谢您如此迅速的反应。我今天一定会尝试你的一些建议并报告回来。 @David Strachan,请您删除重复的标签,因为该问题不是指边界圆或第一次切割。相反,它只是遍历数据库中的每条记录,并通过最接近的距离吐出最近的记录。我已经具备这种能力,但现在我正在尝试对这些结果进行 FirstCut 以使我的操作运行得更快。不过谢谢你的建议。

标签: php mysqli pdo


【解决方案1】:

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

所以这是我找到解决方案的链接:Creating a Store Locator with PHP, MySQL & Google Maps

尽管本教程旨在使用 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'、'lng' 和 'radius' 值。

例如phpsqlsearch_genxml.php?lat=37&lng=-122&radius=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 ;

现在让示例数据填充表格:Click Here - 此示例数据集总共包含 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 有基本的了解,您就可以立即启动并运行它。

祝你好运!

【讨论】:

    【解决方案2】:

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

    正确。但这不是你想的那样。

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

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

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

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

    【讨论】:

    • 感谢您的建议。我不再收到错误 500(这是我的 $servername 错误)。我还添加了“or die(header("location:index.php#error1"));”在“$points->execute($params);”上手术。我现在被重定向,出现 error1 ......那个链接很有趣,但我很惭愧地承认,我还没有弄清楚。老实说,我感到有点失败 - 有什么办法可以给我一个例子,说明我的代码在完成后应该是什么样的?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-10
    • 2015-01-30
    相关资源
    最近更新 更多