]> git.street.me.uk Git - andy/viking.git/blob - tools/images2waypoints.pl
Add map's logo support
[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 drawmode=0
109 drawlines=t
110 drawpoints=t
111 drawelevation=f
112 elevation_factor=30
113 drawstops=f
114 stop_length=60
115 line_thickness=1
116 bg_line_thickness=0
117 trackbgcolor=#ffffff
118 velocity_min=0.000000
119 velocity_max=5.000000
120 drawlabels=t
121 wpcolor=#000000
122 wptextcolor=#ffffff
123 wpbgcolor=#8383c4
124 wpbgand=t
125 wpsymbol=0
126 wpsize=4
127 wpsyms=t
128 drawimages=t
129 image_size=64
130 image_alpha=255
131 image_cache_size=300
132
133
134 ~LayerData
135 type="waypointlist"
136 END
137 #
138 #
139 }
140
141 # Write header first part of .gpx file
142 sub Header_GPX {
143     return <<END;
144 <?xml version="1.0" encoding="UTF-8"?>
145 <gpx
146  version="1.1"
147 creator="$0"
148 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
149 xmlns="http://www.topografix.com/GPX/1/1"
150 xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd">
151 END
152 # TODO consider metadata time & bounds info
153 }
154
155 sub Footer_GPX {
156     return "</gpx>\n";
157 }
158
159 sub My_Process_File {
160     my ($dir, $file) = @_;
161
162     ## Start
163     ##
164
165     # Only do files we are interested in
166     unless ($file =~ m/\.(?:JPG|jpg)$/) {
167         return;
168     }
169     #print "My_Process_File $file\n";
170
171     @waypoint = ("","","","","","");
172
173     # Extract meta information from an image
174     my $info = $exifTool->ImageInfo("$dir/$file");
175
176     #DateTimeOriginal
177     #GPSVersionID
178     #GPSAltitude
179     #GPSLatitude (1)
180     #GPSLongitude (1)
181
182     foreach (sort keys %$info) {
183         #print "$file: $_ => $$info{$_}\n";
184         
185         # If we can make the sort in reverse 
186
187         #if (/GPSVersionID/) {
188         #    unless ("$$info{$_}" eq "2.0.0.0") {
189         #       # Only handle version 2 Ids
190         #       return;
191         #    }
192         #}
193
194         # Assume datum is in WGS-84
195         if (/^GPSLatitude\ /) {
196             $waypoint[0] = $$info{$_};
197             next;
198         }
199
200         if (/^GPSLatitudeRef/) {
201             $waypoint[1] = $$info{$_};
202             next;
203         }
204
205         if (/^GPSLongitude\ /) {
206             $waypoint[2] = $$info{$_};
207             next;
208         }
209
210         if (/^GPSLongitudeRef/) {
211             $waypoint[3] = $$info{$_};
212             next;
213         }
214
215         if (/^GPSAltitude\ /) {
216             my @row = split / /, $$info{$_};
217             $waypoint[4] = $row[0]; #hopefully in metres
218             next;
219         }
220
221         if (/^DateTimeOriginal$/) {
222             $waypoint[5] = $$info{$_};
223             next;
224         }
225
226     }
227
228     # Check for South
229     if ($waypoint[1] eq "South" && $waypoint[0] ne "") {
230         $waypoint[0] = '-'."$waypoint[0]";
231     }
232
233     # Check for West
234     if ($waypoint[3] eq "West" && $waypoint[2] ne "") {
235         $waypoint[2] = '-'."$waypoint[2]";
236     }
237
238     # At least lat/long
239     if ($waypoint[0] ne "" && $waypoint[2] ne "") {
240         # Update position so can track where to center map
241         $position[0] = $waypoint[0];
242         $position[1] = $waypoint[2];
243         my ($name, $path) = File::Basename::fileparse ($file, qr/\.[^.]*/);
244         my $filename = "$file"; # Relative filename
245         if ($mode == VIKING) {
246             # ATM, Viking (0.9.94) wp image loading only works if absolute
247             # See SF 2998555
248             # Create absolute filename
249             if ($imagefilemode == ABSOLUTE) {
250                 my ($abs_path) = File::Spec->rel2abs("$path", "$dir");
251                 $filename = "$abs_path/$file";
252             }
253             return "type=\"waypoint\" latitude=\"$waypoint[0]\" longitude=\"$waypoint[2]\" name=\"$name\" altitude=\"$waypoint[4]\" comment=\"$waypoint[5]\" image=\"$filename\" symbol=\"scenic\"\n";
254         }
255         else {
256             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>";
257         }
258
259     }
260
261     return "";
262     ## END
263     ##
264 }
265
266 sub Footer_Viking {
267         return <<END;
268 type="waypointlistend"
269 ~EndLayerData
270 ~EndLayer
271
272 xmpp=32.000000
273 ympp=32.000000
274 lat=$position[0]
275 lon=$position[1]
276 mode=mercator
277 color=#cccccc
278 drawscale=t
279 drawcentermark=t
280
281 END
282 }
283
284 #
285 sub My_Process_Dir {
286     my ($dir) = @_;
287     my $FTT = 1; # First Time Through flag to mark first pass
288     my $has_location = 0; 
289
290     #print "My_Process_Dir $dir\n";
291     opendir(DIR, $dir) or die "$0: can't opendir $dir: $!\n";
292     
293     my $line;
294     while (defined(my $file = readdir(DIR))) {
295         next if $file =~ /^\.\.?$/;     # skip . and ..
296         $line = My_Process_File($dir, $file);
297         # At least one file with location data exists
298         if (defined ($line)) {
299             if ($line ne "" && $FTT == 1) {
300                 $FTT = 0;
301                 open (FILE, ">$dir/$out_file") or die "$0: Can not open $!\n";
302
303                 if ($mode == VIKING) {
304                     print FILE Header_Viking();
305                 }
306                 else {
307                     print FILE Header_GPX();
308                 }
309                 $has_location = 1; # Remember that we have found something
310             }
311             if ($line ne "") {
312                 print FILE $line;
313             }
314         }
315     }
316
317     if ($has_location) {
318         if ($mode == VIKING) {
319             print FILE Footer_Viking();
320         }
321         else {
322             print FILE Footer_GPX();
323         }
324         close (FILE);
325     }
326     closedir(DIR);
327 }
328
329 #
330 sub Process_File {
331     my $file = $_;
332     unless (-d $file) {
333         return;
334     }    
335     return if $file =~ /^\.\.$/; # Ensure skip ..
336     print "$0: Doing directory $file\n";
337     My_Process_Dir($file);
338 }
339
340 ############ START ##################
341
342 # Default filename
343 my $out_file_start = "waypoints";
344
345 if (defined @ARGV) {
346     for (my $arg=0; $arg < $#ARGV+1; $arg++) {
347         if ("$ARGV[$arg]" eq "-o") {
348             # Set filename to next arg
349             if ($arg < $#ARGV) {
350                 $out_file_start = $ARGV[$arg + 1];
351             }
352         }
353         if ("$ARGV[$arg]" eq "-g") {
354             $mode = GPX;
355         }
356         if ("$ARGV[$arg]" eq "-r") {
357             $imagefilemode = RELATIVE;
358         }
359     }
360 }
361 #print "$0: Mode is $mode\n";
362
363 if ($mode == VIKING) {
364     $out_file = "$out_file_start".".vik";
365 }
366 else {
367     $out_file = "$out_file_start".".gpx";
368 }
369 #print "$0: File output is $out_file\n";
370
371 $exifTool->Options(CoordFormat => q{%.6f});
372 $exifTool->Options(FastScan => 1);
373
374 # Only get information in Standard EXIF
375 $exifTool->Options(Group0 => ['EXIF']);
376
377 find(\&Process_File, ".");
378
379 #end