使用边界圆作为MySQL中的“First Cut”返回给定半径内的结果

时间:2016-04-29 21:51:57

标签: php mysqli pdo

我正在使用来自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非常关注某些事情他的回答如下。他比我更了解这些东西。

尽管如此 - 我[终于]有一个有效的解决方案!请参阅下面的答案。

2 个答案:

答案 0 :(得分: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来收集&#39; lat,&#39; lng&#39;和&#39; radius&#39;值。

EG的 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 ;

现在填充表的示例数据: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的基本了解,您可以立即启动并运行它。

祝你好运!

答案 1 :(得分:0)

  

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

正确。但这不是你在想什么。

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

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

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

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