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