]> git.street.me.uk Git - andy/viking.git/blame - tools/images2waypoints.pl
[QA] Convert DEG2RAD and RAD2DEG to macro functions
[andy/viking.git] / tools / images2waypoints.pl
CommitLineData
ae417ab2
RN
1#! /usr/bin/perl -w
2# Copyright (C) 2010 Rob Norris <rw_norris@hotmail.com>
a1362474
GB
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
ae417ab2
RN
19
20=head1 Overview
21
22A script to auto generate basic Viking .vik files for directories containing images.
23
24Simply 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
27For each directory this info is output to a file into either Viking (default) or GPX data file formats.
28Output filename is waypoints.vik (or waypoints.gpx in GPX mode) unless the -o option is specified.
29
30Options:
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
35Required programs:
36. exiftool - getting location data from EXIF (Debian package libimage-exiftool-perl)
37
38Recommended 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
43Various 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
61use strict;
62use File::Find;
63use Image::ExifTool qw(:Public);
64
65# Output modes
66use constant VIKING => 1;
67use constant GPX => 2;
68
69use constant RELATIVE => 1;
70use constant ABSOLUTE => 2;
71
72#################################
73# Some global variables
74# Create a new Image::ExifTool object
75my $exifTool = new Image::ExifTool;
76
77my @waypoint = ("","","","","","");
78my @position = ("0.0", "0.0"); # lat / long
79
80my $out_file;
81
82# Default mode
83my $mode = VIKING;
84my $imagefilemode = ABSOLUTE;
85
86#################################
87
88# Write header first part of .vik file
89sub Header_Viking {
90
91 return <<END;
92#VIKING GPS Data file http://viking.sf.net/
93
94~Layer Map
95name=Map
96mode=13
97directory=
98alpha=255
99autodownload=f
100mapzoom=0
101~EndLayer
102
103
104~Layer TrackWaypoint
105name=TrackWaypoint
106tracks_visible=f
107waypoints_visible=t
108drawmode=0
109drawlines=t
110drawpoints=t
111drawelevation=f
112elevation_factor=30
113drawstops=f
114stop_length=60
115line_thickness=1
116bg_line_thickness=0
117trackbgcolor=#ffffff
118velocity_min=0.000000
119velocity_max=5.000000
120drawlabels=t
121wpcolor=#000000
122wptextcolor=#ffffff
123wpbgcolor=#8383c4
124wpbgand=t
125wpsymbol=0
126wpsize=4
127wpsyms=t
128drawimages=t
129image_size=64
130image_alpha=255
131image_cache_size=300
132
133
134~LayerData
135type="waypointlist"
136END
137#
138#
139}
140
141# Write header first part of .gpx file
142sub Header_GPX {
143 return <<END;
144<?xml version="1.0" encoding="UTF-8"?>
145<gpx
146 version="1.1"
147creator="$0"
148xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
149xmlns="http://www.topografix.com/GPX/1/1"
150xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd">
151END
152# TODO consider metadata time & bounds info
153}
154
155sub Footer_GPX {
156 return "</gpx>\n";
157}
158
159sub 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
266sub Footer_Viking {
267 return <<END;
268type="waypointlistend"
269~EndLayerData
270~EndLayer
271
272xmpp=32.000000
273ympp=32.000000
274lat=$position[0]
275lon=$position[1]
276mode=mercator
277color=#cccccc
278drawscale=t
279drawcentermark=t
280
281END
282}
283
284#
285sub 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#
330sub 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
343my $out_file_start = "waypoints";
344
345if (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
363if ($mode == VIKING) {
364 $out_file = "$out_file_start".".vik";
365}
366else {
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
377find(\&Process_File, ".");
378
379#end