]> git.street.me.uk Git - andy/viking.git/blame - tools/images2waypoints.pl
Add a perl script to auto generate basic Viking .vik files for directories containing...
[andy/viking.git] / tools / images2waypoints.pl
CommitLineData
ae417ab2
RN
1#! /usr/bin/perl -w
2# Copyright (C) 2010 Rob Norris <rw_norris@hotmail.com>
3
4=head1 Overview
5
6A script to auto generate basic Viking .vik files for directories containing images.
7
8Simply 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
11For each directory this info is output to a file into either Viking (default) or GPX data file formats.
12Output filename is waypoints.vik (or waypoints.gpx in GPX mode) unless the -o option is specified.
13
14Options:
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
19Required programs:
20. exiftool - getting location data from EXIF (Debian package libimage-exiftool-perl)
21
22Recommended 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
27Various 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
45use strict;
46use File::Find;
47use Image::ExifTool qw(:Public);
48
49# Output modes
50use constant VIKING => 1;
51use constant GPX => 2;
52
53use constant RELATIVE => 1;
54use constant ABSOLUTE => 2;
55
56#################################
57# Some global variables
58# Create a new Image::ExifTool object
59my $exifTool = new Image::ExifTool;
60
61my @waypoint = ("","","","","","");
62my @position = ("0.0", "0.0"); # lat / long
63
64my $out_file;
65
66# Default mode
67my $mode = VIKING;
68my $imagefilemode = ABSOLUTE;
69
70#################################
71
72# Write header first part of .vik file
73sub Header_Viking {
74
75 return <<END;
76#VIKING GPS Data file http://viking.sf.net/
77
78~Layer Map
79name=Map
80mode=13
81directory=
82alpha=255
83autodownload=f
84mapzoom=0
85~EndLayer
86
87
88~Layer TrackWaypoint
89name=TrackWaypoint
90tracks_visible=f
91waypoints_visible=t
92drawmode=0
93drawlines=t
94drawpoints=t
95drawelevation=f
96elevation_factor=30
97drawstops=f
98stop_length=60
99line_thickness=1
100bg_line_thickness=0
101trackbgcolor=#ffffff
102velocity_min=0.000000
103velocity_max=5.000000
104drawlabels=t
105wpcolor=#000000
106wptextcolor=#ffffff
107wpbgcolor=#8383c4
108wpbgand=t
109wpsymbol=0
110wpsize=4
111wpsyms=t
112drawimages=t
113image_size=64
114image_alpha=255
115image_cache_size=300
116
117
118~LayerData
119type="waypointlist"
120END
121#
122#
123}
124
125# Write header first part of .gpx file
126sub Header_GPX {
127 return <<END;
128<?xml version="1.0" encoding="UTF-8"?>
129<gpx
130 version="1.1"
131creator="$0"
132xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
133xmlns="http://www.topografix.com/GPX/1/1"
134xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd">
135END
136# TODO consider metadata time & bounds info
137}
138
139sub Footer_GPX {
140 return "</gpx>\n";
141}
142
143sub 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
250sub Footer_Viking {
251 return <<END;
252type="waypointlistend"
253~EndLayerData
254~EndLayer
255
256xmpp=32.000000
257ympp=32.000000
258lat=$position[0]
259lon=$position[1]
260mode=mercator
261color=#cccccc
262drawscale=t
263drawcentermark=t
264
265END
266}
267
268#
269sub 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#
314sub 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
327my $out_file_start = "waypoints";
328
329if (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
347if ($mode == VIKING) {
348 $out_file = "$out_file_start".".vik";
349}
350else {
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
361find(\&Process_File, ".");
362
363#end