Geotag your photos with Perl and GPX files


Geotagging photos used to be a very expensive operation, since you needed special equipment, heavy antennas and especially: a lot muscles to carry all your extra gear.

Nowadays, a lot of people have iPhone or like me, an Android phone. A descent Android phone contains a GPS device. A GPS device connected to a computer (or Android phone) could record your GPS positions during a certain period. Ok … that’s cool actually!
Because, when you’re shooting photos, you practically always have your mobile phone with you!

So, all we need now is a handy tool on your Android phone, which will record or track your GPS positions during a certian period. Behold ‘Open GPS Tracker‘. Open GPS Tracker is a tool which allows you to track your GPS positions and then afterwards, when you stop the program, you can export and share this information. The export is done using the GPX format, sharing is done through email.

Of course, any GPS device (plus software) which generates GPX files, will do just fine 😉

This GPX file is nothing more than an XML file. And XML files are easy to parse in Perl using the XML::LibXML module.
We will also need to other CPAN modules, Date::Parse and Image::ExifTool. The first one is used to parse the date strings in the GPX XML file and the date strings in the EXIF information of the photo. The second module is used to read the EXIF information into a Perl hash.

use strict; use warnings;
use XML::LibXML;
use Image::ExifTool;
use Date::Parse;

use constant MAX_TIME_THRESHOLD => 10;

my ($gpx_file) = './Track201104180901.gpx';
my ($gpx_obj)  = XML::LibXML->new() ;
my $xml        = $gpx_obj->parse_file($gpx_file);
my $root       = $xml->getDocumentElement;

my $gps_db;
foreach my $pt ( $root->getElementsByTagName('trkpt') ) {
    my($lat)  = $pt->findvalue('@lat');
    my($lon)  = $pt->findvalue('@lon');
    my($alt)  = $pt->getElementsByTagName('ele')->[0]->textContent();
    my($time) = $pt->getElementsByTagName('time')->[0]->textContent();
    my $utime = str2time($time);

    $gps_db->{$utime} = {
        'time'   => $time,
        'lat'    => $lat,
        'lon'    => $lon,
        'alt'    => $alt,

In this first part, we read in the XML file and store relevant information – like latitude, longitude and altitude – in a hash.

When the hash is ready, we can start scanning some photos.

my @images = (qw/jmp151.jpg jmp152.jpg jmp154.jpg/);
foreach my $img (@images) {
    my $exif = Image::ExifTool->new();
    my $info = $exif->ImageInfo($img);
    if(not defined $info->{GPSLatitude} and not defined $info->{GPSLongitude}){
        next unless $info->{DateTimeOriginal};
        my $data = get_gps_fix($info->{DateTimeOriginal});
        next unless defined $data and keys %{$data};

        $exif->SetNewValue('GPSLatitude',  $data->{lat});
        $exif->SetNewValue('GPSLongitude', $data->{lon});
        $exif->SetNewValue('GPSAltitude',  $data->{alt});

        if( $exif->WriteInfo($img) ){
            print "Image [$img] EXIF info has been successfully updatedn";

sub get_gps_fix {
    my($time) = @_;
    $time =~ s/ /T/;
    $time =~ s/$/Z/;
    my $utime = str2time($time);

    for(my $count=0; $count < MAX_TIME_THRESHOLD; $count++) {
        my $index = $utime - $count;
        if(exists $gps_db->{"$index"}){
            return $gps_db->{"$index"}

I’m using a constant called MAX_TIME_THRESHOLD, because it may happen that at a certian timestamp, no GPS position was recorded or available. The threshold I use goes back maximum 10 seconds before giving up. Moreover, if the logging density has been set to Fine, Open GPS Tracker should record also every second a GPS position.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Previous Post

Generate thumbnails with Perl and Image Magick

Next Post

Connect your home and company networks with OpenVPN

Related Posts