Facebook-like time ago with PHP

November 11th, 2011 by Alex No comments »

Ever wanted to have your date display like Facebook has? “3 minutes ago” instead of “09:12:10″?

Well, here’s a very simple code with PHP:

function ago($time)
{
   $periods = array("second", "minute", "hour", "day", "week", "month", "year", "decade");
   $lengths = array("60","60","24","7","4.35","12","10");
 
   $now = time();
 
   $difference = $now - $time;
   if ($difference > 0)
      $tense = 'ago';
   else
      $tense = 'in future';
 
   for($j = 0; $difference >= $lengths[$j] && $j < count($lengths)-1; $j++) {
       $difference /= $lengths[$j];
   }
 
   $difference = round($difference);
 
   if($difference != 1) {
       $periods[$j].= "s";
   }
 
   return "$difference $periods[$j] $tense ";
}
 
echo "I was here ".ago(time()-3600);
// will output "I was here 1 hour ago"

Another way is to use a function from andypsv

function time_ago($tm,$rcs = 0) {
    $cur_tm = time(); $dif = $cur_tm-$tm;
    $pds = array('second','minute','hour','day','week','month','year','decade');
    $lngh = array(1,60,3600,86400,604800,2630880,31570560,315705600);
    for($v = sizeof($lngh)-1; ($v >= 0)&&(($no = $dif/$lngh[$v])<=1); $v--); if($v < 0) $v = 0; $_tm = $cur_tm-($dif%$lngh[$v]);
 
    $no = floor($no); if($no <> 1) $pds[$v] .='s'; $x=sprintf("%d %s ",$no,$pds[$v]);
    if(($rcs == 1)&&($v >= 1)&&(($cur_tm-$_tm) > 0)) $x .= time_ago($_tm);
    return $x;
}

Hope you found it handy, I did.

Geocoding with Yahoo PlaceFinder and Google Geocoding in PHP and jQuery

August 24th, 2011 by Alex No comments »

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! :)

Google Plus One Icon PSD

August 9th, 2011 by Alex 1 comment »

Want a free hand-crafted Google +1 icon? Here you go!

Included is a PSD source file with the font used for “G+” (so you can substitute it with “g+” and resize to your needs). Download using any link below:

TurboBit
Depositfiles
Letitbit

Auto-select input field with jQuery, without having a deselect on clicking

July 26th, 2011 by Alex No comments »

Recently, i had a bug which was kinda confusing.. The input field was easily selected using the standard input select() method, so i could do like this:

$('input').focus(function() { $(this).select(); }).click(function() { $(this).select(); });

Easy as pie, it was auto-selected on mouse click AND when tab-clicking between fields. But, if it was already selected after focus, and then i clicked again, it was deselected.. which i didn’t need to. So the workaround was to cancel the standard mouseup event:

$('input').focus(function() { $(this).select(); }).click(function() { $(this).select(); }).mouseup(function(e) { e.preventDefault(); });

And that’s it! Now it’s always auto-selected and deselected only on focus change.

Reload page with JavaScript

July 4th, 2011 by Alex No comments »

There’s a couple options for this in JavaScript

window.location.href=window.location.href;
window.location.reload();
history.go(0);

In HTML, it’s simply

<!-- reload in 60 seconds -->
<meta http-equiv="refresh" content="60" />