Geocoding with Yahoo PlaceFinder and Google Geocoding in PHP and jQuery

August 24th, 2011 by Alex Leave a reply »

Sometimes (for someone, very often) you need to get coordinates of your local address. Hard? Not really. With the help of the “giants” we can easily get the latitude and longitude in a couple milliseconds.

Here’s how.

Google Geocoding API

First, let’s take a look at Google Geocoding. Free, 2.500 requests per day, premium users get up to 100.000 requests per day. Both XML and JSON outputs are provided. Let’s take a look at a simple example:

http://maps.googleapis.com/maps/api/geocode/json?address=Manhattan,%20New%20York%20City&sensor=false&language=en

What we’ll need here most of the time is address, sensor and language. Language is set only if we want to output the formatted address which was returned or check if it’s == with your request, so if we host our server in Holland we won’t receive . Sensor is a variable which is determines if we use a GPS-enabled device. According to Google,

Use of the Google Maps API(s) requires that you indicate whether your application is using a sensor (such as a GPS locator) to determine the user’s location in any Maps API library or service requests. This is especially important for mobile devices. If your Google Maps API application uses any form of sensor to determine the location of the device accessing your application, you must declare this with a sensor parameter value of true.
Note that even if your application does not use a location sensor, you still must set the sensor parameter (in this case to false).

So you decide in what case to use it.

Let’s take a look at a sample output

{
   "results" : [
      {
         "address_components" : [
            {
               "long_name" : "Manhattan",
               "short_name" : "Manhattan",
               "types" : [ "sublocality", "political" ]
            },
            {
               "long_name" : "New York",
               "short_name" : "New York",
               "types" : [ "locality", "political" ]
            },
            {
               "long_name" : "New York",
               "short_name" : "New York",
               "types" : [ "administrative_area_level_2", "political" ]
            },
            {
               "long_name" : "New York",
               "short_name" : "NY",
               "types" : [ "administrative_area_level_1", "political" ]
            },
            {
               "long_name" : "United States",
               "short_name" : "US",
               "types" : [ "country", "political" ]
            }
         ],
         "formatted_address" : "Manhattan, New York, NY, USA",
         "geometry" : {
            "bounds" : {
               "northeast" : {
                  "lat" : 40.8200450,
                  "lng" : -73.90331300000001
               },
               "southwest" : {
                  "lat" : 40.6980780,
                  "lng" : -74.03514899999999
               }
            },
            "location" : {
               "lat" : 40.78343450,
               "lng" : -73.96624950
            },
            "location_type" : "APPROXIMATE",
            "viewport" : {
               "northeast" : {
                  "lat" : 40.8200450,
                  "lng" : -73.90331300000001
               },
               "southwest" : {
                  "lat" : 40.6980780,
                  "lng" : -74.03514899999999
               }
            }
         },
         "types" : [ "sublocality", "political" ]
      },
      {
         "address_components" : [
            {
               "long_name" : "New York",
               "short_name" : "New York",
               "types" : [ "administrative_area_level_2", "political" ]
            },
            {
               "long_name" : "New York",
               "short_name" : "NY",
               "types" : [ "administrative_area_level_1", "political" ]
            },
            {
               "long_name" : "United States",
               "short_name" : "US",
               "types" : [ "country", "political" ]
            }
         ],
         "formatted_address" : "Manhattan, New York, USA",
         "geometry" : {
            "bounds" : {
               "northeast" : {
                  "lat" : 40.8822140,
                  "lng" : -73.9070
               },
               "southwest" : {
                  "lat" : 40.67954790,
                  "lng" : -74.0472850
               }
            },
            "location" : {
               "lat" : 40.78306030,
               "lng" : -73.97124880
            },
            "location_type" : "APPROXIMATE",
            "viewport" : {
               "northeast" : {
                  "lat" : 40.8822140,
                  "lng" : -73.9070
               },
               "southwest" : {
                  "lat" : 40.67954790,
                  "lng" : -74.0472850
               }
            }
         },
         "partial_match" : true,
         "types" : [ "administrative_area_level_2", "political" ]
      }
   ],
   "status" : "OK"
}

As you see, plenty of data and even several results returned. We’ll need the first one.
So a path to our lat/lng is results[0]->geometry->location. Now let’s finally build a function or what?..

Starting simple, we can load urls with file_get_contents or cURL. I’ll show both ways in case any of the method is blocked on your server.

function geocode($address,$city,$state,$country) {
	$address = array($address, $city, $state, $country);
	$address = array_filter($address);
	$address = urlencode(implode(', ', $address));
 
	$url = "http://maps.googleapis.com/maps/api/geocode/json?address=".$address."&sensor=false";
 
	/*
	$data = file_get_contents($url);
	*/
 
	$ch = curl_init();
	curl_setopt($ch, CURLOPT_URL, $url);
	curl_setopt($ch, CURLOPT_HEADER,0); 
	curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
	$data = curl_exec($ch);
	curl_close($ch);
 
	if ($data != '') {
		try {
			$data = json_decode($data);
			if ($data && count($data->results) && $data->results[0]->geometry && $data->results[0]->geometry->location)
				return $data->results[0]->geometry->location;
		} catch(Exception $e) {
			return false;
		}
	}
	return false;
}

