]> git.street.me.uk Git - andy/viking.git/blob - tools/images2waypoints.pl
Set 'OK' default for download along track dialog.
[andy/viking.git] / tools / images2waypoints.pl
1 #! /usr/bin/perl -w
2 # Copyright (C) 2010 Rob Norris <rw_norris@hotmail.com>
3
4 =head1 Overview
5
6 A script to auto generate basic Viking .vik files for directories containing images.
7
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.
10
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.
13
14 Options:
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)
18
19 Required programs:
20 . exiftool - getting location data from EXIF (Debian package libimage-exiftool-perl)
21
22 Recommended programs:
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
26
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
31     .a non recursive mode
32     .a mode to generate one massive file instead of one per directory 
33
34 . Work out zoom factor to see all points in Viking
35 . Metadata bounds for gpx
36
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...]?
40
41 =cut 
42
43 # ************ START OF CODE *******************
44
45 use strict;
46 use File::Find;
47 use Image::ExifTool qw(:Public);
48
49 # Output modes
50 use constant VIKING => 1;
51 use constant GPX => 2;
52
53 use constant RELATIVE => 1;
54 use constant ABSOLUTE => 2;
55
56 #################################
57 # Some global variables
58 # Create a new Image::ExifTool object
59 my $exifTool = new Image::ExifTool;
60
61 my @waypoint = ("","","","","","");
62 my @position = ("0.0", "0.0"); # lat / long
63
64 my $out_file;
65
66 # Default mode
67 my $mode = VIKING;
68 my $imagefilemode = ABSOLUTE;
69
70 #################################
71
72 # Write header first part of .vik file
73 sub Header_Viking {
74
75     return <<END;
76 #VIKING GPS Data file http://viking.sf.net/
77
78 ~Layer Map
79 name=Map
80 mode=13
81 directory=
82 alpha=255
83 autodownload=f
84 mapzoom=0
85 ~EndLayer
86
87
88 ~Layer TrackWaypoint
89 name=TrackWaypoint
90 tracks_visible=f
91 waypoints_visible=t
92 drawmode=0
93 drawlines=t
94 drawpoints=t
95 drawelevation=f
96 elevation_factor=30
97 drawstops=f
98 stop_length=60
99 line_thickness=1
100 bg_line_thickness=0
101 trackbgcolor=#ffffff
102 velocity_min=0.000000
103 velocity_max=5.000000
104 drawlabels=t
105 wpcolor=#000000
106 wptextcolor=#ffffff
107 wpbgcolor=#8383c4
108 wpbgand=t
109 wpsymbol=0
110 wpsize=4
111 wpsyms=t
112 drawimages=t
113 image_size=64
114 image_alpha=255
115 image_cache_size=300
116
117
118 ~LayerData
119 type="waypointlist"
120 END
121 #
122 #
123 }
124
125 # Write header first part of .gpx file
126 sub Header_GPX {
127     return <<END;
128 <?xml version="1.0" encoding="UTF-8"?>
129 <gpx
130  version="1.1"
131 creator="$0"
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">
135 END
136 # TODO consider metadata time & bounds info
137 }
138
139 sub Footer_GPX {
140     return "</gpx>\n";
141 }
142
143 sub My_Process_File {
144     my ($dir, $file) = @_;
145
146     ## Start
147     ##
148
149     # Only do files we are interested in
150     unless ($file =~ m/\.(?:JPG|jpg)$/) {
151         return;
152     }
153     #print "My_Process_File $file\n";
154
155     @waypoint = ("","","","","","");
156
157     # Extract meta information from an image
158     my $info = $exifTool->ImageInfo("$dir/$file");
159
160     #DateTimeOriginal
161     #GPSVersionID
162     #GPSAltitude
163     #GPSLatitude (1)
164     #GPSLongitude (1)
165
166     foreach (sort keys %$info) {
167         #print "$file: $_ => $$info{$_}\n";
168         
169         # If we can make the sort in reverse 
170
171         #if (/GPSVersionID/) {
172         #    unless ("$$info{$_}" eq "2.0.0.0") {
173         #       # Only handle version 2 Ids
174         #       return;
175         #    }
176         #}
177
178         # Assume datum is in WGS-84
179         if (/^GPSLatitude\ /) {
180             $waypoint[0] = $$info{$_};
181             next;
182         }
183
184         if (/^GPSLatitudeRef/) {
185             $waypoint[1] = $$info{$_};
186             next;
187         }
188
189         if (/^GPSLongitude\ /) {
190             $waypoint[2] = $$info{$_};
191             next;
192         }
193
194         if (/^GPSLongitudeRef/) {
195             $waypoint[3] = $$info{$_};
196             next;
197         }
198
199         if (/^GPSAltitude\ /) {
200             my @row = split / /, $$info{$_};
201             $waypoint[4] = $row[0]; #hopefully in metres
202             next;
203         }
204
205         if (/^DateTimeOriginal$/) {
206             $waypoint[5] = $$info{$_};
207             next;
208         }
209
210     }
211
212     # Check for South
213     if ($waypoint[1] eq "South" && $waypoint[0] ne "") {
214         $waypoint[0] = '-'."$waypoint[0]";
215     }
216
217     # Check for West
218     if ($waypoint[3] eq "West" && $waypoint[2] ne "") {
219         $waypoint[2] = '-'."$waypoint[2]";
220     }
221
222     # At least lat/long
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
231             # See SF 2998555
232             # Create absolute filename
233             if ($imagefilemode == ABSOLUTE) {
234                 my ($abs_path) = File::Spec->rel2abs("$path", "$dir");
235                 $filename = "$abs_path/$file";
236             }
237             return "type=\"waypoint\" latitude=\"$waypoint[0]\" longitude=\"$waypoint[2]\" name=\"$name\" altitude=\"$waypoint[4]\" comment=\"$waypoint[5]\" image=\"$filename\" symbol=\"scenic\"\n";
238         }
239         else {
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>";
241         }
242
243     }
244
245     return "";
246     ## END
247     ##
248 }
249
250 sub Footer_Viking {
251         return <<END;
252 type="waypointlistend"
253 ~EndLayerData
254 ~EndLayer
255
256 xmpp=32.000000
257 ympp=32.000000
258 lat=$position[0]
259 lon=$position[1]
260 mode=mercator
261 color=#cccccc
262 drawscale=t
263 drawcentermark=t
264
265 END
266 }
267
268 #
269 sub My_Process_Dir {
270     my ($dir) = @_;
271     my $FTT = 1; # First Time Through flag to mark first pass
272     my $has_location = 0; 
273
274     #print "My_Process_Dir $dir\n";
275     opendir(DIR, $dir) or die "$0: can't opendir $dir: $!\n";
276     
277     my $line;
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) {
284                 $FTT = 0;
285                 open (FILE, ">$dir/$out_file") or die "$0: Can not open $!\n";
286
287                 if ($mode == VIKING) {
288                     print FILE Header_Viking();
289                 }
290                 else {
291                     print FILE Header_GPX();
292                 }
293                 $has_location = 1; # Remember that we have found something
294             }
295             if ($line ne "") {
296                 print FILE $line;
297             }
298         }
299     }
300
301     if ($has_location) {
302         if ($mode == VIKING) {
303             print FILE Footer_Viking();
304         }
305         else {
306             print FILE Footer_GPX();
307         }
308         close (FILE);
309     }
310     closedir(DIR);
311 }
312
313 #
314 sub Process_File {
315     my $file = $_;
316     unless (-d $file) {
317         return;
318     }    
319     return if $file =~ /^\.\.$/; # Ensure skip ..
320     print "$0: Doing directory $file\n";
321     My_Process_Dir($file);
322 }
323
324 ############ START ##################
325
326 # Default filename
327 my $out_file_start = "waypoints";
328
329 if (defined @ARGV) {
330     for (my $arg=0; $arg < $#ARGV+1; $arg++) {
331         if ("$ARGV[$arg]" eq "-o") {
332             # Set filename to next arg
333             if ($arg < $#ARGV) {
334                 $out_file_start = $ARGV[$arg + 1];
335             }
336         }
337         if ("$ARGV[$arg]" eq "-g") {
338             $mode = GPX;
339         }
340         if ("$ARGV[$arg]" eq "-r") {
341             $imagefilemode = RELATIVE;
342         }
343     }
344 }
345 #print "$0: Mode is $mode\n";
346
347 if ($mode == VIKING) {
348     $out_file = "$out_file_start".".vik";
349 }
350 else {
351     $out_file = "$out_file_start".".gpx";
352 }
353 #print "$0: File output is $out_file\n";
354
355 $exifTool->Options(CoordFormat => q{%.6f});
356 $exifTool->Options(FastScan => 1);
357
358 # Only get information in Standard EXIF
359 $exifTool->Options(Group0 => ['EXIF']);
360
361 find(\&Process_File, ".");
362
363 #end