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