PhpRiot
Become Zend Certified

Prepare for the ZCE exam using our quizzes (web or iPad/iPhone). More info...


When you're ready get 7.5% off your exam voucher using voucher CJQNOV23 at the Zend Store
Related Articles

Geocoding with PHP and the Google Maps API

Creating the Geocoder Class

We will now implement a PHP 5 solution for easily using the Google geocoder. We will achieve this by creating three classes:

  • Geocoder: This will be the class used to perform the geocoder request and to read the response.
  • Placemark: This class will be used to hold each returned location from the geocoder.
  • Point: We will store longitude and latitude data using this class.

While you could implement a solution using a single class, I have chosen to do as just outlined so you can easily drop it in to your own applications. It also hopefully demonstrates some good principles for object oriented development in PHP.

Let's begin by creating the Geocoder class. This class will contain a method called lookup(), to which the address to be geocoded is accepted as the only argument. From this method an array of Placemark objects will be returned.

The code for this class should reside in a file called Geocoder.php. The complete file is available from the article file downloads section.

Listing 7 shows the beginning of this class. Firstly we define the URL of the geocoder (which we learned earlier was http://maps.google.com/maps/geo). We then define a number of constants to correspond to each of the status codes that can be returned. Using constants makes the code much easier to read and maintain. For consistency we use the same names as the JavaScript API does, documented at http://code.google.com/apis/maps/documentation/reference.html#GGeoStatusCode.

This listing concludes by defining the class constructor, which accepts your Google Maps API key as its only argument.

Listing 7 Initializing the Geocoder class (listing-7.php)
<?php
    class Geocoder
    {
        public static $url = 'http://maps.google.com/maps/geo';
 
        const G_GEO_SUCCESS             = 200;
        const G_GEO_BAD_REQUEST         = 400;
        const G_GEO_SERVER_ERROR        = 500;
        const G_GEO_MISSING_QUERY       = 601;
        const G_GEO_MISSING_ADDRESS     = 601;
        const G_GEO_UNKNOWN_ADDRESS     = 602;
        const G_GEO_UNAVAILABLE_ADDRESS = 603;
        const G_GEO_UNKNOWN_DIRECTIONS  = 604;
        const G_GEO_BAD_KEY             = 610;
        const G_GEO_TOO_MANY_QUERIES    = 620;
 
        protected $_apiKey;
 
        public function __construct($key)
        {
            $this->_apiKey = $key;
        }
 
        // other code
    }
?>

As mentioned above, we will be implementing a method called lookup(), used to return an array of Placemark objects. Internally, as we will soon see this method will be responsible for reading the geocoder response. We firstly need a way to retrieve that response, which we will do using the performRequest() method.

Listing 8 shows the code for performRequest(), which accepts the location to geocoder as its first argument and the output type as its second argument. While we will only be using the XML output, coding the method in this manner allows you to easily reuse it for other output types (such as JSON) if required.

This method uses the PHP Curl functions to perform the HTTP request. In order to retrieve the response as a string, the CURLOPT_RETURNTRANSFER option must be set to true. Not doing so would result in the response being output directly to your browser.

Listing 8 The performRequest() method, used to send the request to Google and retrieve the response (listing-8.php)
<?php
    class Geocoder
    {
        // other code
        
        public function performRequest($search, $output = 'xml')
        {
            $url = sprintf('%s?q=%s&output=%s&key=%s&oe=utf-8',
                           self::$url,
                           urlencode($search),
                           $output,
                           $this->_apiKey);
 
            $ch = curl_init($url);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            $response = curl_exec($ch);
            curl_close($ch);
 
            return $response;
        }
        
        // other code
    }
?>

We can now implement the lookup() method, which is shown in Listing 9. This method begins by retrieving the XML response using the performRequest() method. An instance of SimpleXMLELement is then created using this response.

Next we read the returned status value to determine the response. The way I have coded this is to assume a request was successful if the status code is G_GEO_SUCCESS, G_GEO_UNKNOWN_ADDRESS or G_GEO_UNAVAILABLE_ADDRESS. The latter two, while indicating a response that includes no placemarks, still indicate that a search successfully occurred (that is, the request was valid, as was the API key), so we return an empty array (that is, no placemarks).

Note: Technically speaking, G_GEO_UNKNOWN_ADDRESS and G_GEO_UNAVAILABLE_ADDRESS correspond to the same value. I have included both for completeness. If Google decide to use a different value for one of these, our code would only need to be updated in the definitions at the start of the class.

If the status code is G_GEO_SUCCESS, we loop over the returned placemarks and create a Placemark object for each one. Each Placemark object is added to the return array. We will cover the FromSimpleXml() method when we implement the Placemark class. For now, all you need to know is that it returns an instance of Placemark.

For every other status code, we assume some kind of error occurred. To deal with this error, we throw an exception in which the returned status code is included in the exception message.

Listing 9 Implementing the lookup() method, which returns an array of Placemark objects (listing-9.php)
<?php
    class Geocoder
    {
        // other code
        
        public function lookup($search)
        {
            $response = $this->performRequest($search, 'xml');
            $xml      = new SimpleXMLElement($response);
            $status   = (int) $xml->Response->Status->code;
 
            switch ($status) {
                case self::G_GEO_SUCCESS:
                    require_once('Placemark.php');
 
                    $placemarks = array();
                    foreach ($xml->Response->Placemark as $placemark)
                        $placemarks[] = Placemark::FromSimpleXml($placemark);
 
                    return $placemarks;
 
                case self::G_GEO_UNKNOWN_ADDRESS:
                case self::G_GEO_UNAVAILABLE_ADDRESS:
                    return array();
 
                default:
                    throw new Exception(sprintf('Google Geo error %d occurred', $status));
            }
        }
    }
?>

This now completes the Geocoder class. If you want to use the performRequest() method on its own, you can now do so, however we must first implement the Placemark class before you can use the lookup() method.

Listing 10 shows the code you can use to test out the performRequest() method. Remember to use your own API key when instantiating the Geocoder class.

Listing 10 A test script to view the raw output from different request types (test.php) (listing-10.php)
<?php
    require_once('Geocoder.php');
 
    $address  = '1600 Pennsylvania Ave Washington DC';
    $geocoder = new Geocoder('your key');
 
    $xml  = $geocoder->performRequest($address, 'xml');
    $json = $geocoder->performRequest($address, 'json');
    $csv  = $geocoder->performRequest($address, 'csv');
?>
<html>
    <head>
        <title>Testing the performRequest() method</title>
    </head>
    <body>
        <div>
            <h1>Testing the performRequest() method</h1>
 
            <h2>Address: <?php echo $address ?></h2>
 
            <h3>XML</h3>
            <?php echo htmlSpecialChars($xml) ?>
 
            <h3>JSON</h3>
            <?php echo htmlSpecialChars($json) ?>
 
            <h3>CSV</h3>
            <?php echo htmlSpecialChars($csv) ?>
        </div>
    </body>
</html>

In This Article


Additional Files