简明现代魔法 -> 软件项目 -> Google Maps 创建商店定位

Google Maps 创建商店定位

2009-11-28

This tutorial is intended for developers who are familiar with PHP/MySQL, and want to learn how to use Google Maps with a MySQL database to create a store locator-type app. After completing this tutorial, you will have a database of locations and a webpage that lets a user enter their address and see markers on a map for the locations nearest to them, within a chosen distance restriction.

本教程面向熟悉PHP/MySQL的开发者并且他们想了解如何使用Google Maps和MySQL数据库去创建一个商店定位器应用。本教程结束之后,您应该有一个定位器数据库和一个可以让用户输入他们地址并且在地图上看得到他提供的地址附近的标注的页面,还可以选定距离限制。

Creating the table 创建数据库

When you create the MySQL table, you want to pay particular attention to the lat and lng attributes. With the current zoom capabilities of Google Maps, you should only need 6 digits of precision after the decimal. To keep the storage space required for our table at a minimum, you can specify that the lat and lng attributes are floats of size (10,6). That will let the fields store 6 digits after the decimal, plus up to 4 digits before the decimal, e.g. -123.456789 degrees. Your table should also have an id attribute to serve as the primary key.

当创建MySQL数据库的时候,您需要特别注意经度和纬度属性。由于Google Maps的缩放功能,您只需要精确到小数点后6位即可。为了保持我们数据表的最小存储空间要求,您可以将经度纬度的属性指定为(10,6)。这样的话,小数点后可以存储6位数字,并且小数之前可以最多有4个数字。比如-123.456789。您的数据表还应该有一个id属性去存储主键。

Note: This tutorial uses location data that already have latitude and longitude information needed to plot corresponding markers. If you're trying to use your own data that don't yet have that information, use a batch geocoding service to convert the addresses into latitudes/longitudes. You can check out our tutorial on using the Google Maps API geocoder, or check out this link for a good list of geocoders: http://groups.google.com/group/Google-Maps-API/web/resources-non-google-geocoders

注:本教程使用的位置数据已经有了可以用于标注的经纬度信息,如果您想使用自己的数据但那些数据还没有经纬度信息,可以使用批处理地理编码服务转化为纬度的地址/经度。

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 ;  

Populating the table 填充数据表

After creating the table, it's time to populate it with data. The sample data we provide below is for about 180 pizzarias scattered across the United States. In phpMyAdmin, you can use the IMPORT tab to import various file formats, including CSV (comma-separated values).

创建完数据表之后,就需要填充数据。下面已提供一些样本数据。

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');
...  

Finding locations nearby with MySQL

To find locations in our markers table that are within a certain radius distance of a given latitude/longitude, you can use a SELECT statement based on the Haversine formula. The Haversine formula is used generally for computing great-circle distances between two pairs of coordinates on a sphere. Here's the SQL statement that will find the closest 20 locations that are within a radius of 25 miles to the 37, -122 coordinate. It calculates the distance based on the latitude/longitude of that row and the target latitude/longitude, and then asks for only rows where the distance value is less than 25, orders the whole query by distance, and limits it to 20 results. To search by kilometers instead of miles, replace 3959 with 6371.

要在我们标注数据库中查找某一特定半径距离的经度/纬度的定位,您可以使用SELECT语句的矢公式计算。该矢公式用于计算之间的两对大圈的距离,一般在一个球体上的坐标。下面的SQL语句会发现最近的20个地点的25英里范围内半径的37个,-122的坐标。它计算的基础上纬度的距离/在该行的目标经度和纬度/经度,然后再行要求只有在距离值小于25,命令整个查询的距离,并限制到20个结果。要搜索的,而不是英里公里,取代6371 3959。

SELECT id, ( 3959 * acos( cos( radians(37) ) * cos( radians( lat ) ) * cos( radians( lng )
 - radians(-122) ) + sin( radians(37) ) * sin( radians( lat ) ) ) ) 
 AS distance FROM markers HAVING distance < 25 ORDER BY distance LIMIT 0 , 20;  

Outputting XML with PHP 用PHP输出XML

Now that you have the table and the SQL statement, you can put them together to output the search results into an XML format that our map can retrieve through asynchronous JavaScript calls. If the code provided here does not work with your PHP configuration, read through the Outputting XML with PHP section of our Using PHP/MySQL with Google Maps article. The only difference between the various PHP code samples shown there and the code needed for this article will be the SQL statement and the rows that are outputted in the XML.

