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.
23 Note that from Viking 1.3 onwards it can load geotagged images directly,
24 although it does not have recursive directory capabilities.
26 Simply recursively search down the directory tree (from the current location) for suitable image files
27 [normally jpg|JPG (probably photographs)] and then extract any location data from the EXIF part.
29 For each directory this info is output to a file into either Viking (default) or GPX data file formats.
30 Output filename is waypoints.vik (or waypoints.gpx in GPX mode) unless the -o option is specified.
34 -g put into outputting GPX file mode
35 -o <name> - specify output base filename (overriding 'waypoints')
36 -r make waypoint image filenames relative (rather than absolute)
39 . exiftool - getting location data from EXIF (Debian package libimage-exiftool-perl)
41 Various improvements can be:
42 . Command line options to control things eg:
43 .which symbol to use for each point
44 .which Viking Map / other viking defaults
46 .a mode to generate one massive file instead of one per directory
48 . Work out zoom factor to see all points in Viking
49 . Metadata bounds for gpx
51 . Any Speed optimizations - deciding which files to process could be improved
52 . Maybe better control of which files to analyse - maybe any - not just files named .jpg
53 . Is even doing this in Perl the best tool for the job [consider python, C, etc...]?
57 # ************ START OF CODE *******************
61 use Image::ExifTool qw(:Public);
64 use constant VIKING => 1;
65 use constant GPX => 2;
67 use constant RELATIVE => 1;
68 use constant ABSOLUTE => 2;
70 #################################
71 # Some global variables
72 # Create a new Image::ExifTool object
73 my $exifTool = new Image::ExifTool;
75 my @waypoint = ("","","","","","");
76 my @position = ("0.0", "0.0"); # lat / long
82 my $imagefilemode = ABSOLUTE;
84 #################################
86 # Write header first part of .vik file
90 #VIKING GPS Data file http://viking.sf.net/
138 # Write header first part of .gpx file
141 <?xml version="1.0" encoding="UTF-8"?>
145 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
146 xmlns="http://www.topografix.com/GPX/1/1"
147 xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd">
149 # TODO consider metadata time & bounds info
156 sub My_Process_File {
157 my ($dir, $file) = @_;
162 # Only do files we are interested in
163 unless ($file =~ m/\.(?:JPG|jpg)$/) {
166 #print "My_Process_File $file\n";
168 @waypoint = ("","","","","","");
170 # Extract meta information from an image
171 my $info = $exifTool->ImageInfo("$dir/$file");
179 foreach (sort keys %$info) {
180 #print "$file: $_ => $$info{$_}\n";
182 # If we can make the sort in reverse
184 #if (/GPSVersionID/) {
185 # unless ("$$info{$_}" eq "2.0.0.0") {
186 # # Only handle version 2 Ids
191 # Assume datum is in WGS-84
192 if (/^GPSLatitude\ /) {
193 $waypoint[0] = $$info{$_};
197 if (/^GPSLatitudeRef/) {
198 $waypoint[1] = $$info{$_};
202 if (/^GPSLongitude\ /) {
203 $waypoint[2] = $$info{$_};
207 if (/^GPSLongitudeRef/) {
208 $waypoint[3] = $$info{$_};
212 if (/^GPSAltitude\ /) {
213 my @row = split / /, $$info{$_};
214 $waypoint[4] = $row[0]; #hopefully in metres
218 if (/^DateTimeOriginal$/) {
219 $waypoint[5] = $$info{$_};
226 if ($waypoint[1] eq "South" && $waypoint[0] ne "") {
227 $waypoint[0] = '-'."$waypoint[0]";
231 if ($waypoint[3] eq "West" && $waypoint[2] ne "") {
232 $waypoint[2] = '-'."$waypoint[2]";
236 if ($waypoint[0] ne "" && $waypoint[2] ne "") {
237 # Update position so can track where to center map
238 $position[0] = $waypoint[0];
239 $position[1] = $waypoint[2];
240 my ($name, $path) = File::Basename::fileparse ($file, qr/\.[^.]*/);
241 my $filename = "$file"; # Relative filename
242 if ($mode == VIKING) {
243 # ATM, Viking (0.9.94) wp image loading only works if absolute
245 # Create absolute filename
246 if ($imagefilemode == ABSOLUTE) {
247 my ($abs_path) = File::Spec->rel2abs("$path", "$dir");
248 $filename = "$abs_path/$file";
250 return "type=\"waypoint\" latitude=\"$waypoint[0]\" longitude=\"$waypoint[2]\" name=\"$name\" altitude=\"$waypoint[4]\" comment=\"$waypoint[5]\" image=\"$filename\" symbol=\"scenic area\"\n";
253 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>";
265 type="waypointlistend"
284 my $FTT = 1; # First Time Through flag to mark first pass
285 my $has_location = 0;
287 #print "My_Process_Dir $dir\n";
288 opendir(DIR, $dir) or die "$0: can't opendir $dir: $!\n";
291 while (defined(my $file = readdir(DIR))) {
292 next if $file =~ /^\.\.?$/; # skip . and ..
293 $line = My_Process_File($dir, $file);
294 # At least one file with location data exists
295 if (defined ($line)) {
296 if ($line ne "" && $FTT == 1) {
298 open (FILE, ">$dir/$out_file") or die "$0: Can not open $!\n";
300 if ($mode == VIKING) {
301 print FILE Header_Viking();
304 print FILE Header_GPX();
306 $has_location = 1; # Remember that we have found something
315 if ($mode == VIKING) {
316 print FILE Footer_Viking();
319 print FILE Footer_GPX();
332 return if $file =~ /^\.\.$/; # Ensure skip ..
333 print "$0: Doing directory $file\n";
334 My_Process_Dir($file);
337 ############ START ##################
340 my $out_file_start = "waypoints";
343 for (my $arg=0; $arg < $#ARGV+1; $arg++) {
344 if ("$ARGV[$arg]" eq "-o") {
345 # Set filename to next arg
347 $out_file_start = $ARGV[$arg + 1];
350 if ("$ARGV[$arg]" eq "-g") {
353 if ("$ARGV[$arg]" eq "-r") {
354 $imagefilemode = RELATIVE;
358 #print "$0: Mode is $mode\n";
360 if ($mode == VIKING) {
361 $out_file = "$out_file_start".".vik";
364 $out_file = "$out_file_start".".gpx";
366 #print "$0: File output is $out_file\n";
368 $exifTool->Options(CoordFormat => q{%.6f});
369 $exifTool->Options(FastScan => 1);
371 # Only get information in Standard EXIF
372 $exifTool->Options(Group0 => ['EXIF']);
374 find(\&Process_File, ".");