The Technical Implementation of Geo Location Software

0 min read

Leah Wagner

This blog post outlines the technical implementation of the techniques shared in a previous blog post: eCommerce Solutions: Personalized Content Delivery Based On Geographical Location.

When this case first presented itself, our first thought was to incorporate and implement Drupal’s Internationalization module, which would present different content based on geographical location. The functionality would be similar to a multilingual site except that all of the content would be displayed in English. As we dove into the project requirements, we quickly recognized that this would only make things more complicated -- not just during development, but also for maintenance.

Our client made it clear that they weren’t looking to restrict content for each geographical region. In fact, they were looking for a system that would customize their content based on each individual visitor's location. The client also wanted location specific content to be featured on the homepage, also to include view listings that would show the most relevant information to a visitor based on their designated location.

So, where did we turn?

The Smart IP module provided us with the capability to obtain a visitors IP address upon first visiting the site, or allow a user to override this setting and specify which geographical location they wanted to browse from. The module was also very flexible to work with when obtaining IP addresses through location service databases. In our case, our client was specifically looking to obtain a visitor's country location. At the time, we chose to work with IPInfoDB, which provided this information for free. This service also integrates with many other services, which can narrow searches down to province, city and even postal code -- but, it usually comes with a fee.

The Smart IP module gave us two functions to work with. The first function, smart_ip_session_get(), retrieved the the users location and stored it in a session variable. The second, smart_ip_session_set(), changed this setting, which was automatically detected on the users first visit. This function was required for both of our recent projects. With these functions, we would show users which content they were accessing and give them a dropdown menu. This feature would also provide them with an option to override the default location and see content targeted at a different location.

A step-by-step guide to implementing Smart IP

1. Getting Setup

First, create an account at IPInfroDB. Then enable the SmartIP module and enter the key provided.

This module has the ability to manually look up an IP address. This is a really good way to test if things are working properly.

Next, we setup a field that can be added to all of the content types.

In our case, this gave our client the ability to classify and target which region(s) should be seen by default. So, we created a multi-select text list field.

2. Getting Custom

In our clients case, they wanted to target continents. At this stage of the project, customizing content based on country was too granular.

This is where the custom bit comes in.

The Smart IP module provides you with the following information which, is then stored within a visitor’s session:

Country: Canada Country code: CA Region: British Columbia City: Vancouver Postal code: V5K 0A1 Latitude: 49.2497 Longitude: -123.119 Time zone: -08:00

In this example, IPInfoDB provides us with some really specific information. Keep in mind: this is not always the case with the free service.

There is no mention of continent in this data. However, by travelling back to elementary school geography class, we are able to quickly solve this problem!

With our client’s case, we were able to create a function, which mapped a country code to one of the continent codes defined in our fields (NA, SA, EU, etc.). The goal was to have this function return the options, which was defined in the multi-select text list field with the content types.