First, we remove unknown address parts and leave only those we know, then implode with comma and urlencode. Then we are ready to go and get the JSON.

Whether you are using file_get_contents or cURL, you should have the same result. But, with cURL you can check if there were request errors like 404 or 550 permission denied. Then, we just check if $data is json_decoded, if it’s not null and has results, and if the result has a geometry entry in it, and if that geometry has a location. Optionally, you can just wipe all checks and do just

$data = json_decode($data);
return $data->results[0]->geometry->location;

Now let’s see what shall we do if Google didn’t help us.

Yahoo PlaceFinder API

So, let’s take a look at the differences. Yahoo PlaceFinder is also a free service with 50.000 requests per day limit. It requires you to register your application and get a free API key. The REST url for the API is:

http://where.yahooapis.com/geocode?

The basic parameter we will need to pass is location or its alias q. It can be very short or long, depending on your needs, but it will almost always return some results.

The other very nice parameter is flags. It determines different output options. We will set a J flag to output data as JSON. Let’s take a look at an example output, we will try to search for Hreschatyk, the main street in Kyiv, Ukraine.

http://where.yahooapis.com/geocode?location=Hreschatyk,%20Kyiv,%20Ukraine&flags=J
{
    "ResultSet": {
        "version": "1.0",
        "Error": 0,
        "ErrorMessage": "No error",
        "Locale": "us_US",
        "Quality": 87,
        "Found": 2,
        "Results": [{
            "quality": 70,
            "latitude": "50.448550",
            "longitude": "30.522483",
            "offsetlat": "50.448550",
            "offsetlon": "30.522483",
            "radius": 500,
            "name": "",
            "line1": "Kreshchatik Ulitsa",
            "line2": "Pechers'Kyy Rayon",
            "line3": "",
            "line4": "Ukraine",
            "house": "",
            "street": "Kreshchatik Ulitsa",
            "xstreet": "",
            "unittype": "",
            "unit": "",
            "postal": null,
            "neighborhood": "Pechers'Kyy Rayon",
            "city": "Kiev",
            "county": "Kiev",
            "state": "",
            "country": "Ukraine",
            "countrycode": "UA",
            "statecode": "",
            "countycode": "",
            "uzip": null,
            "hash": "",
            "woeid": 924938,
            "woetype": 7
        }, {
            "quality": 70,
            "latitude": "50.452060",
            "longitude": "30.526855",
            "offsetlat": "50.452060",
            "offsetlon": "30.526855",
            "radius": 500,
            "name": "",
            "line1": "Kreshchatik Ulitsa",
            "line2": "Shevchenkivs'Kyy Rayon",
            "line3": "",
            "line4": "Ukraine",
            "house": "",
            "street": "Kreshchatik Ulitsa",
            "xstreet": "",
            "unittype": "",
            "unit": "",
            "postal": null,
            "neighborhood": "Shevchenkivs'Kyy Rayon",
            "city": "Kiev",
            "county": "Kiev",
            "state": "",
            "country": "Ukraine",
            "countrycode": "UA",
            "statecode": "",
            "countycode": "",
            "uzip": null,
            "hash": "",
            "woeid": 924938,
            "woetype": 7
        }]
    }
}

Pretty nice, right? No geometry or location objects, just simply an array of Results which has all the values we need, and it broke down the location we passed to house, street, city, county, state and country. That is very nice. What we will need now is to simply add a new function to what we have:

function geocode_yahoo($address,$city,$state,$country) {
	$address = array($address, $city, $state, $country);
	$address = array_filter($address);
	$address = urlencode(implode(', ', $address));
 
	$appid = 'appid';
 
	$url = 'http://where.yahooapis.com/geocode?location='.$address.'&flags=J&appid='.$appid;
	$data = file_get_contents($url);
	if ($data != '') {
		$data = json_decode($data);
		if ($data && $data->ResultSet && $data->ResultSet->Error == '0' && $data->ResultSet->Found) {
			return (object) array('lat'=>$data->ResultSet->Results[0]->latitude, 'lng'=>$data->ResultSet->Results[0]->longitude); 
		}
	}
	return false;
}

Now, when we have both, we can simply do this:

$data = geocode('', 'Philadelphia', '', 'USA');
if (!$data)
  $data = geocode_yahoo('', 'Philadelphia', '', 'USA');

Getting same in jQuery

jQuery isn’t hard as well. All you need is to prepare the url we need and get the results by simply selecting it from an object. For this, we will use YQL. It’s simple, it’s free, it’s fast, it does the things we don’t want to do 🙂

