Geocoding Customer Addresses in Magento via Google Maps

Geocoding is the process of finding associated geographic coordinates (often expressed as latitude and longitude) from other geographic data, such as street addresses, or zip codes (postal codes)… so says Wikipedia. Geocoding an address is pretty simple if you are using Google Maps API. Since all we do here is Magento, let me show you how easily you can geocode customer address.

We will start with adding the Gmap.php helper to our extension:

<?php
/**
* @author Branko Ajzele <ajzele@gmail.com>
*/
class Inchoo_Extension_Helper_Gmap extends Mage_Core_Helper_Abstract
{
const GOOGLE_MAPS_HOST = 'maps.google.com';
const CONFIG_PATH_GOOGLE_MAPS_API_KEY = 'inchoo_google/maps/api_key';
 
public function getGoogleMapsApiKey()
{
return Mage::getStoreConfig(self::CONFIG_PATH_GOOGLE_MAPS_API_KEY);
}
 
/**
*
* @param Mage_Customer_Model_Address $address
* @param boolean $saveCoordinatesToAddress
* @param boolean $forceLookup
* @return array Returns the array of float values like {[0] => float(18.4438156) [1] => float(45.5013644)}
*/
public function fetchAddressGeoCoordinates(Mage_Customer_Model_Address $address, $saveCoordinatesToAddress = true, $forceLookup = false)
{
/**
* USAGE EXAMPLE
 
$customer = Mage::getModel('customer/customer');
$customer->setWebsiteId(Mage::app()->getWebsite()->getId());
$customer->loadByEmail('some-email@domain.com');
 
Mage::helper('inchoo/gmap')->fetchAddressGeoCoordinates($customer->getDefaultBillingAddress())
 
*/
 
$coordinates = array();
 
if ($address->getId() && $address->getGmapCoordinatePointX() && $address->getGmapCoordinatePointY() && $forceLookup == false) {
$coordinates = array($address->getGmapCoordinatePointX(), $address->getGmapCoordinatePointY());
} else if (!$address->getGmapCoordinatePointX() && !$address->getGmapCoordinatePointY())
OR ($address->getId() && $address->getGmapCoordinatePointX() && $address->getGmapCoordinatePointY() && $forceLookup == true)) {
 
$lineAddress = $address->getStreet1(). ', '.$address->getPostcode().' '.$address->getCity().', '.$address->getCountry();
 
$client = new Zend_Http_Client();
$client->setUri('http://'.self::GOOGLE_MAPS_HOST.'/maps/geo');
$client->setMethod(Zend_Http_Client::GET);
$client->setParameterGet('output', 'json');
$client->setParameterGet('key', $this->getGoogleMapsApiKey());
$client->setParameterGet('q', $lineAddress);
 
$response = $client->request();
 
if ($response->isSuccessful() && $response->getStatus() == 200) {
$_response = json_decode($response->getBody());
$_coordinates = @$_response->Placemark[0]->Point->coordinates;
 
if (is_array($_coordinates) && count($_coordinates) >= 2) {
 
$coordinates = array_slice($_coordinates, 0, 2);
 
if ($saveCoordinatesToAddress) {
try {
$address->setGmapLng($coordinates[0]);
$address->setGmapLat($coordinates[1]);
 
$address->save();
} catch (Exception $e) {
Mage::logException($e);
}
}
}
}
}
 
return $coordinates;
}
}

As you can see above in the code, function getGoogleMapsApiKey() pulls the API key info from my system configuration. I will not show you that part here. So for the sake of simplicity if you do not know how to code a configuration for your module you can just return a raw API key string here (surely this approach is then just for testing/playing not on production).

Since this helper references the two non-defualt-existing attributes on the address we will need to add those attributes. We do so trough install/upgrade scripts (you know, the files under app/code/{codePool}/Company/Extension/sql/extension_setup/ folder). I will not give you the full example of the file and it’s corresponding config.xml entry, as you should know how to do it yourself already, rather just the partial code that goes into the install/upgrade script in order to add the two missing customer address attributes:

//Add attribute that will be used for Google Maps as a coordinate lng
$installer->addAttribute('customer_address', 'gmap_lng', array(
'type' => 'varchar',
'input' => 'hidden',
'visible' => false,
'required' => false
));
//Add attribute that will be used for Google Maps as a coordinate lat
$installer->addAttribute('customer_address', 'gmap_lat', array(
'type' => 'varchar',
'input' => 'hidden',
'visible' => false,
'required' => false
));

Next, we will add the observer to the proper _beforeSave() dispatched event of the Mage_Customer_Model_Address model class. Since Mage_Customer_Model_Address extends the Mage_Customer_Model_Address_Abstract which defines the “protected $_eventPrefix = ‘customer_address’;” we can easily conclude the all it takes is to create observer for “customer_address_save_before” event (Mage_Core_Model_Abstract::_beforeSave() … we have Mage::dispatchEvent($this->_eventPrefix.’_save_before’, $this->_getEventData());).

So we add something like this to the config.xml file of our extension:

<global>
 
<events>
<customer_address_save_before>
<observers>
<inchoo_observer_customer_address_save_before>
<type>singleton</type>
<class>Inchoo_Extension_Model_Observer</class>
<method>geocodeAddress</method>
</inchoo_observer_customer_address_save_before>
</observers>
</customer_address_save_before>
</events>
 
</global>

Then we create the observer Inchoo_Extension_Model_Observer with something like:

Within that observer we simply need to call something like:

<?php
class Inchoo_Extension_Model_Observer
{
public function geocodeAddress()
{
Mage::helper('inchoo/gmap')
->fetchAddressGeoCoordinates($observer->getEvent()->getCustomerAddress());
 
return $this;
}
}

Basically that’s it. Now each time an address is saved in Magento, there should be a lookup on Google Maps API in order to fetch the longitude and latitude coordinates. Please note, that all of this is just to fetch the coordinates and save them under the address. Showing the actual map somewhere on the frontend or backend requires a bit more input.

To show the Google Map on the frontend with the proper location marker shown on the map you need two things. First we need to add the script like shown below to the head part of the page:

<script type="text/javascript" src="http://maps.googleapis.com/maps/api/js?key=<?php echo Mage::helper('inchoo/gmap')->getGoogleMapsApiKey() ?>&sensor=false"></script>

Then finally we need to add something like:

<div id="address<?php echo $_address->getId() ?>-gmap" style="width:480px; height:480px;"></div>
 
<script type="text/javascript">
//<![CDATA[
 
Event.observe(window, 'load', function() {
 
var myLatlng = new google.maps.LatLng(<?php echo $_address->getGmapLat() ?>,<?php echo $_address->getGmapLng() ?>);
var myOptions = {
zoom: 4,
center: myLatlng,
mapTypeId: google.maps.MapTypeId.ROADMAP
}
var map = new google.maps.Map(document.getElementById("address<?php echo $_address->getId() ?>-gmap"), myOptions);
 
var marker = new google.maps.Marker({
position: myLatlng,
map: map,
title:"Address Location"
});
 
});
//]]>
</script>

And that’s it. Please note, the code above might not be fully copy paste, check the JavaScript when you implement it, and test if your customer address attributes are installed correctly. This article is not for Magento beginners so if you experience some difficulties implementing this approach, please consult the Magento Knowledge Base first.

P.S. I was playing with this on Magento CE 1.6.1.0.

Hope this was helpful. Cheers.