2 # Copyright (C) 2010 Rob Norris <rw_norris@hotmail.com>
6 A script to auto generate basic Viking .vik files for directories containing images.
8 Simply recursively search down the directory tree (from the current location) for suitable image files
9 [normally jpg|JPG (probably photographs)] and then extract any location data from the EXIF part.
11 For each directory this info is output to a file into either Viking (default) or GPX data file formats.
12 Output filename is waypoints.vik (or waypoints.gpx in GPX mode) unless the -o option is specified.
15 -g put into outputting GPX file mode
16 -o <name> - specify output base filename (overriding 'waypoints')
17 -r make waypoint image filenames relative (rather than absolute)
20 . exiftool - getting location data from EXIF (Debian package libimage-exiftool-perl)
23 . viking - Obviously when viking mode used to view the output [can view gpx as well]
24 . gpscorrelate[-gui] - Correlates digital photos with GPS data filling EXIF fields
25 i.e. something to put EXIF info into files to begin with
27 Various improvements can be:
28 . Command line options to control things eg:
29 .which symbol to use for each point
30 .which Viking Map / other viking defaults
32 .a mode to generate one massive file instead of one per directory
34 . Work out zoom factor to see all points in Viking
35 . Metadata bounds for gpx
37 . Any Speed optimizations - deciding which files to process could be improved
38 . Maybe better control of which files to analyse - maybe any - not just files named .jpg
39 . Is even doing this in Perl the best tool for the job [consider python, C, etc...]?
43 # ************ START OF CODE *******************
47 use Image::ExifTool qw(:Public);
50 use constant VIKING => 1;
51 use constant GPX => 2;
53 use constant RELATIVE => 1;
54 use constant ABSOLUTE => 2;
56 #################################
57 # Some global variables
58 # Create a new Image::ExifTool object
59 my $exifTool = new Image::ExifTool;
61 my @waypoint = ("","","","","","");
62 my @position = ("0.0", "0.0"); # lat / long
68 my $imagefilemode = ABSOLUTE;
70 #################################
72 # Write header first part of .vik file
76 #VIKING GPS Data file http://viking.sf.net/
102 velocity_min=0.000000
103 velocity_max=5.000000
125 # Write header first part of .gpx file
128 <?xml version="1.0" encoding="UTF-8"?>
132 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
133 xmlns="http://www.topografix.com/GPX/1/1"
134 xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd">
136 # TODO consider metadata time & bounds info
143 sub My_Process_File {
144 my ($dir, $file) = @_;
149 # Only do files we are interested in
150 unless ($file =~ m/\.(?:JPG|jpg)$/) {
153 #print "My_Process_File $file\n";
155 @waypoint = ("","","","","","");
157 # Extract meta information from an image
158 my $info = $exifTool->ImageInfo("$dir/$file");
166 foreach (sort keys %$info) {
167 #print "$file: $_ => $$info{$_}\n";
169 # If we can make the sort in reverse
171 #if (/GPSVersionID/) {
172 # unless ("$$info{$_}" eq "2.0.0.0") {
173 # # Only handle version 2 Ids
178 # Assume datum is in WGS-84
179 if (/^GPSLatitude\ /) {
180 $waypoint[0] = $$info{$_};
184 if (/^GPSLatitudeRef/) {
185 $waypoint[1] = $$info{$_};
189 if (/^GPSLongitude\ /) {
190 $waypoint[2] = $$info{$_};
194 if (/^GPSLongitudeRef/) {
195 $waypoint[3] = $$info{$_};
199 if (/^GPSAltitude\ /) {
200 my @row = split / /, $$info{$_};
201 $waypoint[4] = $row[0]; #hopefully in metres
205 if (/^DateTimeOriginal$/) {
206 $waypoint[5] = $$info{$_};
213 if ($waypoint[1] eq "South" && $waypoint[0] ne "") {
214 $waypoint[0] = '-'."$waypoint[0]";
218 if ($waypoint[3] eq "West" && $waypoint[2] ne "") {
219 $waypoint[2] = '-'."$waypoint[2]";
223 if ($waypoint[0] ne "" && $waypoint[2] ne "") {
224 # Update position so can track where to center map
225 $position[0] = $waypoint[0];
226 $position[1] = $waypoint[2];
227 my ($name, $path) = File::Basename::fileparse ($file, qr/\.[^.]*/);
228 my $filename = "$file"; # Relative filename
229 if ($mode == VIKING) {
230 # ATM, Viking (0.9.94) wp image loading only works if absolute
232 # Create absolute filename
233 if ($imagefilemode == ABSOLUTE) {
234 my ($abs_path) = File::Spec->rel2abs("$path", "$dir");
235 $filename = "$abs_path/$file";
237 return "type=\"waypoint\" latitude=\"$waypoint[0]\" longitude=\"$waypoint[2]\" name=\"$name\" altitude=\"$waypoint[4]\" comment=\"$waypoint[5]\" image=\"$filename\" symbol=\"scenic\"\n";
240 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>";
252 type="waypointlistend"
271 my $FTT = 1; # First Time Through flag to mark first pass
272 my $has_location = 0;
274 #print "My_Process_Dir $dir\n";
275 opendir(DIR, $dir) or die "$0: can't opendir $dir: $!\n";
278 while (defined(my $file = readdir(DIR))) {
279 next if $file =~ /^\.\.?$/; # skip . and ..
280 $line = My_Process_File($dir, $file);
281 # At least one file with location data exists
282 if (defined ($line)) {
283 if ($line ne "" && $FTT == 1) {
285 open (FILE, ">$dir/$out_file") or die "$0: Can not open $!\n";
287 if ($mode == VIKING) {
288 print FILE Header_Viking();
291 print FILE Header_GPX();
293 $has_location = 1; # Remember that we have found something
302 if ($mode == VIKING) {
303 print FILE Footer_Viking();
306 print FILE Footer_GPX();
319 return if $file =~ /^\.\.$/; # Ensure skip ..
320 print "$0: Doing directory $file\n";
321 My_Process_Dir($file);
324 ############ START ##################
327 my $out_file_start = "waypoints";
330 for (my $arg=0; $arg < $#ARGV+1; $arg++) {
331 if ("$ARGV[$arg]" eq "-o") {
332 # Set filename to next arg
334 $out_file_start = $ARGV[$arg + 1];
337 if ("$ARGV[$arg]" eq "-g") {
340 if ("$ARGV[$arg]" eq "-r") {
341 $imagefilemode = RELATIVE;
345 #print "$0: Mode is $mode\n";
347 if ($mode == VIKING) {
348 $out_file = "$out_file_start".".vik";
351 $out_file = "$out_file_start".".gpx";
353 #print "$0: File output is $out_file\n";
355 $exifTool->Options(CoordFormat => q{%.6f});
356 $exifTool->Options(FastScan => 1);
358 # Only get information in Standard EXIF
359 $exifTool->Options(Group0 => ['EXIF']);
361 find(\&Process_File, ".");