Geocoding with Google Maps and the Zend Framework

Maps are a great way to engage your users and visualize data, but it can be a little tricky to setup. Here I will walk you through the steps needed to get a map up and running.

Sign Up

Apply for a Google Maps API Key (free) here using your domain without a subdomain or www (this will allow you to use key with or without www and on subdomains): http://code.google.com/apis/maps/signup.html

Configuration

Put the API key in your configuration file (application.ini, config.ini, etc.) like this:

[google]
google_map_key =  YOUR_KEY

Bootstrapping

In your index.php file, add the following code:

//update CONFIG_PATH to point to your configuration file
defined(CONFIG_PATH)
or define('CONFIG_PATH', APPLICATION_PATH . '/configs/application.ini');
Zend_Registry::set('google',    new Zend_Config_Ini(CONFIG_PATH, 'google'));

This will add your api key to the registry so that it can be accessed throughout your application.

Model

In your models directory, create a new class called ‘Geocoder.php’. This will be the file that contains the application logic for retreiving coordinates on a given location.

models/Geocoder.php


/**
* Geocoder
*
* @author Robert McVey
*/
class Geocoder {
	protected $key = "";
	public function __construct($apiKey)
	{
		$this->key = $apiKey;
	}
	private function _getGeocodedLatitudeAndLongitude($address)
	{
		$client = new Zend_Http_Client();
		$client->setUri($this->getGeocodingUri());
		$client->setParameterGet('q', urlencode($address))
			->setParameterGet('output', 'json')
			->setParameterGet('sensor', 'false')
			->setParameterGet('key', $this->key);
		$result = $client->request('GET');
		$response = Zend_Json_Decoder::decode(
			$result->getBody(),
			Zend_Json::TYPE_OBJECT
		);
		return $response;
	}
	public function getCoordinates($address)
	{
		$response = $this->_getGeocodedLatitudeAndLongitude($address);
		$return = array();
		if(isset($result->Placemark[0]->Point->coordinates[1])){
			$return(
			'lat' => $result->Placemark[0]->Point->coordinates[1];
			'lon' => $result->Placemark[0]->Point->coordinates[0];
			);
		}else{
			$return = null;
		}
		return $return;
	}
	private function getGeocodingUri()
	{
		return 'http://maps.google.com/maps/geo';
	}
}
?>

Usage

Alright, that was pretty easy. Now we just have to implement this in our controller. Here is a sample model that uses the Geocoder class to return geotagged User objects.
models/User.php

class User extends Zend_Db_Table_Abstract{
	protected $_name = 'users';
	protected $key;
	protected $geocoder;

	public function init()
	{
		$this->key = Zend_Registry::get('google')->google_map_key;
		$this->geocoder = new Geocoder($this->key);

	}
	public function getUsersAndGeocode()
	{
		$result = $this->fetchAll();

		$users    = $result->_toArray();
		foreach($users as $user)
		{
			$address = "{$user['address']} {$user['city']} {$user['state']} {$user['zip']}";

			$latlon = $this->geocoder->getCoordinates($address);
			if($latlon)
			{
				$user['lat'] = $latlon['lat'];

				$user['lon'] = $latlon['lon'];
			}
		}
		return $users;
	}

}
?>

The controller part is a little too easy. We will setup a reference to our user table and create a map action. Within the map action we get all of users and geocode them. We pass that collection to our view. This file should be located here:
controllers/UserController.php

class UserController extends Zend_Controller_Action{
	protected $user_table;
	public function init()
	{
		//database connectivity stuff is up to you

		$this->user_table = new User();
	}
	public function mapAction()
	{

		$users = $this->user_table->getUsersAndGeocode();
		$this->view->users = $users;
	}
}
?>

And finally, the view. Here we will create our map div, load the Google Maps API (don’t forget to sub out your key in the script src). We are using a custom marker here (personIcon); you can add your own image here by changing the path to your image. I recommend the image be pretty small with a transparent background. Given our current setup, this file would be located at this path:
views/scripts/user/map.phtml

<div id="map">

<noscript>
<center>
<strong>To view the map feature, you need to have Javascript enabled. If you are unsure about how to turn Javascript support on, please</p>
<p>read Google's instructions found here: <a href="https://www.google.com/support/adsense/bin/answer.py?hl=en&amp;answer=12654" target="_blank">LINK</a></strong>

</center></p>
<p></noscript>
</div>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>

<!--
<script type="text/javascript" src="http://maps.google.com/maps?file=api&amp;amp;v=2&amp;amp;sensor=false&amp;amp;key=your_key"></script>
-->

<script type="text/javascript">
 //Sorry, I love jquery! If you don't use window.onload = function(){initialize();}
    $(document).ready(function(){
        initialize();

    });
var map;
var personIcon;
function initialize() {
    if(GBrowserIsCompatible){

        map = new google.maps.Map2(document.getElementById("thurtene_map"));
        map.setUIToDefault();
        map.setCenter(new google.maps.LatLng('48.858001709', '2.29460000992'), 4);
        <? endif; ?>

        personIcon            = new GIcon();
  /**
  * Change this marker to be a small transparent custom image
  *
  */

        personIcon.image      = '/img/common/custom_marker.png';
        personIcon.shadow     = "http://www.google.com/mapfiles/shadow50.png";
        personIcon.iconSize   = new GSize(32,32);
        personIcon.shadowSize = new GSize(37,34);

        personIcon.iconAnchor = new GPoint(9,34);
        personIcon.infoWindowAnchor = new GPoint(9,2);
    }
    addOverlays(map);
}

function personMarker(point, index, dto){
 //set our marker to be the custom icon we created
    markerOptions = {icon:personIcon};
    var marker = new GMarker(point, markerOptions);

/**
 * This is the content in the info bubble
 */
    GEvent.addListener(marker, 'click', function(){
       marker.openInfoWindowHtml(
            '<h3><a href="/profile/view/'+dto.user_id+'">'+dto.user_first+' '+ dto.user_last+'</a></h3>
'
            +dto.user_address+'

'
            +dto.user_city+'
'
            +dto.user_state+' '+dto.user_zip
        );

    });
    return marker;
}
/*
* Here is where we loop through our users and create new markers for each

*/
function addOverlays(map){
    <?php
  $geo = $this->users->_toArray();
        $size = sizeof($geo);

 ?>
    <? for($i = 0; $i < $size; $i++): ?>
                var latLon = new GLatLng(<?php echo $geo[$i]['lat'];?>, <?php echo $geo[$i]['lon'];?>);

                map.addOverlay(personMarker(latLon, <?=$i?>, <?=Zend_Json::encode($geo[$i])?>));
    <?php endfor; ?>
}
</script>

Post a comment if you have any questions.

About the Author

I am a software developer/IT professional helping businesses save money through informed purchase consulting; website development and marketing; and process automation.