现在,您已有经表和SQL语句,你可以把它们集合起来输出到一个XML格式的搜索结果,我们的地图可以检索通过异步JavaScript调用。如果这里提供的代码不能与你的PHP配置,通过输出用PHP的XML阅读我们的使用PHP/MySQL与谷歌地图章节。之间的各种PHP代码示例显示有唯一的区别和这篇文章需要将SQL语句,并认为在XML输出行的代码。

First, you should put your database connection information in a separate file. This is generally a good idea whenever you're using PHP to access a database, as it keeps your confidential information in a file that you won't be tempted to share. In the Maps API forum, we've occasionally had people accidentally publish their database connection information when they were just trying to debug their XML-outputting code. The file should look like this, but with your own database information filled in. phpsqlsearch_dbinfo.php:

首先,你应该放在一个单独的文件中的数据库连接信息。通常这是一个好主意时,你用PHP来访问数据库,它把一,你不会受到诱惑,分享您的机密文件资料。在地图API论坛,我们偶尔会看到有人不小心公布他们的数据库连接信息时,他们只是尝试调试他们的XML输出代码。该文件应该是这样的,但你自己的数据库中的信息填写phpsqlsearch_dbinfo.php:

<?
$username="username";
$password="password";
$database="username-databaseName";
?>  

Using PHP's DOM functions to output XML 使用PHP的DOM函数输出XML

First, check your configuration and see if you're using PHP5. If so, continue to the explanation and code below. If you aren't, then you can use echo to output XML, as used in this sample code. In PHP, first initialize a new XML document and create the "markers" parent node. Then connect to the database, execute the SELECT by distance query discussed above on the markers table, and iterate through the results.For each row in the table (each location), create a new XML node with the row attributes as XML attributes, and append it to the parent node. Then dump the XML to the screen.

首先,检查您的配置,看看您使用PHP5的。如果是这样,继续解释和代码如下。如果你没有,那么你可以使用echo输出XML,因为在此示例代码中使用。 在PHP中,首先初始化一个新的XML文档,并创建“标志”的父节点。然后连接到数据库,执行者讨论了上述距离标记表的SELECT查询,并通过表中(每个位置),results.For迭代每一行创建一个新的XML与该行的XML节点的属性,属性和追加到父节点。然后将XML转储到屏幕上。

Note: Since this PHP sends user input in a MySQL statement, this code uses the mysql_real_escape_string technique of avoiding SQL injection. A more elegant solution may be to use MySQL prepared statements, though implementation varies on the PHP version and DB wrapper used.

注意:由于此PHP发送一个MySQL声明中用户输入,此代码使用避免SQL注入mysql_real_escape_string技术。一个更完美的解决方案可能是使用MySQL准备好的发言,但执行情况有所不同,PHP的版本和使用DB包装。

Note: If your database contains international characters or you otherwise need to force UTF-8 output, you can use utf8_encode on the outputted data.

注意:如果您的数据库包含国际字符或者其他需要强制的UTF - 8输出,您可以使用输出数据函数utf8_encode。

The PHP file that does all that is shown below (phpsqlsearch_genxml.php):

<?php  
require("phpsqlsearch_dbinfo.php");

// Get parameters from URL
$center_lat = $_GET["lat"];
$center_lng = $_GET["lng"];
$radius = $_GET["radius"];

// Start XML file, create parent node
$dom = new DOMDocument("1.0");
$node = $dom->createElement("markers");
$parnode = $dom->appendChild($node);

// 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 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 markers 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);

$result = mysql_query($query);
if (!$result) {
  die("Invalid query: " . mysql_error());
}

header("Content-type: text/xml");

// Iterate through the rows, adding XML nodes for each
while ($row = @mysql_fetch_assoc($result)){
  $node = $dom->createElement("marker");
  $newnode = $parnode->appendChild($node);
  $newnode->setAttribute("name", $row['name']);
  $newnode->setAttribute("address", $row['address']);
  $newnode->setAttribute("lat", $row['lat']);
  $newnode->setAttribute("lng", $row['lng']);
  $newnode->setAttribute("distance", $row['distance']);
}

echo $dom->saveXML();
?>  

Checking that the XML output works

Call this PHP script from the browser to make sure it's producing valid XML. If you suspect there's a problem with connecting to your database, you may find it easier to debug if you remove the line in the file that sets the header to the text/xml content type, as that usually causes your browser to try to parse XML and may make it difficult to see your debugging messages.

