Extracting Photo Geocodes

How to extract the latitude and longitude from geocoded photos with PHP.

AU gets bonus points for doing this first but these days any GPS and camera enabled mobile lets you geocode your photos with the latitude and longitude which can be extracted to do all sorts of wonderful things.

Ingredients

Adding geocodes to a photo

A geocoded photo of Gumby
Gumby geocoded

The process of gecoding a photo varies between mobiles but it's usually through a menu option to add location information after the photo is saved. This information is then tucked away into the EXIF section of the photo which can be easily viewed in the image information section of most image tools.

GPS EXIF information
GPS information for the above photo using Apple Preview

Reading EXIF data with PHP

Grabbing EXIF data from a photo with PHP is a fairly simple process thanks to the exif_read_data() function: $arrPhotoExif = exif_read_data('media/gumby.jpg'); The above code is all you need to fill the array $arrPhotoExif with EXIF data from our Gumby photo. Turning to the ever faithful print_r() we can print_r($arrPhotoExif); to see the array details: Array ( [FileName] => gumby.jpg [FileDateTime] => 1211439939 [FileSize] => 30655 [FileType] => 2 [MimeType] => image/jpeg [SectionsFound] => ANY_TAG, IFD0, EXIF, GPS, INTEROP [COMPUTED] => Array ( [html] => width="240" height="320" [Height] => 320 [Width] => 240 [IsColor] => 1 [ByteOrderMotorola] => 1 ) [ImageDescription] => untitled [Make] => SHARP [Model] => 904SH [Orientation] => 1 [XResolution] => 72/1 [YResolution] => 72/1 [ResolutionUnit] => 2 [DateTime] => 2006:11:29 12:52:32 [YCbCrPositioning] => 2 [Exif_IFD_Pointer] => 203 [GPS_IFD_Pointer] => 357 [ExifVersion] => 0220 [DateTimeOriginal] => : : : : [DateTimeDigitized] => : : : : [ComponentsConfiguration] =>  [FlashPixVersion] => 0100 [ColorSpace] => 1 [ExifImageWidth] => 240 [ExifImageLength] => 320 [InteroperabilityOffset] => 581 [GPSVersion] =>  [GPSLatitudeRef] => N [GPSLatitude] => Array ( [0] => 34/1 [1] => 40/1 [2] => 273799/10000 ) [GPSLongitudeRef] => E [GPSLongitude] => Array ( [0] => 135/1 [1] => 29/1 [2] => 410700/10000 ) [GPSAltitudeRef] => [GPSAltitude] => 1624990/10000 [GPSTimeStamp] => Array ( [0] => 3/1 [1] => 52/1 [2] => 32/1 ) [GPSMapDatum] => WGS-84 [GPSDateStamp] => 2006:11:29 [InterOperabilityIndex] => R98 [InterOperabilityVersion] => 0100 )

Caution: Prevent disappearing geocodes

Unfortunately some image editors including PHP's own image functions do a good job at mangling photo EXIF data so if you are going to manipulate you geocoded photos in any way, make sure you grab and save the geocodes first.

Extracting EXIF GPS data with PHP

Some photos may not have any GPS info so before we go barging in to grab GPS data, it's a good idea to specify $arrPhotoExif should only be populated if GPS data is actually present by adding GPS to the optional exif_read_data sections parameter: $arrPhotoExif = exif_read_data('media/gumby.jpg','GPS');

The real juicy items in the GPS section are:

GPSLatitudeRef
North or south latitude
GPSLongitudeRef
East or west longitude
GPSLatitude
Array of latitude degrees, minutes and seconds
GPSLongitude
Array of longitude degrees, minutes and seconds