First, prepare the address (strip empty address parts, urlencode etc.):

var address = new Array(address, city, state, country);
var newaddress = new Array();
for (var i in address) {
	if (address[i] != '')
		newaddress.push(address[i]);
}
address = newaddress;
address = escape(address.join(', '));

Then, prepare the URLs for both Google and Yahoo (yes, we can combine both services in one request!):

var url = "http://maps.googleapis.com/maps/api/geocode/json?address="+address+"&sensor=false";
var url2 = "http://where.yahooapis.com/geocode?location="+address+"&flags=J&appid="+yahoo_appid;

Construct a YQL request and format all the stuff in a single JSON URL:

var yql = 'SELECT * FROM json WHERE url = "'+url+'" OR url = "'+url2+'"';
var yqlurl = "http://query.yahooapis.com/v1/public/yql?q="+escape(yql)+"&format=json&diagnostics=true&callback=?";

That’s it, we can now load the yqlurl and get the data. I won’t describe the resulting tree, just try loading the url with getJSON and seeing what’s what:

$.getJSON(yqlurl, function(d) {
	data = null;
	if (d && d.query && d.query.results) {
		if (false && d.query.results.json && d.query.results.json.results.length)
			data = d.query.results.json.results[0].geometry.location;
		else if (d.query.results.ResultSet && d.query.results.ResultSet.Found)
			if (d.query.results.ResultSet.Found == 1) {
				data = {lat:d.query.results.ResultSet.Results.latitude, lng:d.query.results.ResultSet.Results.longitude};
			} else {
				data = {lat:d.query.results.ResultSet.Results[0].latitude, lng:d.query.results.ResultSet.Results[0].longitude};
			}
	}
 
	$('#geocode').append('lat => '+data.lat+', lng => '+data.lng);
});

I used a simple <div id=’geocode’> tag to just see it in action, all in all i came up to this one:

function geocode(address,city,state,country) {
	var address = new Array(address, city, state, country);
	var newaddress = new Array();
	for (var i in address) {
		if (address[i] != '')
			newaddress.push(address[i]);
	}
	address = newaddress;
	address = escape(address.join(', '));
 
	var yahoo_appid = 'appid';
 
	var url = "http://maps.googleapis.com/maps/api/geocode/json?address="+address+"&sensor=false";
	var url2 = "http://where.yahooapis.com/geocode?location="+address+"&flags=J&appid="+yahoo_appid;
	var yql = 'SELECT * FROM json WHERE url = "'+url+'" OR url = "'+url2+'"';
	var yqlurl = "http://query.yahooapis.com/v1/public/yql?q="+escape(yql)+"&format=json&diagnostics=true&callback=?";
 
	$.getJSON(yqlurl, function(d) {
		data = null;
		if (d && d.query && d.query.results) {
			if (d.query.results.json && d.query.results.json.results.length)
				data = d.query.results.json.results[0].geometry.location;
			else if (d.query.results.ResultSet && d.query.results.ResultSet.Found)
				if (d.query.results.ResultSet.Found == 1) {
					data = {lat:d.query.results.ResultSet.Results.latitude, lng:d.query.results.ResultSet.Results.longitude};
				} else {
					data = {lat:d.query.results.ResultSet.Results[0].latitude, lng:d.query.results.ResultSet.Results[0].longitude};
				}
		}
 
		$('#geocode').append('lat => '+data.lat+', lng => '+data.lng);
	});
}
 
geocode('', 'Kiev', '', 'Ukraine');

As you may see, YQL gives you a possibility to forget about combining requests, synchronizing and all that stuff. Even more, it combines your requests, calculates time spent, total time etc. Check it out yourself:

{
    "query": {
        "count": 2,
        "created": "2011-08-24T10:56:20Z",
        "lang": "en-US",
        "diagnostics": {
            "publiclyCallable": "true",
            "url": [{
                "execution-start-time": "1",
                "execution-stop-time": "104",
                "execution-time": "103",
                "proxy": "DEFAULT",
                "content": "http://where.yahooapis.com/geocode?location=Kyiv,%20Ukraine&flags=J"
            }, {
                "execution-start-time": "1",
                "execution-stop-time": "126",
                "execution-time": "125",
                "proxy": "DEFAULT",
                "content": "http://maps.googleapis.com/maps/api/geocode/json?address=Kyiv,%20Ukraine&sensor=false"
            }],
            "user-time": "127",
            "service-time": "228",
            "build-version": "20626"
        },
...

As you may see, it has it all to see if a website is taking too long to respond or if you want to filter out only the fastest ones (example can be if you have 50-100 RSS feed urls and don’t want slow ones to show up). Happy experimenting and happy Ukrainian Independence Day! 🙂

This page can be found by searching for:

yahoo placefinder php json_decodeget geocode one address with php from googleyahoo placefinder php



Comments are closed.