从浏览器调用这个PHP脚本,以确保它的生产有效的XML。如果您怀疑有问题的连接到您的数据库,您可能会发现更容易调试如果删除该文件中的行头设置文本/ XML内容类型,通常导致您的浏览器,试图解析XML并可能难以看到您的调试信息。

If the script is working correctly and you append reasonable sample query parameters to the URL (e.g. ?lat=37&lng=-122&radius=25), you should see XML output like this (phpsqlsearch_expectedoutput.xml):

如果脚本运行正常和合理的样本中追加查询参数的URL(例如?纬度= 37&经度=- 122&半径= 25),你应该看到类似(phpsqlsearch_expectedoutput.xml XML输出):

<?xml version="1.0"?>
<markers><marker name="Round Table Pizza: Mountain View" address="570 N Shoreline Blvd, Mountain View, CA" lat="37.402653" lng="-122.079353" distance="0.38091455044131"/>
<marker name="Kapp's Pizza Bar & Grill" address="191 Castro St, Mountain
     View, CA" lat="37.393887" lng="-122.078918" distance="0.5596115438175"/>
<marker name="Amici's East Coast Pizzeria" address="790 Castro St, Mountain View, CA" lat="37.387138" lng="-122.083237" distance="1.0796074495809"/>
<marker name="Frankie Johnnie & Luigo Too" address="939 W El Camino Real, Mountain View, CA" lat="37.386337" lng="-122.085823" distance="1.2044231336188"/>
<marker name="Tony & Alba's Pizza & Pasta" address="619 Escuela Ave, Mountain View, CA" lat="37.394012" lng="-122.095528" distance="1.3156538737837"/><marker name="Round Table Pizza: Sunnyvale-Mary-Central Expy" address="415 N Mary Ave, Sunnyvale, CA" lat="37.390038" lng="-122.042030" distance="1.84565061776"/>
<marker name="Oregano's Wood-Fired Pizza" address="4546 El Camino Real, Los Altos, CA" lat="37.401726" lng="-122.114647" distance="2.2887425990519"/>
...
</markers>  

Creating the map 创建地图

Once the XML is working in the browser, it's time to move on to actually creating the map with JavaScript. If you have never created a Google Map, please try some of the basic examples in the documentation to make sure you understand the basics of creating a Google Map.

一旦XML是工作在浏览器,现在是时候继续前进,以实际使用JavaScript创建地图。如果您从未创建了谷歌地图,请尝试在文件中的一些基本的例子,以确保您了解创建一个谷歌地图的基础知识。

Searching near a geocode 搜索附近的地理编码

Our PHP script takes latitude and longitude parameters in order to perform the search. Since most people who use your map will know their address but not their coordinates, you can use the GClientGeocodersearchLocations function shown below which just passes in the address from the textbox to the asynchronous GClientGeocoder.getLatLng() function, gets a GLatLng in response, and sends it off to the searchLocationsNear function if the geocode was successful.

我们的PHP脚本需要的经度和纬度参数,以执行搜索。由于大多数人使用你的地图谁知道他们的地址,但不是他们的坐标,您可以使用GClientGeocodersearchLocations功能如下刚刚通过的从TextBox地址异步GClientGeocoder.getLatLng()函数,得到的答复为GLatLng,和它发送到起飞searchLocationsNear函数,如果成功的地理编码。

function searchLocations() {
  var address = document.getElementById('addressInput').value;
  geocoder.getLatLng(address, function(latlng) {
    if (!latlng) {
      alert(address + ' not found');
    } else {
      searchLocationsNear(latlng);
    }
  });
}  

The searchLocationsNear function shown below will pass the latitude and longitude values to our PHP script, and use the asynchronous GDownloadURL function to load the XML outputted into our page. GDownloadURL is a wrapper for the XMLHttpRequest that's used to request an XML file from the server where the HTML page resides. It takes the URL as the first parameter and passes the retrieved data to the callback function, if successful.

该searchLocationsNear函数所示将通过经度和纬度值我们的PHP脚本,并使用异步GDownloadURL函数加载到我们的页面输出的XML。 GDownloadURL是一个XMLHttpRequest的包装器的使用要求,从服务器的XML文件,其中的HTML页所在。它采用作为第一个参数的URL和检索的数据传递给回调函数,如果成功的话。

