When using maps on a website, Google is clearly the service to go with. If you are using JavaScript only, parsing a JSON response from Google shouldn´t be difficult, but how to use the response in PHP? In this article, I´ll describe how to use Zend Framework components, but with a link to How-To´s with standard PHP functions, to

  1. Send a request to Google Maps API
  2. Get the response in JSON format (no problem to switch to XML)
  3. Parse the response from Google to a more readable format
  4. And finally, use those values in a PHP script

I needed the response for the meta geotags on surfspot.de, where I use a lot of maps to show the locations of… surfspots, obviously. The geotag for a spot would be something like this:

<meta name="geo.region" content="COUNTRY_SHORT-REGION_SHORT" />
<meta name="geo.placename" content="CITY" />
<meta name="geo.position" content="LATITUDE;LONGITUDE" />
<meta name="ICBM" content="LATITUDE, LONGITUDE" />

… with latitude and longitude known (stored in database) , and city, region and country to find out from Google. Pls find details about HTML geotags on Wikipedia. The meaning of the ICBM tag is just hilarious 😀 For details on Google Geocoding, pls refer to their documentation.

Fist, we need the class to query Google:

class JD_Geocoder_Request
{
 /**
 * @class vars
 */

 // Google´s geocode URL
 public $url = 'http://maps.google.com/maps/api/geocode/json?';

 // Params for request
 public $sensor       = "false"; // REQUIRED FOR REQUEST!
 public $language     = "en";

 // Class vars
 public $response     = '';
 public $country_long = '';
 public $country_short= '';
 public $region_long  = '';
 public $region_short = '';
 public $city         = '';
 public $address      = '';
 public $lat          = '';
 public $lng          = '';
 public $location_type= '';

 /**
 * Constructor
 *
 * @param mixed $config
 * @return void
 */
 public function __construct($config = null)
 {

 }

} // end class

I prefer to store all results I may or may not need in class vars, so I can just echo them out.

Second, we need to know what kind of search we need to perform, forward geocoding with an address (say, from a form), or a reverse geocoding search, using lat and lng for the query. I implemented both methods, but pls be aware I didn´t test the forward search… since I don´t need it yet 😉

/**
 * Forward search: string must be an address
 *
 * @param string $address
 * @return obj $response
 */
 public function forwardSearch($address)
 {
   return $this->_sendRequest("address=" . urlencode(stripslashes($address)));
 } // end forward

 /**
 * Reverse search: string must be latitude and longitude
 *
 * @param float $lat
 * @param float $lng
 * @return obj $response
 */
 public function reverseSearch($lat, $lng)
 {
   return $this->_sendRequest("latlng=" . (float) $lat . ',' . (float) $lng);
 } // end reverse

Both methods handle formatting the parameters, as well. If you want to handle just the response object, you can skip ahead and ignore the next functions. As said earlier, I like to store the response in class vars in order to just echo them out, or store them in a database. The two functions set the defaults I need to know, and search for other values as well. Pls note that only the response object $address_components is used for the search, Google may or may not return a lot more – check their docs for this.

/**
 * Search Address Components Object
 *
 * @param string $type
 * @return object / false
 */
 public function searchAddressComponents($type) {
   foreach($this->response->results[0]->address_components as $k=>$found){
     if(in_array($type, $found->types)){
       return $found;
     }
   }
   return false;
 }

/**
 * Parse JSON default values: map object values to readable content
 *
 * @param none
 * @return none
 */
 private function _setDefaults()
 {
   $country = $this->searchAddressComponents("country");
   $this->country_long    = $country->long_name;
   $this->country_short    = $country->short_name;
   $region = $this->searchAddressComponents("administrative_area_level_1");
   $this->region_long = $region->long_name;
   $this->region_short    = $region->short_name;
   $city = $this->searchAddressComponents("locality");
   $this->city    = $city->short_name;
   $this->address = $this->response->results[0]->formatted_address;
   $this->lat = $this->response->results[0]->geometry->location->lat;
   $this->lng = $this->response->results[0]->geometry->location->lng;
   $this->location_type = $this->response->results[0]->geometry->location_type;
 } // end set

The JSON response from Google is likely to change, so you need to control the mapping every now and then. If you use the reverse geocoding, pls note that lat and lng may NOT be the same values you used to call the search with, but refer to the next best address, even if location_type is returned as „rooftop“. This doesn´t matter if you search on land, but surfspots might be 500m down a beach from a known location, at least, known to Google.

Now for the slightly more difficult part… the actual request to Google Maps API, and parsing the JSON response. Using the Zend Framework, sending a request to Google is a tad bit easier than using pure PHP. If you don´t use ZF and/or use XML response format, check this article on phpRiot, and replace the $client code.

/**
 * Send Google geocoding request
 *
 * @param string $search
 * @return object response (body only)
 */
 private function _sendRequest($search)
 {
   $client = new Zend_Http_Client();
   $client->setUri($this->url . $search . '&language=' . strtolower($this->language) . '&sensor=' . strtolower($this->sensor));
   $client->setConfig(array(
     'maxredirects' => 0,
     'timeout'      => 30));
   $client->setHeaders(array(
     'Accept-encoding' => 'json',
     'X-Powered-By' => 'Zend Framework GEOCMS by Joerg Drzycimski'));
   $response = $client->request();
   $body = $response->getBody();
   $this->response = Zend_Json::decode($body, Zend_Json::TYPE_OBJECT);
   if ($this->response->status == "OK") {
     // set some default values for reading
     $defaults = $this->_setDefaults();
     return $this->response;
   } else {
     echo "Geocoding failed, server responded: " . $this->response->status;
     return false;
   }
 } // end request

Now that you have the request up and running, the code requirde to generate the meta geotags is quite simple:

$georequest = new JD_Geocoder_Request();
$lat = '54.424899';
$lng = '11.096671';
$georequest->reverseSearch($lat,$lng);
echo '<meta name="geo.region" content="' . $georequest->country_short . '-' . $georequest->region_short . '" />';
echo '<meta name="geo.placename" content="' . $georequest->city . '" />';
echo '<meta name="geo.position" content="' . $lat . ';' . $lng . '" />';
echo '<meta name="ICBM" content="' . $lat . ',' . $lng . '" />';

Links: Class File as ZIP.