source: gs2-extensions/parallel-building/trunk/src/perllib/cpan/Image/ExifTool/GPS.pm@ 24626

Last change on this file since 24626 was 24626, checked in by jmt12, 13 years ago

An (almost) complete copy of the perllib directory from a (circa SEP2011) head checkout from Greenstone 2 trunk - in order to try and make merging in this extension a little easier later on (as there have been some major changes to buildcol.pl commited in the main trunk but not in the x64 branch)

File size: 14.6 KB
Line 
1#------------------------------------------------------------------------------
2# File: GPS.pm
3#
4# Description: EXIF GPS meta information tags
5#
6# Revisions: 12/09/2003 - P. Harvey Created
7#------------------------------------------------------------------------------
8
9package Image::ExifTool::GPS;
10
11use strict;
12use vars qw($VERSION);
13use Image::ExifTool::Exif;
14
15$VERSION = '1.34';
16
17my %coordConv = (
18 ValueConv => 'Image::ExifTool::GPS::ToDegrees($val)',
19 ValueConvInv => 'Image::ExifTool::GPS::ToDMS($self, $val)',
20 PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1)',
21 PrintConvInv => 'Image::ExifTool::GPS::ToDegrees($val)',
22);
23
24%Image::ExifTool::GPS::Main = (
25 GROUPS => { 0 => 'EXIF', 1 => 'GPS', 2 => 'Location' },
26 WRITE_PROC => \&Image::ExifTool::Exif::WriteExif,
27 CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
28 WRITABLE => 1,
29 WRITE_GROUP => 'GPS',
30 0x0000 => {
31 Name => 'GPSVersionID',
32 Writable => 'int8u',
33 Mandatory => 1,
34 Count => 4,
35 PrintConv => '$val =~ tr/ /./; $val',
36 PrintConvInv => '$val =~ tr/./ /; $val',
37 },
38 0x0001 => {
39 Name => 'GPSLatitudeRef',
40 Writable => 'string',
41 Count => 2,
42 PrintConv => {
43 # extract N/S if written from Composite:GPSLatitude
44 # (also allow writing from a signed number)
45 OTHER => sub {
46 my ($val, $inv) = @_;
47 return undef unless $inv;
48 return uc $1 if $val =~ /\b([NS])$/i;
49 return $1 eq '-' ? 'S' : 'N' if $val =~ /^([-+]?)\d+(\.\d*)?$/;
50 return undef;
51 },
52 N => 'North',
53 S => 'South',
54 },
55 },
56 0x0002 => {
57 Name => 'GPSLatitude',
58 Writable => 'rational64u',
59 Count => 3,
60 %coordConv,
61 },
62 0x0003 => {
63 Name => 'GPSLongitudeRef',
64 Writable => 'string',
65 Count => 2,
66 PrintConv => {
67 # extract E/W if written from Composite:GPSLongitude
68 # (also allow writing from a signed number)
69 OTHER => sub {
70 my ($val, $inv) = @_;
71 return undef unless $inv;
72 return uc $1 if $val =~ /\b([EW])$/i;
73 return $1 eq '-' ? 'W' : 'E' if $val =~ /^([-+]?)\d+(\.\d*)?$/;
74 return undef;
75 },
76 E => 'East',
77 W => 'West',
78 },
79 },
80 0x0004 => {
81 Name => 'GPSLongitude',
82 Writable => 'rational64u',
83 Count => 3,
84 %coordConv,
85 },
86 0x0005 => {
87 Name => 'GPSAltitudeRef',
88 Writable => 'int8u',
89 PrintConv => {
90 0 => 'Above Sea Level',
91 1 => 'Below Sea Level',
92 },
93 },
94 0x0006 => {
95 Name => 'GPSAltitude',
96 Writable => 'rational64u',
97 # extricate unsigned decimal number from string
98 ValueConvInv => '$val=~/((?=\d|\.\d)\d*(?:\.\d*)?)/ ? $1 : undef',
99 PrintConv => '$val =~ /^(inf|undef)$/ ? $val : "$val m"',
100 PrintConvInv => '$val=~s/\s*m$//;$val',
101 },
102 0x0007 => {
103 Name => 'GPSTimeStamp',
104 Groups => { 2 => 'Time' },
105 Writable => 'rational64u',
106 Count => 3,
107 Shift => 'Time',
108 Notes => q{
109 when writing, date is stripped off if present, and time is adjusted to UTC
110 if it includes a timezone
111 },
112 ValueConv => 'Image::ExifTool::GPS::ConvertTimeStamp($val)',
113 ValueConvInv => '$val=~tr/:/ /;$val',
114 # pull time out of any format date/time string
115 # (converting to UTC if a timezone is given)
116 PrintConvInv => sub {
117 my $v = shift;
118 my @tz;
119 if ($v =~ s/([-+])(.*)//s) { # remove timezone
120 my $s = $1 eq '-' ? 1 : -1; # opposite sign to convert back to UTC
121 my $t = $2;
122 @tz = ($s*$1, $s*$2) if $t =~ /^(\d{2}):?(\d{2})\s*$/;
123 }
124 my @a = ($v =~ /((?=\d|\.\d)\d*(?:\.\d*)?)/g);
125 push @a, '00' while @a < 3;
126 if (@tz) {
127 # adjust to UTC
128 $a[-2] += $tz[1];
129 $a[-3] += $tz[0];
130 while ($a[-2] >= 60) { $a[-2] -= 60; ++$a[-3] }
131 while ($a[-2] < 0) { $a[-2] += 60; --$a[-3] }
132 $a[-3] = ($a[-3] + 24) % 24;
133 }
134 return "$a[-3]:$a[-2]:$a[-1]";
135 },
136 },
137 0x0008 => {
138 Name => 'GPSSatellites',
139 Writable => 'string',
140 },
141 0x0009 => {
142 Name => 'GPSStatus',
143 Writable => 'string',
144 Count => 2,
145 PrintConv => {
146 A => 'Measurement Active', # Exif2.2 "Measurement in progress"
147 V => 'Measurement Void', # Exif2.2 "Measurement Interoperability" (WTF?)
148 # (meaning for 'V' taken from status code in NMEA GLL and RMC sentences)
149 },
150 },
151 0x000a => {
152 Name => 'GPSMeasureMode',
153 Writable => 'string',
154 Count => 2,
155 PrintConv => {
156 2 => '2-Dimensional Measurement',
157 3 => '3-Dimensional Measurement',
158 },
159 },
160 0x000b => {
161 Name => 'GPSDOP',
162 Description => 'GPS Dilution Of Precision',
163 Writable => 'rational64u',
164 },
165 0x000c => {
166 Name => 'GPSSpeedRef',
167 Writable => 'string',
168 Count => 2,
169 PrintConv => {
170 K => 'km/h',
171 M => 'mph',
172 N => 'knots',
173 },
174 },
175 0x000d => {
176 Name => 'GPSSpeed',
177 Writable => 'rational64u',
178 },
179 0x000e => {
180 Name => 'GPSTrackRef',
181 Writable => 'string',
182 Count => 2,
183 PrintConv => {
184 M => 'Magnetic North',
185 T => 'True North',
186 },
187 },
188 0x000f => {
189 Name => 'GPSTrack',
190 Writable => 'rational64u',
191 },
192 0x0010 => {
193 Name => 'GPSImgDirectionRef',
194 Writable => 'string',
195 Count => 2,
196 PrintConv => {
197 M => 'Magnetic North',
198 T => 'True North',
199 },
200 },
201 0x0011 => {
202 Name => 'GPSImgDirection',
203 Writable => 'rational64u',
204 },
205 0x0012 => {
206 Name => 'GPSMapDatum',
207 Writable => 'string',
208 },
209 0x0013 => {
210 Name => 'GPSDestLatitudeRef',
211 Writable => 'string',
212 Count => 2,
213 PrintConv => {
214 N => 'North',
215 S => 'South',
216 },
217 },
218 0x0014 => {
219 Name => 'GPSDestLatitude',
220 Writable => 'rational64u',
221 Count => 3,
222 %coordConv,
223 },
224 0x0015 => {
225 Name => 'GPSDestLongitudeRef',
226 Writable => 'string',
227 Count => 2,
228 PrintConv => {
229 E => 'East',
230 W => 'West',
231 },
232 },
233 0x0016 => {
234 Name => 'GPSDestLongitude',
235 Writable => 'rational64u',
236 Count => 3,
237 %coordConv,
238 },
239 0x0017 => {
240 Name => 'GPSDestBearingRef',
241 Writable => 'string',
242 Count => 2,
243 PrintConv => {
244 M => 'Magnetic North',
245 T => 'True North',
246 },
247 },
248 0x0018 => {
249 Name => 'GPSDestBearing',
250 Writable => 'rational64u',
251 },
252 0x0019 => {
253 Name => 'GPSDestDistanceRef',
254 Writable => 'string',
255 Count => 2,
256 PrintConv => {
257 K => 'Kilometers',
258 M => 'Miles',
259 N => 'Nautical Miles',
260 },
261 },
262 0x001a => {
263 Name => 'GPSDestDistance',
264 Writable => 'rational64u',
265 },
266 0x001b => {
267 Name => 'GPSProcessingMethod',
268 Writable => 'undef',
269 Notes => 'values of "GPS", "CELLID", "WLAN" or "MANUAL" by the EXIF spec.',
270 RawConv => 'Image::ExifTool::Exif::ConvertExifText($self,$val)',
271 RawConvInv => 'Image::ExifTool::Exif::EncodeExifText($self,$val)',
272 },
273 0x001c => {
274 Name => 'GPSAreaInformation',
275 Writable => 'undef',
276 RawConv => 'Image::ExifTool::Exif::ConvertExifText($self,$val)',
277 RawConvInv => 'Image::ExifTool::Exif::EncodeExifText($self,$val)',
278 },
279 0x001d => {
280 Name => 'GPSDateStamp',
281 Groups => { 2 => 'Time' },
282 Writable => 'string',
283 Notes => 'YYYY:mm:dd',
284 Count => 11,
285 Shift => 'Time',
286 Notes => q{
287 when writing, time is stripped off if present, after adjusting date/time to
288 UTC if time includes a timezone
289 },
290 ValueConv => 'Image::ExifTool::Exif::ExifDate($val)',
291 ValueConvInv => '$val',
292 # pull date out of any format date/time string
293 # (and adjust to UTC if this is a full date/time/timezone value)
294 PrintConvInv => q{
295 my $secs;
296 if ($val =~ /[-+]/ and ($secs = Image::ExifTool::GetUnixTime($val, 1))) {
297 $val = Image::ExifTool::ConvertUnixTime($secs);
298 }
299 return $val =~ /(\d{4}).*?(\d{2}).*?(\d{2})/ ? "$1:$2:$3" : undef;
300 },
301 },
302 0x001e => {
303 Name => 'GPSDifferential',
304 Writable => 'int16u',
305 PrintConv => {
306 0 => 'No Correction',
307 1 => 'Differential Corrected',
308 },
309 },
310 0x001f => {
311 Name => 'GPSHPositioningError',
312 Description => 'GPS Horizontal Positioning Error',
313 PrintConv => '"$val m"',
314 PrintConvInv => '$val=~s/\s*m$//; $val',
315 Writable => 'rational64u',
316 },
317);
318
319# Composite GPS tags
320%Image::ExifTool::GPS::Composite = (
321 GROUPS => { 2 => 'Location' },
322 GPSDateTime => {
323 Description => 'GPS Date/Time',
324 Groups => { 2 => 'Time' },
325 SubDoc => 1, # generate for all sub-documents
326 Require => {
327 0 => 'GPS:GPSDateStamp',
328 1 => 'GPS:GPSTimeStamp',
329 },
330 ValueConv => '"$val[0] $val[1]Z"',
331 PrintConv => '$self->ConvertDateTime($val)',
332 },
333 # Note: The following tags are used by other modules
334 # which must therefore require this module as necessary
335 GPSLatitude => {
336 SubDoc => 1, # generate for all sub-documents
337 Require => {
338 0 => 'GPS:GPSLatitude',
339 1 => 'GPS:GPSLatitudeRef',
340 },
341 ValueConv => '$val[1] =~ /^S/i ? -$val[0] : $val[0]',
342 PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "N")',
343 },
344 GPSLongitude => {
345 SubDoc => 1, # generate for all sub-documents
346 Require => {
347 0 => 'GPS:GPSLongitude',
348 1 => 'GPS:GPSLongitudeRef',
349 },
350 ValueConv => '$val[1] =~ /^W/i ? -$val[0] : $val[0]',
351 PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "E")',
352 },
353 GPSAltitude => {
354 SubDoc => 1, # generate for all sub-documents
355 Desire => {
356 0 => 'GPS:GPSAltitude',
357 1 => 'GPS:GPSAltitudeRef',
358 2 => 'XMP:GPSAltitude',
359 3 => 'XMP:GPSAltitudeRef',
360 },
361 # Require either GPS:GPSAltitudeRef or XMP:GPSAltitudeRef
362 RawConv => '(defined $val[1] or defined $val[3]) ? $val : undef',
363 ValueConv => q{
364 my $alt = $val[0];
365 $alt = $val[2] unless defined $alt;
366 return undef unless defined $alt;
367 return ($val[1] || $val[3]) ? -$alt : $alt;
368 },
369 PrintConv => q{
370 $val = int($val * 10) / 10;
371 return ($val =~ s/^-// ? "$val m Below" : "$val m Above") . " Sea Level";
372 },
373 },
374);
375
376# add our composite tags
377Image::ExifTool::AddCompositeTags('Image::ExifTool::GPS');
378
379#------------------------------------------------------------------------------
380# Convert GPS timestamp value
381# Inputs: 0) raw timestamp value string
382# Returns: EXIF-formatted time string
383sub ConvertTimeStamp($)
384{
385 my $val = shift;
386 my ($h,$m,$s) = split ' ', $val;
387 my $f = (($h || 0) * 60 + ($m || 0)) * 60 + ($s || 0);
388 $h = int($f / 3600); $f -= $h * 3600;
389 $m = int($f / 60); $f -= $m * 60;
390 $s = int($f); $f -= $s;
391 $f = int($f * 1000000 + 0.5);
392 if ($f) {
393 ($f = sprintf(".%.6d", $f)) =~ s/0+$//;
394 } else {
395 $f = ''
396 }
397 return sprintf("%.2d:%.2d:%.2d$f",$h,$m,$s);
398}
399
400#------------------------------------------------------------------------------
401# Convert degrees to DMS, or whatever the current settings are
402# Inputs: 0) ExifTool reference, 1) Value in degrees,
403# 2) format code (0=no format, 1=CoordFormat, 2=XMP format)
404# 3) 'N' or 'E' if sign is significant and N/S/E/W should be added
405# Returns: DMS string
406sub ToDMS($$;$$)
407{
408 my ($exifTool, $val, $doPrintConv, $ref) = @_;
409 my ($fmt, $num);
410
411 if ($ref) {
412 if ($val < 0) {
413 $val = -$val;
414 $ref = {N => 'S', E => 'W'}->{$ref};
415 }
416 $ref = " $ref" unless $doPrintConv and $doPrintConv eq '2';
417 } else {
418 $val = abs($val);
419 $ref = '';
420 }
421 if ($doPrintConv) {
422 if ($doPrintConv eq '1') {
423 $fmt = ($exifTool->Options('CoordFormat') || q{%d deg %d' %.2f"}) . $ref;
424 } else {
425 $fmt = "%d,%.6f$ref"; # use XMP standard format
426 }
427 # count the number of format specifiers
428 $num = ($fmt =~ tr/%/%/);
429 } else {
430 $num = 3;
431 }
432 my ($d, $m, $s);
433 $d = $val;
434 if ($num > 1) {
435 $d = int($d);
436 $m = ($val - $d) * 60;
437 if ($num > 2) {
438 $m = int($m);
439 $s = ($val - $d - $m / 60) * 3600;
440 }
441 }
442 return $doPrintConv ? sprintf($fmt, $d, $m, $s) : "$d $m $s$ref";
443}
444
445#------------------------------------------------------------------------------
446# Convert to decimal degrees
447# Inputs: 0) a string containing 1-3 decimal numbers and any amount of other garbage
448# 1) true if value should be negative if coordinate ends in 'S' or 'W'
449# Returns: Coordinate in degrees
450sub ToDegrees($;$)
451{
452 my ($val, $doSign) = @_;
453 # extract decimal or floating point values out of any other garbage
454 my ($d, $m, $s) = ($val =~ /((?:[+-]?)(?=\d|\.\d)\d*(?:\.\d*)?(?:[Ee][+-]\d+)?)/g);
455 my $deg = ($d || 0) + (($m || 0) + ($s || 0)/60) / 60;
456 # make negative if S or W coordinate
457 $deg = -$deg if $doSign ? $val =~ /[^A-Z](S|W)$/i : $deg < 0;
458 return $deg;
459}
460
461
4621; #end
463
464__END__
465
466=head1 NAME
467
468Image::ExifTool::GPS - EXIF GPS meta information tags
469
470=head1 SYNOPSIS
471
472This module is loaded automatically by Image::ExifTool when required.
473
474=head1 DESCRIPTION
475
476This module contains definitions required by Image::ExifTool to interpret
477GPS (Global Positioning System) meta information in EXIF data.
478
479=head1 AUTHOR
480
481Copyright 2003-2011, Phil Harvey (phil at owl.phy.queensu.ca)
482
483This library is free software; you can redistribute it and/or modify it
484under the same terms as Perl itself.
485
486=head1 REFERENCES
487
488=over 4
489
490=item L<Image::Info|Image::Info>
491
492=back
493
494=head1 SEE ALSO
495
496L<Image::ExifTool::TagNames/GPS Tags>,
497L<Image::ExifTool(3pm)|Image::ExifTool>,
498L<Image::Info(3pm)|Image::Info>
499
500=cut
Note: See TracBrowser for help on using the repository browser.