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/
140 # Write header first part of .gpx file
143 <?xml version="1.0" encoding="UTF-8"?>
147 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
148 xmlns="http://www.topografix.com/GPX/1/1"
149 xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd">
151 # TODO consider metadata time & bounds info
158 sub My_Process_File {
159 my ($dir, $file) = @_;
164 # Only do files we are interested in
165 unless ($file =~ m/\.(?:JPG|jpg)$/) {
168 #print "My_Process_File $file\n";
170 @waypoint = ("","","","","","");
172 # Extract meta information from an image
173 my $info = $exifTool->ImageInfo("$dir/$file");
181 foreach (sort keys %$info) {
182 #print "$file: $_ => $$info{$_}\n";
184 # If we can make the sort in reverse
186 #if (/GPSVersionID/) {
187 # unless ("$$info{$_}" eq "2.0.0.0") {
188 # # Only handle version 2 Ids
193 # Assume datum is in WGS-84
194 if (/^GPSLatitude\ /) {
195 $waypoint[0] = $$info{$_};
199 if (/^GPSLatitudeRef/) {
200 $waypoint[1] = $$info{$_};
204 if (/^GPSLongitude\ /) {
205 $waypoint[2] = $$info{$_};
209 if (/^GPSLongitudeRef/) {
210 $waypoint[3] = $$info{$_};
214 if (/^GPSAltitude\ /) {
215 my @row = split / /, $$info{$_};
216 $waypoint[4] = $row[0]; #hopefully in metres
220 if (/^DateTimeOriginal$/) {
221 $waypoint[5] = $$info{$_};
228 if ($waypoint[1] eq "South" && $waypoint[0] ne "") {
229 $waypoint[0] = '-'."$waypoint[0]";
233 if ($waypoint[3] eq "West" && $waypoint[2] ne "") {
234 $waypoint[2] = '-'."$waypoint[2]";
238 if ($waypoint[0] ne "" && $waypoint[2] ne "") {
239 # Update position so can track where to center map
240 $position[0] = $waypoint[0];
241 $position[1] = $waypoint[2];
242 my ($name, $path) = File::Basename::fileparse ($file, qr/\.[^.]*/);
243 my $filename = "$file"; # Relative filename
244 if ($mode == VIKING) {
245 # ATM, Viking (0.9.94) wp image loading only works if absolute
247 # Create absolute filename
248 if ($imagefilemode == ABSOLUTE) {
249 my ($abs_path) = File::Spec->rel2abs("$path", "$dir");
250 $filename = "$abs_path/$file";
252 return "type=\"waypoint\" latitude=\"$waypoint[0]\" longitude=\"$waypoint[2]\" name=\"$name\" altitude=\"$waypoint[4]\" comment=\"$waypoint[5]\" image=\"$filename\" symbol=\"scenic area\"\n";
255 return "<wpt lat=\"$waypoint[0]\" lon=\"$waypoint[2]\">\n <name>$name</name>\n <ele>$waypoint[4]</ele>\n <desc>$waypoint[5]</desc>\n <sym>scenic area</sym>\n</wpt>";
267 type="waypointlistend"
286 my $FTT = 1; # First Time Through flag to mark first pass
287 my $has_location = 0;
289 #print "My_Process_Dir $dir\n";
290 opendir(DIR, $dir) or die "$0: can't opendir $dir: $!\n";
293 while (defined(my $file = readdir(DIR))) {
294 next if $file =~ /^\.\.?$/; # skip . and ..
295 $line = My_Process_File($dir, $file);
296 # At least one file with location data exists
297 if (defined ($line)) {
298 if ($line ne "" && $FTT == 1) {
300 open (FILE, ">$dir/$out_file") or die "$0: Can not open $!\n";
302 if ($mode == VIKING) {
303 print FILE Header_Viking();
306 print FILE Header_GPX();
308 $has_location = 1; # Remember that we have found something
317 if ($mode == VIKING) {
318 print FILE Footer_Viking();
321 print FILE Footer_GPX();
334 return if $file =~ /^\.\.$/; # Ensure skip ..
335 print "$0: Doing directory $file\n";
336 My_Process_Dir($file);
339 ############ START ##################
342 my $out_file_start = "waypoints";
345 for (my $arg=0; $arg < $#ARGV+1; $arg++) {
346 if ("$ARGV[$arg]" eq "-o") {
347 # Set filename to next arg
349 $out_file_start = $ARGV[$arg + 1];
352 if ("$ARGV[$arg]" eq "-g") {
355 if ("$ARGV[$arg]" eq "-r") {
356 $imagefilemode = RELATIVE;
360 #print "$0: Mode is $mode\n";
362 if ($mode == VIKING) {
363 $out_file = "$out_file_start".".vik";
366 $out_file = "$out_file_start".".gpx";
368 #print "$0: File output is $out_file\n";
370 $exifTool->Options(CoordFormat => q{%.6f});
371 $exifTool->Options(FastScan => 1);
373 # Only get information in Standard EXIF
374 $exifTool->Options(Group0 => ['EXIF']);
376 find(\&Process_File, ".");