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&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;v=2&amp;sensor=false&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.

Thanks for this! I was trying to figure out how to work the Google Maps API into a Zend project and was not having much luck. The custom icon is cool too!
Thanks again!
-Jim
Another way to do this is to store your key in the config then just load the key out of the config via your bootstrap and set it in the registry.
Then create a Service class in your custom library. and use Zend_Http
$this->_httpClient->setUri("http://maps.google.com/maps/geo");
$this->_httpClient->setHeaders('Accept-Charset', 'ISO-8859-1,utf-8');
$this->_httpClient->setParameterGet(array(
'key' => $this->_key,
'output' => $this->_output,
'q' => $address,
));
$data = $this->_httpClient->request('GET');
return $data->getBody();
An adapter seems like a lot of work to get a lat long.
Hi Shaun, thanks for the input. You are correct, an adapter would be overkill. I am not quite sure why I put “Adapter” in there, as it is really meant to be used as a model, the references have been fixed to be Geocoder.
If I’m not mistaken, the only change you’ve proposed is moving the code to a “Service” class, as I am already making use of Zend_Http in pretty much the exact same way. By having this logic in a model, I am able to have utility methods and expose more methods without fudging the purpose of the class. In reality, it makes the most sense to incorporate both approaches, and retrieve the data for my Geocoder model from a Services class.
It’s not about the amount of work I need to do get lat and lon, I can reuse this class as many times as I want. It is more about providing a clean and intuitive interface to gathering geodata.
Cheers