In the callback function, you need to find all the "marker" elements in the XML, and iterate through them. For each marker element you find, retrieve the name, address, distance, and lat/lng attributes and pass them to createMarker and createSidebarEntry to create the marker and sidebar entry. You can also figure out the optimal viewport for the returned results by using GLatLngBounds.extend, GLatLngBounds.getCenter and GMap2.getBoundsZoomLevel.

在回调函数中,你需要找到所有的“标志”的XML元素,并通过他们迭代。对于每一个标记元素您查找,检索的名称,地址,距离和纬度/经度的属性,并通过他们createMarker和createSidebarEntry创建标记和补充工具栏项。您还可以计算出了用GLatLngBounds.extend,GLatLngBounds.getCenter和GMap2.getBoundsZoomLevel返回的结果的最佳视点。

function searchLocationsNear(center) {
  var radius = document.getElementById('radiusSelect').value;
  var searchUrl = 'phpsqlsearch_genxml.php?lat=' + center.lat() + '&lng=' + center.lng() + '&radius=' + radius;
  GDownloadUrl(searchUrl, function(data) {
    var xml = GXml.parse(data);
    var markers = xml.documentElement.getElementsByTagName('marker');
    map.clearOverlays();

    var sidebar = document.getElementById('sidebar');
    sidebar.innerHTML = '';
    if (markers.length == 0) {
      sidebar.innerHTML = 'No results found.';
      map.setCenter(new GLatLng(40, -100), 4);
      return;
    }

    var bounds = new GLatLngBounds();
    for (var i = 0; i < markers.length; i++) {
      var name = markers[i].getAttribute('name');
      var address = markers[i].getAttribute('address');
      var distance = parseFloat(markers[i].getAttribute('distance'));
      var point = new GLatLng(parseFloat(markers[i].getAttribute('lat')),
                              parseFloat(markers[i].getAttribute('lng')));

      var marker = createMarker(point, name, address);
      map.addOverlay(marker);
      var sidebarEntry = createSidebarEntry(marker, name, address, distance);
      sidebar.appendChild(sidebarEntry);
      bounds.extend(point);
    }
    map.setCenter(bounds.getCenter(), map.getBoundsZoomLevel(bounds));
  });
}  

Creating markers & the sidebar 创建标注和侧栏

In the createMarker function shown below, you just create a marker at the given GLatLng, and add an event listener to the marker so that when clicked, an info window is displayed showing the name and address.

在下面显示的createMarker功能,您只需创建一个给定的GLatLng标记,并添加一个事件监听器的标记,以便当点击一个信息窗口显示显示名称和地址。

function createMarker(point, name, address) {
  var marker = new GMarker(point);
  var html = '<b>' + name + '</b> <br/>' + address;
  GEvent.addListener(marker, 'click', function() {
    marker.openInfoWindowHtml(html);
  });
  return marker;
}  

In the createSidebarEntry function shown below, you create a div with the name, address, and distance information, and then add event listeners to the div so that it changes background color on mouseover (a nice UI touch), and opens the info window over the marker on click by triggering the marker's click event.

在下面显示的createSidebarEntry功能,创建一个名为科,地址和距离信息,然后添加事件侦听器的分区,以便它在鼠标悬停改变背景颜色(一个很好的用户界面接触),并打开了信息窗口由触发标志的单击事件标记上点击。

function createSidebarEntry(marker, name, address, distance) {
  var div = document.createElement('div');
  var html = '' + name + ' (' + distance.toFixed(1) + ')
' + address;
  div.innerHTML = html;
  div.style.cursor = 'pointer';
  div.style.marginBottom = '5px';
  GEvent.addDomListener(div, 'click', function() {
    GEvent.trigger(marker, 'click');
  });
  GEvent.addDomListener(div, 'mouseover', function() {
    div.style.backgroundColor = '#eee';
  });
  GEvent.addDomListener(div, 'mouseout', function() {
    div.style.backgroundColor = '#fff';
  });
  return div;
}  

Putting it all together 将以上的代码放到一起