function thejibe_smartip_bootstrap_location_mapping($country_code) { switch ($country_code) { /* NORTH AMERICA */ case 'AI': // Anguilla case 'AG': // Antigua and Barbuda case 'AW': // Aruba case 'BS': // Bahamas case 'BB': // Barbados case 'BZ': // Belize case 'BM': // Bermuda case 'VG': // British Virgin Islands case 'CA': // Canada case 'KY': // Cayman Islands case 'CR': // Costa Rica case 'CU': // Cuba case 'CW': // Curacao case 'DM': // Dominica case 'DO': // Dominican Republic case 'SV': // El Salvador case 'GL': // Greenland case 'GD': // Grenada and Carriacuou case 'GP': // Guadeloupe case 'GT': // Guatemala case 'HN': // Honduras case 'HT': // Haiti case 'JM': // Jamaica case 'MQ': // Martinique case 'MX': // Mexico case 'PM': // Miquelon case 'MS': // Montserrat case 'CW': // Netherlands Antilles case 'KN': // Nevis case 'NI': // Nicaragua case 'PA': // Panama case 'PR': // Puerto Rico case 'SX': // Sint Maarten case 'KN': // St. Kitts case 'LC': // St. Lucia case 'PM': // St. Pierre and Miquelon case 'VC': // St. Vincent case 'TT': // Trinidad and Tobago case 'TC': // Turks and Caicos Islands case 'VI': // US Virgin Islands case 'US': // United States $location = 'NA'; break; /* SOUTH AMERICA */ case 'BQ': // Bonaire case 'SA': // Saba case 'SE': // Sint Eustatius case 'AR': // Argentina case 'BO': // Bolivia case 'BR': // Brazil case 'CL': // Chile case 'CO': // Colombia case 'EC': // Ecuador case 'FK': // Falkland Islands case 'GF': // French Guiana case 'GY': // Guyana case 'PY': // Paraguay case 'PE': // Peru case 'SR': // Suriname case 'UY': // Uruguay case 'VE': // Venezuela $location = 'SA'; break; /* ASIA */ case 'AM': // Armenia case 'AZ': // Azerbaijan case 'BD': // Bangladesh case 'BT': // Bhutan case 'BN': // Brunei case 'KH': // Cambodia case 'CN': // China case 'CX': // Christmas Island case 'CC': // Cocos Islands case 'IO': // Diego Garcia case 'GE': // Georgia case 'HK': // Hong Kong case 'IN': // India case 'ID': // Indonesia case 'IL': // Israel case 'JP': // Japan case 'KZ': // Kazakhstan case 'KP': // North Korea case 'KR': // South Korea case 'KG': // Kyrgyzstan case 'LA': // Laos case 'MO': // Macau case 'MY': // Malaysia case 'MV': // Maldives case 'MN': // Mongolia case 'MM': // Myanmar case 'NP': // Nepal case 'PH': // Philippines case 'SA': // Saudi Arabia case 'SG': // Singapore case 'LK': // Sri Lanka case 'SY': // Syria case 'TW': // Taiwan case 'TJ': // Tajikistan case 'TH': // Thailand case 'TR': // Turkey case 'TM': // Turkmenistan case 'UZ': // Uzbekistan case 'VN': // Vietnam case 'AF': // Afghanistan case 'BH': // Bahrain case 'IR': // Iran case 'IQ': // Iraq case 'JO': // Jordan case 'KW': // Kuwait case 'LB': // Lebanon case 'OM': // Oman case 'PK': // Pakistan case 'PS': // Palestine case 'QA': // Qatar case 'AE': // United Arab Emirates case 'YE': // Yemen $location = 'AS'; break; /* AUSTRAILIA & OCEANA */ case 'AS': // American Samoa case 'AU': // Australia case 'NZ': // Chatham Island, NZ case 'CK': // Cook Islands case 'FJ': // Fiji Islands case 'PF': // French Polynesia case 'GU': // Guam case 'KI': // Kiribati case 'MP': // Mariana Islands case 'MH': // Marshall Islands case 'FM': // Federated States of Micronesia case 'UM': // Midway Islands case 'NR': // Nauru case 'NC': // New Caledonia case 'NZ': // New Zealand case 'NU': // Niue case 'NF': // Norfolk Island case 'PW': // Palau case 'PG': // Papua New Guinea case 'MP': // Saipan case 'SB': // Solomon Islands case 'TK': // Tokelau case 'TO': // Tonga case 'TV': // Tuvalu case 'VU': // Vanuatu case 'UM': // Wake Island case 'WF': // Wallis and Futuna Islands case 'WS': // Samoa case 'TL': // East Timor $location = 'AU'; break; /* ANTARCTICA */ case 'AQ': // Antarctica $location = 'AQ'; break; /* EUROPE */ case 'AL': // Albania case 'AD': // Andorra case 'AM': // Armenia case 'AT': // Austria case 'BY': // Belarus case 'BE': // Belgium case 'BA': // Bosnia and Herzegovina case 'BG': // Bulgaria case 'CH': // Switzerland case 'CY': // Cyprus case 'CZ': // Czech Republic case 'DE': // Germany case 'DK': // Denmark case 'EE': // Estonia case 'ES': // Spain case 'FO': // Faeroe Islands case 'FI': // Finland case 'FR': // France case 'GB': // United Kingdom case 'GE': // Georgia case 'GI': // Gibraltar case 'GR': // Greece case 'HU': // Hungary case 'HR': // Croatia case 'IE': // Ireland case 'IS': // Iceland case 'IT': // Italy case 'LT': // Lithuania case 'LU': // Luxembourg case 'LV': // Latvia case 'MC': // Monaco case 'MK': // Macedonia case 'MT': // Malta case 'NO': // Norway case 'NL': // Netherlands case 'PO': // Poland case 'PT': // Portugal case 'RO': // Romania case 'RU': // Russian Federation case 'SE': // Sweden case 'SI': // Slovenia case 'SK': // Slovakia case 'SM': // San Marino case 'TR': // Turkey case 'UA': // Ukraine case 'VA': // Vatican City State $location = 'EU'; break; /* AFRICA */ case 'DZ': // Algeria case 'AO': // Angola case 'SH': // Ascension case 'BJ': // Benin case 'BW': // Botswana case 'BF': // Burkina Faso case 'BI': // Burundi case 'CM': // Cameroon case 'CV': // Cape Verde Islands case 'CF': // Central African Republic case 'TD': // Chad Republic case 'KM': // Comoros case 'CG': // Congo case 'DJ': // Djibouti case 'EG': // Egypt case 'GQ': // Equatorial Guinea case 'ER': // Eritrea case 'ET': // Ethiopia case 'GA': // Gabon Republic case 'GM': // Gambia case 'GH': // Ghana case 'GW': // Guinea-Bissau case 'GN': // Guinea case 'CI': // Ivory Coast case 'KE': // Kenya case 'LS': // Lesotho case 'LR': // Liberia case 'LY': // Libya case 'MG': // Madagascar case 'MW': // Malawi case 'ML': // Mali Republic case 'MR': // Mauritania case 'MU': // Mauritius case 'YT': // Mayotte Island case 'MA': // Morocco case 'MZ': // Mozambique case 'NA': // Namibia case 'NE': // Niger Republic case 'NG': // Nigeria case 'ST': // Principe case 'RE': // Reunion Island case 'RW': // Rwanda case 'ST': // Sao Tome case 'SN': // Senegal Republic case 'SC': // Seychelles case 'SL': // Sierra Leone case 'SO': // Somalia Republic case 'ZA': // South Africa case 'SH': // St. Helena case 'SD': // Sudan case 'SZ': // Swaziland case 'TZ': // Tanzania case 'TG': // Togo case 'TN': // Tunisia case 'UG': // Uganda case 'CD': // Zaire case 'ZM': // Zambia case 'TZ': // Zanzibar case 'ZW': // Zimbabwe case 'SS': // South Sudan case 'CD': // Dem. Republic of the Congo $location = 'AF'; break; default: $location = 'ALL'; } return $location; }

Next, we wrote a separate function that would grab the data from the Smart IP module, which mapped the country code based in the breakdown of the first function. This function used the smart_ip_session_get() function that was made available by the Smart IP module.

function thejibe_smartip_bootstrap_smartip_location() { $smart_ip_session = smart_ip_session_get('smart_ip'); return thejibe_smartip_bootstrap_location_mapping($smart_ip_session['location']['country_code']); }

3. Integrating with views

The key to making a view respond to the users location, is to compare the location information saved to the node with the location information which, is stored in the visitor's Smart IP session.

This is done with a contextual filter. The first step is to add the field we created (field_smartip_location) as a contextual filter.

Next we provide a default PHP value. In this PHP field, we call the function that will retrieve the user's current location and then return the appropriate continent.

Finally, we set an expectation of ALL which, is also defined in our field as the option for Worldwide. If a user’s location cannot be detected, this filter will be ignored.

4. Allowing control

Now that we have our content being customized based on where the user is coming from, let’s give them the option to change this.

With our client, we used the hook_menu function to create a URL that would accept an argument. In our case, the argument was the continent code.

function thejibe_smartip_bootstrap_menu() { $items = array(); $items['location/switch/%'] = array( 'page callback' => 'thejibe_smartip_bootstrap_location_switch', 'page arguments' => array(2), 'access arguments' => array('access content'), 'type' => MENU_CALLBACK, ); return $items; }

This URL argument then calls another function which will ultimately switch the user’s session accordingly.

This is the smart_ip_session_set() function, which is provided by the Smart IP module.

function thejibe_smartip_bootstrap_location_switch($location) { // Get the current session $smart_ip_session = smart_ip_session_get('smart_ip'); switch ($location) { case 'ALL': $smart_ip_session['location']['country_code'] = ''; break; case 'NA': $smart_ip_session['location']['country_code'] = 'US'; break; case 'SA': $smart_ip_session['location']['country_code'] = 'BR'; break; case 'EU': $smart_ip_session['location']['country_code'] = 'GB'; break; case 'AS': $smart_ip_session['location']['country_code'] = 'CN'; break; case 'AF': $smart_ip_session['location']['country_code'] = 'EG'; break; case 'AU': $smart_ip_session['location']['country_code'] = 'AU'; break; case 'AQ': $smart_ip_session['location']['country_code'] = 'AQ'; break; } smart_ip_session_set('smart_ip', $smart_ip_session); drupal_goto(); return ''; }

Lastly, we create a menu that the user can interact with, then place it somewhere on the page.

5. Next steps

Now that you understand the methodology, you don’t have to personally start from scratch. We have a bootstrap module, TheJibe: SmartIP Bootstrap that can help get you started!

Our bootstrap module includes:

The base field (field_smartip_location) which, can be added to your content types. An example view that shows how to implement the filtering with a sample menu in the header region. The code (thejibe_smartip_bootstrap.module) that will map a country to its appropriate continent. Along with the second step which, sets the continent using Smart IP’s smart_ip_session_get() function. The code that creates the menu path, which accepts arguments. Along with the second step, that changes the location using Smart IP’s smart_ip_session_set() function.

We would love to see how you use it, modify it, and improve it. Please don’t hesitate to reach out to us with any questions!

For more on eCommerce solutions, contact us or see: Personalized Content Based On Geographical Location.