Once we have these values, it is a relatively simple process to extract the photo's latitude and longitude: $arrPhotoExif = exif_read_data('media/gumby.jpg','GPS'); // returns false if there's no GPS section in the EXIF data if($arrPhotoExif) { $arrLatDeg = split("/",$arrPhotoExif["GPSLatitude"][0]); $intLatDeg = $arrLatDeg[0]/$arrLatDeg[1]; $arrLatMin = split("/",$arrPhotoExif["GPSLatitude"][1]); $intLatMin = $arrLatMin[0]/$arrLatMin[1]; $arrLatSec = split("/",$arrPhotoExif["GPSLatitude"][2]); $intLatSec = $arrLatSec[0]/$arrLatSec[1]; $arrLongDeg = split("/",$arrPhotoExif["GPSLongitude"][0]); $intLongDeg = $arrLongDeg[0]/$arrLongDeg[1]; $arrLongMin = split("/",$arrPhotoExif["GPSLongitude"][1]); $intLongMin = $arrLongMin[0]/$arrLongMin[1]; $arrLongSec = split("/",$arrPhotoExif["GPSLongitude"][2]); $intLongSec = $arrLongSec[0]/$arrLongSec[1]; echo 'Latitude: '; echo $intLatDeg.'&deg;'; echo $intLatMin.'\''; echo $intLatSec.'"'; echo $arrPhotoExif["GPSLatitudeRef"]; echo '<br />'; echo 'Longitude: '; echo $intLongDeg.'&deg;'; echo $intLongMin.'\''; echo $intLongSec.'"'; echo $arrPhotoExif["GPSLongitudeRef"]; } For our photo, this returns: Latitude: 34°40'27.3799"N
Longitude: 135°29'41.07"E

Converting degrees to decimal

Although we have successfully extracted the photo's latitude and longitude, having these values in old school degrees, minutes and seconds in not that useful if you want to do something more interesting like integrate the photo with Google Maps which uses decimal values. Fortunately I have a wee function to do this on the fly: /************************************************** Type: FUNCTION Function: DegToDec Purpose: Converts degrees to decimal Input: $deg,$min,$sec Output: decimal value Requires: **************************************************/ function DegToDec($strRef,$intDeg,$intMin,$intSec) { $arrLatLong = array(); $arrLatLong["N"] = 1; $arrLatLong["E"] = 1; $arrLatLong["S"] = -1; $arrLatLong["W"] = -1; return ($intDeg+((($intMin*60)+($intSec))/3600)) * $arrLatLong[$strRef]; } $arrPhotoExif = exif_read_data('media/gumby.jpg','GPS'); // returns false if there's no GPS section in the EXIF data if($arrPhotoExif) { $arrLatDeg = split("/",$arrPhotoExif["GPSLatitude"][0]); $intLatDeg = $arrLatDeg[0]/$arrLatDeg[1]; $arrLatMin = split("/",$arrPhotoExif["GPSLatitude"][1]); $intLatMin = $arrLatMin[0]/$arrLatMin[1]; $arrLatSec = split("/",$arrPhotoExif["GPSLatitude"][2]); $intLatSec = $arrLatSec[0]/$arrLatSec[1]; $arrLongDeg = split("/",$arrPhotoExif["GPSLongitude"][0]); $intLongDeg = $arrLongDeg[0]/$arrLongDeg[1]; $arrLongMin = split("/",$arrPhotoExif["GPSLongitude"][1]); $intLongMin = $arrLongMin[0]/$arrLongMin[1]; $arrLongSec = split("/",$arrPhotoExif["GPSLongitude"][2]); $intLongSec = $arrLongSec[0]/$arrLongSec[1]; // round to 5 = approximately 1 meter accuracy $intLatitude = round(DegToDec($arrPhotoExif["GPSLatitudeRef"], $intLatDeg,$intLatMin,$intLatSec),5); $intLongitude = round(DegToDec($arrPhotoExif["GPSLongitudeRef"], $intLongDeg,$intLongMin,$intLongSec), 5); echo 'Latitude: '; echo $intLatitude; echo '<br />'; echo 'Longitude: '; echo $intLongitude; } This returns: Latitude: 34.67427
Longitude: 135.49474

Once we the decimal latitude and longitude for our geotagged photo sorted out, we can start having fun with Google Maps:

Enjoy!