2 # Copyright (C) 2010 Rob Norris <rw_norris@hotmail.com>
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation; either version 2 of the License, or
7 # (at your option) any later version.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 A script to auto generate basic Viking .vik files for directories containing images.
24 Simply recursively search down the directory tree (from the current location) for suitable image files
25 [normally jpg|JPG (probably photographs)] and then extract any location data from the EXIF part.
27 For each directory this info is output to a file into either Viking (default) or GPX data file formats.
28 Output filename is waypoints.vik (or waypoints.gpx in GPX mode) unless the -o option is specified.
31 -g put into outputting GPX file mode
32 -o <name> - specify output base filename (overriding 'waypoints')
33 -r make waypoint image filenames relative (rather than absolute)
36 . exiftool - getting location data from EXIF (Debian package libimage-exiftool-perl)
39 . viking - Obviously when viking mode used to view the output [can view gpx as well]
40 . gpscorrelate[-gui] - Correlates digital photos with GPS data filling EXIF fields
41 i.e. something to put EXIF info into files to begin with
43 Various improvements can be:
44 . Command line options to control things eg:
45 .which symbol to use for each point
46 .which Viking Map / other viking defaults
48 .a mode to generate one massive file instead of one per directory
50 . Work out zoom factor to see all points in Viking
51 . Metadata bounds for gpx
53 . Any Speed optimizations - deciding which files to process could be improved
54 . Maybe better control of which files to analyse - maybe any - not just files named .jpg
55 . Is even doing this in Perl the best tool for the job [consider python, C, etc...]?
59 # ************ START OF CODE *******************
63 use Image::ExifTool qw(:Public);
66 use constant VIKING => 1;
67 use constant GPX => 2;
69 use constant RELATIVE => 1;
70 use constant ABSOLUTE => 2;
72 #################################
73 # Some global variables
74 # Create a new Image::ExifTool object
75 my $exifTool = new Image::ExifTool;
77 my @waypoint = ("","","","","","");
78 my @position = ("0.0", "0.0"); # lat / long
84 my $imagefilemode = ABSOLUTE;
86 #################################
88 # Write header first part of .vik file
92 #VIKING GPS Data file http://viking.sf.net/
118 velocity_min=0.000000
119 velocity_max=5.000000
141 # Write header first part of .gpx file
144 <?xml version="1.0" encoding="UTF-8"?>
148 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
149 xmlns="http://www.topografix.com/GPX/1/1"
150 xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd">
152 # TODO consider metadata time & bounds info
159 sub My_Process_File {
160 my ($dir, $file) = @_;
165 # Only do files we are interested in
166 unless ($file =~ m/\.(?:JPG|jpg)$/) {
169 #print "My_Process_File $file\n";
171 @waypoint = ("","","","","","");
173 # Extract meta information from an image
174 my $info = $exifTool->ImageInfo("$dir/$file");
182 foreach (sort keys %$info) {
183 #print "$file: $_ => $$info{$_}\n";
185 # If we can make the sort in reverse
187 #if (/GPSVersionID/) {
188 # unless ("$$info{$_}" eq "2.0.0.0") {
189 # # Only handle version 2 Ids
194 # Assume datum is in WGS-84
195 if (/^GPSLatitude\ /) {
196 $waypoint[0] = $$info{$_};
200 if (/^GPSLatitudeRef/) {
201 $waypoint[1] = $$info{$_};
205 if (/^GPSLongitude\ /) {
206 $waypoint[2] = $$info{$_};
210 if (/^GPSLongitudeRef/) {
211 $waypoint[3] = $$info{$_};
215 if (/^GPSAltitude\ /) {
216 my @row = split / /, $$info{$_};
217 $waypoint[4] = $row[0]; #hopefully in metres
221 if (/^DateTimeOriginal$/) {
222 $waypoint[5] = $$info{$_};
229 if ($waypoint[1] eq "South" && $waypoint[0] ne "") {
230 $waypoint[0] = '-'."$waypoint[0]";
234 if ($waypoint[3] eq "West" && $waypoint[2] ne "") {
235 $waypoint[2] = '-'."$waypoint[2]";
239 if ($waypoint[0] ne "" && $waypoint[2] ne "") {
240 # Update position so can track where to center map
241 $position[0] = $waypoint[0];
242 $position[1] = $waypoint[2];
243 my ($name, $path) = File::Basename::fileparse ($file, qr/\.[^.]*/);
244 my $filename = "$file"; # Relative filename
245 if ($mode == VIKING) {
246 # ATM, Viking (0.9.94) wp image loading only works if absolute
248 # Create absolute filename
249 if ($imagefilemode == ABSOLUTE) {
250 my ($abs_path) = File::Spec->rel2abs("$path", "$dir");
251 $filename = "$abs_path/$file";
253 return "type=\"waypoint\" latitude=\"$waypoint[0]\" longitude=\"$waypoint[2]\" name=\"$name\" altitude=\"$waypoint[4]\" comment=\"$waypoint[5]\" image=\"$filename\" symbol=\"scenic\"\n";
256 return "<wpt lat=\"$waypoint[0]\" lon=\"$waypoint[2]\">\n <name>$name</name>\n <ele>$waypoint[4]</ele>\n <desc>$waypoint[5]</desc>\n <sym>secenic</sym>\n</wpt>";
268 type="waypointlistend"
287 my $FTT = 1; # First Time Through flag to mark first pass
288 my $has_location = 0;
290 #print "My_Process_Dir $dir\n";
291 opendir(DIR, $dir) or die "$0: can't opendir $dir: $!\n";
294 while (defined(my $file = readdir(DIR))) {
295 next if $file =~ /^\.\.?$/; # skip . and ..
296 $line = My_Process_File($dir, $file);
297 # At least one file with location data exists
298 if (defined ($line)) {
299 if ($line ne "" && $FTT == 1) {
301 open (FILE, ">$dir/$out_file") or die "$0: Can not open $!\n";
303 if ($mode == VIKING) {
304 print FILE Header_Viking();
307 print FILE Header_GPX();
309 $has_location = 1; # Remember that we have found something
318 if ($mode == VIKING) {
319 print FILE Footer_Viking();
322 print FILE Footer_GPX();
335 return if $file =~ /^\.\.$/; # Ensure skip ..
336 print "$0: Doing directory $file\n";
337 My_Process_Dir($file);
340 ############ START ##################
343 my $out_file_start = "waypoints";
346 for (my $arg=0; $arg < $#ARGV+1; $arg++) {
347 if ("$ARGV[$arg]" eq "-o") {
348 # Set filename to next arg
350 $out_file_start = $ARGV[$arg + 1];
353 if ("$ARGV[$arg]" eq "-g") {
356 if ("$ARGV[$arg]" eq "-r") {
357 $imagefilemode = RELATIVE;
361 #print "$0: Mode is $mode\n";
363 if ($mode == VIKING) {
364 $out_file = "$out_file_start".".vik";
367 $out_file = "$out_file_start".".gpx";
369 #print "$0: File output is $out_file\n";
371 $exifTool->Options(CoordFormat => q{%.6f});
372 $exifTool->Options(FastScan => 1);
374 # Only get information in Standard EXIF
375 $exifTool->Options(Group0 => ['EXIF']);
377 find(\&Process_File, ".");