Here's a screenshot and code for the webpage that ties everything together (phpsqlsearch_map.html). When the page loads, the load function is called. This function sets up the map and initializes the geocoder.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">

  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8"/>
    <title>Google Maps AJAX + MySQL/PHP Example</title>
    <script src="http://maps.google.com/maps?file=api&v=2&key=abcdef"
            type="text/javascript"></script>

    <script type="text/javascript">
    //<![CDATA[
    var map;
    var geocoder;

    function load() {
      if (GBrowserIsCompatible()) {
        geocoder = new GClientGeocoder();
        map = new GMap2(document.getElementById('map'));
        map.addControl(new GSmallMapControl());
        map.addControl(new GMapTypeControl());
        map.setCenter(new GLatLng(40, -100), 4);
      }
    }

   function searchLocations() {
     var address = document.getElementById('addressInput').value;
     geocoder.getLatLng(address, function(latlng) {
       if (!latlng) {
         alert(address + ' not found');
       } else {
         searchLocationsNear(latlng);
       }
     });
   }

   function searchLocationsNear(center) {
     var radius = document.getElementById('radiusSelect').value;
     var searchUrl = 'phpsqlsearch_genxml.php?lat=' + center.lat() + '&lng=' + center.lng() + '&radius=' + radius;
     GDownloadUrl(searchUrl, function(data) {
       var xml = GXml.parse(data);
       var markers = xml.documentElement.getElementsByTagName('marker');
       map.clearOverlays();

       var sidebar = document.getElementById('sidebar');
       sidebar.innerHTML = '';
       if (markers.length == 0) {
         sidebar.innerHTML = 'No results found.';
         map.setCenter(new GLatLng(40, -100), 4);
         return;
       }

       var bounds = new GLatLngBounds();
       for (var i = 0; i < markers.length; i++) {
         var name = markers[i].getAttribute('name');
         var address = markers[i].getAttribute('address');
         var distance = parseFloat(markers[i].getAttribute('distance'));
         var point = new GLatLng(parseFloat(markers[i].getAttribute('lat')),
                                 parseFloat(markers[i].getAttribute('lng')));
         
         var marker = createMarker(point, name, address);
         map.addOverlay(marker);
         var sidebarEntry = createSidebarEntry(marker, name, address, distance);
         sidebar.appendChild(sidebarEntry);
         bounds.extend(point);
       }
       map.setCenter(bounds.getCenter(), map.getBoundsZoomLevel(bounds));
     });
   }

    function createMarker(point, name, address) {
      var marker = new GMarker(point);
      var html = '<b>' + name + '</b> <br/>' + address;
      GEvent.addListener(marker, 'click', function() {
        marker.openInfoWindowHtml(html);
      });
      return marker;
    }

    function createSidebarEntry(marker, name, address, distance) {
      var div = document.createElement('div');
      var html = '<b>' + name + '</b> (' + distance.toFixed(1) + ')<br/>' + address;
      div.innerHTML = html;
      div.style.cursor = 'pointer';
      div.style.marginBottom = '5px'; 
      GEvent.addDomListener(div, 'click', function() {
        GEvent.trigger(marker, 'click');
      });
      GEvent.addDomListener(div, 'mouseover', function() {
        div.style.backgroundColor = '#eee';
      });
      GEvent.addDomListener(div, 'mouseout', function() {
        div.style.backgroundColor = '#fff';
      });
      return div;
    }
    //]]>

  </script>
  </head>

  <body onload="load()" onunload="GUnload()">
    Address: <input type="text" id="addressInput"/>
     

    Radius: <select id="radiusSelect">

      <option value="25" selected>25</option>
      <option value="100">100</option>

      <option value="200">200</option>

    </select>

    <input type="button" onclick="searchLocations()" value="Search Locations"/>
    <br/>    
    <br/>
<div style="width:600px; font-family:Arial, 
sans-serif; font-size:11px; border:1px solid black">
  <table> 
    <tbody> 
      <tr id="cm_mapTR">

        <td width="200" valign="top"> <div id="sidebar" style="overflow: auto; height: 400px; font-size: 11px; color: #000"></div>

        </td>
        <td> <div id="map" style="overflow: hidden; width:400px; height:400px"></div> </td>

      </tr> 
    </tbody>
  </table>
</div>    
  </body>
</html>  

随机文章推荐
网站分类


注:如需转载本文,请注明出处(原文链接),谢谢。更多精彩内容,请进入简明现代魔法首页。

进入新博客
喜欢本文,就分享它吧
给我留言
您的名字:
您的邮件:
您的网站:


 

copyright © 2009 简明现代魔法    学习、分享、进步

power by Gonn 感谢所有关心和支持本站的朋友们