exiftool-vendored.exe 12.82.1 → 12.84.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/exiftool_files/Changes +37 -0
- package/bin/exiftool_files/README +3 -2
- package/bin/exiftool_files/build_geolocation +872 -0
- package/bin/exiftool_files/config_files/example.config +2 -2
- package/bin/exiftool_files/exiftool.pl +28 -6
- package/bin/exiftool_files/fmt_files/gpx.fmt +2 -1
- package/bin/exiftool_files/fmt_files/gpx_wpt.fmt +2 -1
- package/bin/exiftool_files/lib/Image/ExifTool/Apple.pm +51 -7
- package/bin/exiftool_files/lib/Image/ExifTool/BuildTagLookup.pm +5 -2
- package/bin/exiftool_files/lib/Image/ExifTool/CanonVRD.pm +18 -5
- package/bin/exiftool_files/lib/Image/ExifTool/DJI.pm +29 -0
- package/bin/exiftool_files/lib/Image/ExifTool/Exif.pm +19 -2
- package/bin/exiftool_files/lib/Image/ExifTool/GM.pm +17 -8
- package/bin/exiftool_files/lib/Image/ExifTool/Geolocation.dat +0 -0
- package/bin/exiftool_files/lib/Image/ExifTool/Geolocation.pm +163 -101
- package/bin/exiftool_files/lib/Image/ExifTool/Geotag.pm +18 -10
- package/bin/exiftool_files/lib/Image/ExifTool/Nikon.pm +7 -6
- package/bin/exiftool_files/lib/Image/ExifTool/QuickTime.pm +6 -1
- package/bin/exiftool_files/lib/Image/ExifTool/QuickTimeStream.pl +5 -0
- package/bin/exiftool_files/lib/Image/ExifTool/Sony.pm +15 -6
- package/bin/exiftool_files/lib/Image/ExifTool/TagLookup.pm +18 -9
- package/bin/exiftool_files/lib/Image/ExifTool/TagNames.pod +24 -5
- package/bin/exiftool_files/lib/Image/ExifTool/WriteQuickTime.pl +2 -1
- package/bin/exiftool_files/lib/Image/ExifTool/Writer.pl +165 -132
- package/bin/exiftool_files/lib/Image/ExifTool/XMP2.pl +3 -0
- package/bin/exiftool_files/lib/Image/ExifTool.pm +45 -23
- package/bin/exiftool_files/lib/Image/ExifTool.pod +23 -14
- package/package.json +2 -2
- package/bin/exiftool_files/lib/Image/ExifTool/GeoLang/cs.pm +0 -978
- package/bin/exiftool_files/lib/Image/ExifTool/GeoLang/de.pm +0 -1975
- package/bin/exiftool_files/lib/Image/ExifTool/GeoLang/en_ca.pm +0 -44
- package/bin/exiftool_files/lib/Image/ExifTool/GeoLang/en_gb.pm +0 -124
- package/bin/exiftool_files/lib/Image/ExifTool/GeoLang/es.pm +0 -2921
- package/bin/exiftool_files/lib/Image/ExifTool/GeoLang/fi.pm +0 -1116
- package/bin/exiftool_files/lib/Image/ExifTool/GeoLang/fr.pm +0 -3171
- package/bin/exiftool_files/lib/Image/ExifTool/GeoLang/it.pm +0 -2750
- package/bin/exiftool_files/lib/Image/ExifTool/GeoLang/ja.pm +0 -10256
- package/bin/exiftool_files/lib/Image/ExifTool/GeoLang/ko.pm +0 -4499
- package/bin/exiftool_files/lib/Image/ExifTool/GeoLang/nl.pm +0 -1270
- package/bin/exiftool_files/lib/Image/ExifTool/GeoLang/pl.pm +0 -3019
- package/bin/exiftool_files/lib/Image/ExifTool/GeoLang/ru.pm +0 -18220
- package/bin/exiftool_files/lib/Image/ExifTool/GeoLang/sk.pm +0 -441
- package/bin/exiftool_files/lib/Image/ExifTool/GeoLang/sv.pm +0 -714
- package/bin/exiftool_files/lib/Image/ExifTool/GeoLang/tr.pm +0 -452
- package/bin/exiftool_files/lib/Image/ExifTool/GeoLang/zh_cn.pm +0 -2225
- package/bin/exiftool_files/lib/Image/ExifTool/GeoLang/zh_tw.pm +0 -72
|
@@ -9,14 +9,15 @@
|
|
|
9
9
|
#
|
|
10
10
|
# References: https://download.geonames.org/export/
|
|
11
11
|
#
|
|
12
|
-
# Notes: Set $Image::ExifTool::Geolocation::geoDir to override
|
|
13
|
-
# default directory
|
|
14
|
-
# and
|
|
12
|
+
# Notes: Set $Image::ExifTool::Geolocation::geoDir to override the
|
|
13
|
+
# default directory containing the database file Geolocation.dat
|
|
14
|
+
# and the GeoLang directory with the alternate language files.
|
|
15
|
+
# If set, this directory is
|
|
15
16
|
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
# entry is a newline-separated list of alternate names
|
|
17
|
+
# AltNames.dat may be loaded from a different directory by
|
|
18
|
+
# specifying $Image::ExifTool::Geolocation::altDir. This
|
|
19
|
+
# database and has entries in the same order as Geolocation.dat,
|
|
20
|
+
# and each entry is a newline-separated list of alternate names
|
|
20
21
|
# terminated by a null byte.
|
|
21
22
|
#
|
|
22
23
|
# Databases are based on data from geonames.org with a
|
|
@@ -37,7 +38,7 @@
|
|
|
37
38
|
# 9 int16u - v1.02: 0x7fff = index in subregion (admin2), 0x8000 = high bit of time zone
|
|
38
39
|
# 9 int16u - v1.03: index in subregion (admin2)
|
|
39
40
|
# 11 int8u - low byte of time zone index
|
|
40
|
-
# 12 int8u -
|
|
41
|
+
# 12 int8u - 0x3f = feature code index (see below), v1.03: 0x80 = high bit of time zone
|
|
41
42
|
# 13 string - UTF8 City name, terminated by newline
|
|
42
43
|
# "\0\0\0\0\x01"
|
|
43
44
|
# Country entries:
|
|
@@ -52,9 +53,12 @@
|
|
|
52
53
|
# "\0\0\0\0\x04"
|
|
53
54
|
# Time zone entries:
|
|
54
55
|
# 1. Time zone name, terminated by newline
|
|
56
|
+
# "\0\0\0\0\x05" (feature codes added in v1.03)
|
|
57
|
+
# Feature codes:
|
|
58
|
+
# 1. Feature code, terminated by newline
|
|
55
59
|
# "\0\0\0\0\0"
|
|
56
60
|
#
|
|
57
|
-
# Feature Codes: (see http://www.geonames.org/export/codes.html#P for descriptions)
|
|
61
|
+
# Feature Codes v1.02: (see http://www.geonames.org/export/codes.html#P for descriptions)
|
|
58
62
|
#
|
|
59
63
|
# 0. Other 3. PPLA2 6. PPLA5 9. PPLF 12. PPLR 15. PPLX
|
|
60
64
|
# 1. PPL 4. PPLA3 7. PPLC 10. PPLG 13. PPLS
|
|
@@ -66,7 +70,7 @@ package Image::ExifTool::Geolocation;
|
|
|
66
70
|
use strict;
|
|
67
71
|
use vars qw($VERSION $geoDir $altDir $dbInfo);
|
|
68
72
|
|
|
69
|
-
$VERSION = '1.
|
|
73
|
+
$VERSION = '1.07'; # (this is the module version number, not the database version)
|
|
70
74
|
|
|
71
75
|
my $debug; # set to output processing time for testing
|
|
72
76
|
|
|
@@ -74,21 +78,19 @@ sub ReadDatabase($);
|
|
|
74
78
|
sub SortDatabase($);
|
|
75
79
|
sub AddEntry(@);
|
|
76
80
|
sub GetEntry($;$$);
|
|
77
|
-
sub Geolocate(
|
|
81
|
+
sub Geolocate($;$);
|
|
78
82
|
|
|
79
83
|
my (@cityList, @countryList, @regionList, @subregionList, @timezoneList);
|
|
80
84
|
my (%countryNum, %regionNum, %subregionNum, %timezoneNum); # reverse lookups
|
|
81
|
-
my (@sortOrder, @altNames, %langLookup, $nCity);
|
|
85
|
+
my (@sortOrder, @altNames, %langLookup, $nCity, %featureCodes);
|
|
82
86
|
my ($lastArgs, %lastFound, @lastByPop, @lastByLat); # cached city matches
|
|
83
87
|
my $dbVer = '1.03';
|
|
84
88
|
my $sortedBy = 'Latitude';
|
|
85
89
|
my $pi = 3.1415926536;
|
|
86
90
|
my $earthRadius = 6371; # earth radius in km
|
|
87
|
-
|
|
91
|
+
# hard-coded feature codes for v1.02 database
|
|
88
92
|
my @featureCodes = qw(Other PPL PPLA PPLA2 PPLA3 PPLA4 PPLA5 PPLC
|
|
89
93
|
PPLCH PPLF PPLG PPLL PPLR PPLS STLMT PPLX);
|
|
90
|
-
my $i = 0;
|
|
91
|
-
my %featureCodes = map { lc($_) => $i++ } @featureCodes;
|
|
92
94
|
|
|
93
95
|
# get path name for database file from lib/Image/ExifTool/Geolocation.dat by default,
|
|
94
96
|
# or according to $Image::ExifTool::Geolocation::directory if specified
|
|
@@ -107,12 +109,10 @@ unless (defined $geoDir and not $geoDir) {
|
|
|
107
109
|
}
|
|
108
110
|
}
|
|
109
111
|
|
|
110
|
-
# set directory for language files
|
|
111
|
-
|
|
112
|
-
if ($geoDir and -
|
|
113
|
-
$
|
|
114
|
-
} elsif ($geoDir or not defined $geoDir) {
|
|
115
|
-
$geoLang = "$defaultDir/GeoLang";
|
|
112
|
+
# set directory for language files and alternate names
|
|
113
|
+
$geoDir = $defaultDir unless defined $geoDir;
|
|
114
|
+
if (not defined $altDir and $geoDir and -e "$geoDir/AltNames.dat") {
|
|
115
|
+
$altDir = $geoDir;
|
|
116
116
|
}
|
|
117
117
|
|
|
118
118
|
# add user-defined entries to the database
|
|
@@ -144,7 +144,7 @@ sub ReadDatabase($)
|
|
|
144
144
|
return 0;
|
|
145
145
|
}
|
|
146
146
|
my $comment = <DATFILE>;
|
|
147
|
-
defined $comment and $comment =~ /(\d+)/ or close(DATFILE), return 0;
|
|
147
|
+
defined $comment and $comment =~ / (\d+) / or close(DATFILE), return 0;
|
|
148
148
|
$dbInfo = "$datfile v$dbVer: $nCity cities with population > $1";
|
|
149
149
|
my $isUserDefined = @Image::ExifTool::UserDefined::Geolocation;
|
|
150
150
|
|
|
@@ -193,7 +193,21 @@ sub ReadDatabase($)
|
|
|
193
193
|
push @timezoneList, $line;
|
|
194
194
|
$timezoneNum{lc $line} = $#timezoneList if $isUserDefined;
|
|
195
195
|
}
|
|
196
|
+
# read feature codes if available
|
|
197
|
+
if ($line eq "\0\0\0\0\x05\n") {
|
|
198
|
+
undef @featureCodes;
|
|
199
|
+
for (;;) {
|
|
200
|
+
$line = <DATFILE>;
|
|
201
|
+
last if length($line) == 6 and $line =~ /\0\0\0\0/;
|
|
202
|
+
chomp $line;
|
|
203
|
+
$line =~ s/ .*//; # (hook to be able to add feature descriptions later)
|
|
204
|
+
push @featureCodes, $line;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
196
207
|
close DATFILE;
|
|
208
|
+
# initialize featureCodes lookup
|
|
209
|
+
$i = 0;
|
|
210
|
+
%featureCodes = map { lc($_) => $i++ } @featureCodes;
|
|
197
211
|
return 1;
|
|
198
212
|
}
|
|
199
213
|
|
|
@@ -229,10 +243,10 @@ sub ReadAltNames()
|
|
|
229
243
|
# Clear last city matches cache
|
|
230
244
|
sub ClearLastMatches()
|
|
231
245
|
{
|
|
232
|
-
undef $lastArgs;
|
|
233
|
-
undef %lastFound;
|
|
234
|
-
undef @lastByPop;
|
|
235
|
-
undef @lastByLat;
|
|
246
|
+
undef $lastArgs; # arguments in last call to Geolocate
|
|
247
|
+
undef %lastFound; # keys are last matching city numbers, values are population codes
|
|
248
|
+
undef @lastByPop; # last matching city numbers ordered by population
|
|
249
|
+
undef @lastByLat; # last matching city numbers ordered by latitude
|
|
236
250
|
}
|
|
237
251
|
|
|
238
252
|
#------------------------------------------------------------------------------
|
|
@@ -274,7 +288,16 @@ sub AddEntry(@)
|
|
|
274
288
|
my ($city, $region, $subregion, $cc, $country, $timezone, $fc, $pop, $lat, $lon, $altNames) = @_;
|
|
275
289
|
@_ < 10 and warn("Too few arguments in $city definition (check for updated format)\n"), return 0;
|
|
276
290
|
length($cc) != 2 and warn("Country code '${cc}' is not 2 characters\n"), return 0;
|
|
277
|
-
$fc
|
|
291
|
+
$fc =~ s/ .*//; # (allow for descriptions to be added after a space -- future feature?)
|
|
292
|
+
my $fn = $featureCodes{lc $fc};
|
|
293
|
+
unless (defined $fn) {
|
|
294
|
+
if ($dbVer eq '1.02' or @featureCodes > 0x3f or not length $fc) {
|
|
295
|
+
$fn = 0;
|
|
296
|
+
} else {
|
|
297
|
+
push @featureCodes, uc($fc);
|
|
298
|
+
$featureCodes{lc $fc} = $fn = $#featureCodes;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
278
301
|
chomp $lon; # (just in case it was read from file)
|
|
279
302
|
# create reverse lookups for country/region/subregion/timezone if not done already
|
|
280
303
|
# (eg. if the entries are being added manually instead of via UserDefined::Geolocation)
|
|
@@ -307,7 +330,7 @@ sub AddEntry(@)
|
|
|
307
330
|
}
|
|
308
331
|
my $sn = $subregionNum{lc $subregion};
|
|
309
332
|
unless (defined $sn) {
|
|
310
|
-
my $max = $dbVer eq '1.02' ?
|
|
333
|
+
my $max = $dbVer eq '1.02' ? 0x7fff : 0xffff;
|
|
311
334
|
$#subregionList >= $max and warn("AddEntry: Too many subregions\n"), return 0;
|
|
312
335
|
push @subregionList, $subregion;
|
|
313
336
|
$sn = $subregionNum{lc $subregion} = $#subregionList;
|
|
@@ -320,13 +343,13 @@ sub AddEntry(@)
|
|
|
320
343
|
if ($dbVer eq '1.02') {
|
|
321
344
|
$sn |= 0x8000;
|
|
322
345
|
} else {
|
|
323
|
-
$
|
|
346
|
+
$fn |= 0x80;
|
|
324
347
|
}
|
|
325
348
|
$tn -= 256;
|
|
326
349
|
}
|
|
327
350
|
$lat = int(($lat + 90) / 180 * 0x100000 + 0.5) & 0xfffff;
|
|
328
351
|
$lon = int(($lon + 180) / 360 * 0x100000 + 0.5) & 0xfffff;
|
|
329
|
-
my $hdr = pack('nCnNnCC', $lat>>4, (($lat&0x0f)<<4)|($lon&0x0f), $lon>>4, $code, $sn, $tn, $
|
|
352
|
+
my $hdr = pack('nCnNnCC', $lat>>4, (($lat&0x0f)<<4)|($lon&0x0f), $lon>>4, $code, $sn, $tn, $fn);
|
|
330
353
|
push @cityList, "$hdr$city";
|
|
331
354
|
# add altNames entry if provided
|
|
332
355
|
if ($altNames) {
|
|
@@ -355,28 +378,28 @@ sub GetEntry($;$$)
|
|
|
355
378
|
my ($entryNum, $lang, $sort) = @_;
|
|
356
379
|
return() if $entryNum > $#cityList;
|
|
357
380
|
$entryNum = $sortOrder[$entryNum] if $sort and @sortOrder > $entryNum;
|
|
358
|
-
my ($lt,$f,$ln,$code,$sn,$tn,$
|
|
381
|
+
my ($lt,$f,$ln,$code,$sn,$tn,$fn) = unpack('nCnNnCC', $cityList[$entryNum]);
|
|
359
382
|
my $city = substr($cityList[$entryNum],13);
|
|
360
383
|
my $ctry = $countryList[$code >> 24];
|
|
361
384
|
my $rgn = $regionList[$code & 0x0fff];
|
|
362
385
|
if ($dbVer eq '1.02') {
|
|
363
386
|
$sn & 0x8000 and $tn += 256, $sn &= 0x7fff;
|
|
364
387
|
} else {
|
|
365
|
-
$
|
|
388
|
+
$fn & 0x80 and $tn += 256;
|
|
366
389
|
}
|
|
367
390
|
my $sub = $subregionList[$sn];
|
|
368
391
|
# convert population digits back into exponent format
|
|
369
392
|
my $pop = (($code>>16 & 0x0f) . '.' . ($code>>12 & 0x0f) . 'e+' . ($code>>20 & 0x0f)) + 0;
|
|
370
393
|
$lt = sprintf('%.4f', (($lt<<4)|($f >> 4)) * 180 / 0x100000 - 90);
|
|
371
394
|
$ln = sprintf('%.4f', (($ln<<4)|($f & 0x0f))* 360 / 0x100000 - 180);
|
|
372
|
-
$fc = $featureCodes[$
|
|
395
|
+
my $fc = $featureCodes[$fn & 0x3f] || 'Other';
|
|
373
396
|
my $cc = substr($ctry, 0, 2);
|
|
374
397
|
my $country = substr($ctry, 2);
|
|
375
398
|
if ($lang) {
|
|
376
399
|
my $xlat = $langLookup{$lang};
|
|
377
400
|
# load language lookups if not done already
|
|
378
401
|
if (not defined $xlat) {
|
|
379
|
-
if (eval "require '$
|
|
402
|
+
if (eval "require '$geoDir/GeoLang/$lang.pm'") {
|
|
380
403
|
my $trans = "Image::ExifTool::GeoLang::${lang}::Translate";
|
|
381
404
|
no strict 'refs';
|
|
382
405
|
$xlat = \%$trans if %$trans;
|
|
@@ -429,17 +452,16 @@ sub GetAltNames($;$)
|
|
|
429
452
|
# Look up lat,lon or city in geolocation database
|
|
430
453
|
# Inputs: 0) "lat,lon", "city,region,country", etc, (city must be first)
|
|
431
454
|
# 1) options hash reference (or undef for no options)
|
|
432
|
-
# Options: GeolocMinPop, GeolocMaxDist, GeolocMulti, GeolocFeature, GeolocAltNames
|
|
433
|
-
#
|
|
434
|
-
#
|
|
435
|
-
#
|
|
436
|
-
#
|
|
437
|
-
|
|
438
|
-
sub Geolocate($;$$$$$)
|
|
455
|
+
# Options: GeolocMinPop, GeolocMaxDist, GeolocMulti, GeolocFeature, GeolocAltNames,
|
|
456
|
+
# GeolocNearby
|
|
457
|
+
# Returns: List of matching city information, empty if none found.
|
|
458
|
+
# Each element in the list is an array with 0=index of city in database,
|
|
459
|
+
# 1=distance in km (or undef if no distance), 2=compass bearing (or undef)
|
|
460
|
+
sub Geolocate($;$)
|
|
439
461
|
{
|
|
440
462
|
my ($arg, $opts) = @_;
|
|
441
463
|
my ($city, @exact, %regex, @multiCity, $other, $idx, @cargs, $useLastFound);
|
|
442
|
-
my ($minPop, $minDistU, $minDistC, @matchParms, @coords,
|
|
464
|
+
my ($minPop, $minDistU, $minDistC, @matchParms, @coords, %fcOK, $both);
|
|
443
465
|
my ($pop, $maxDist, $multi, $fcodes, $altNames, @startTime);
|
|
444
466
|
|
|
445
467
|
$opts and ($pop, $maxDist, $multi, $fcodes, $altNames) =
|
|
@@ -449,7 +471,7 @@ sub Geolocate($;$$$$$)
|
|
|
449
471
|
require Time::HiRes;
|
|
450
472
|
@startTime = Time::HiRes::gettimeofday();
|
|
451
473
|
}
|
|
452
|
-
@cityList or warn('No Geolocation database'), return
|
|
474
|
+
@cityList or warn('No Geolocation database'), return();
|
|
453
475
|
# make population code for comparing with 2 bytes at offset 6 in database
|
|
454
476
|
if ($pop) {
|
|
455
477
|
$pop = sprintf('%.1e', $pop);
|
|
@@ -457,17 +479,18 @@ sub Geolocate($;$$$$$)
|
|
|
457
479
|
}
|
|
458
480
|
if ($fcodes) {
|
|
459
481
|
my $neg = $fcodes =~ s/^-//;
|
|
460
|
-
my @fcodes = split /\s
|
|
482
|
+
my @fcodes = split /\s*,-?\s*/, lc $fcodes; # (allow leading dash on subsequent codes)
|
|
461
483
|
if ($neg) {
|
|
462
|
-
$
|
|
463
|
-
defined $featureCodes{
|
|
484
|
+
$fcOK{$_} = 1 foreach 0..$#featureCodes;
|
|
485
|
+
defined $featureCodes{$_} and delete $fcOK{$featureCodes{$_}} foreach @fcodes;
|
|
464
486
|
} else {
|
|
465
|
-
defined $featureCodes{
|
|
487
|
+
defined $featureCodes{$_} and $fcOK{$featureCodes{$_}} = 1 foreach @fcodes;
|
|
466
488
|
}
|
|
467
489
|
}
|
|
468
490
|
#
|
|
469
491
|
# process input argument
|
|
470
492
|
#
|
|
493
|
+
my $num = 1;
|
|
471
494
|
$arg =~ s/^\s+//; $arg =~ s/\s+$//; # remove leading/trailing spaces
|
|
472
495
|
my @args = split /\s*,\s*/, $arg;
|
|
473
496
|
my %ri = ( cc => 0, co => 1, re => 2, sr => 3, ci => 8, '' => 9 );
|
|
@@ -486,6 +509,8 @@ sub Geolocate($;$$$$$)
|
|
|
486
509
|
push @coords, $_ if @coords < 2;
|
|
487
510
|
} elsif (lc $_ eq 'both') {
|
|
488
511
|
$both = 1;
|
|
512
|
+
} elsif ($_ =~ /^num=(\d+)$/i) {
|
|
513
|
+
$num = $1;
|
|
489
514
|
} elsif ($_) {
|
|
490
515
|
push @cargs, $_;
|
|
491
516
|
if ($city) {
|
|
@@ -497,7 +522,7 @@ sub Geolocate($;$$$$$)
|
|
|
497
522
|
}
|
|
498
523
|
unless (defined $city or @coords == 2) {
|
|
499
524
|
warn("Insufficient information to determine geolocation\n");
|
|
500
|
-
return
|
|
525
|
+
return();
|
|
501
526
|
}
|
|
502
527
|
# sort database by logitude if finding entry based on coordinates
|
|
503
528
|
SortDatabase('Latitude') if @coords == 2 and ($both or not defined $city);
|
|
@@ -551,7 +576,7 @@ Entry: for (; $i<@cityList; ++$i) {
|
|
|
551
576
|
$str !~ $_ or next Entry foreach @{$regex{19}};
|
|
552
577
|
}
|
|
553
578
|
# test feature code and population
|
|
554
|
-
next if $
|
|
579
|
+
next if $fcodes and not $fcOK{ord(substr($cityList[$i],12,1)) & 0x3f};
|
|
555
580
|
my $pc = substr($cityList[$i],6,2);
|
|
556
581
|
if (not defined $minPop or $pc ge $minPop) {
|
|
557
582
|
$lastFound{$i} = $pc;
|
|
@@ -561,16 +586,14 @@ Entry: for (; $i<@cityList; ++$i) {
|
|
|
561
586
|
@startTime and printf("= Processing time: %.3f sec\n", Time::HiRes::tv_interval(\@startTime));
|
|
562
587
|
if (%lastFound) {
|
|
563
588
|
@coords == 2 and $useLastFound = 1, last; # continue to use coords with last city matches
|
|
564
|
-
scalar(keys %lastFound) > 200 and warn("Too many matching cities\n"), return
|
|
589
|
+
scalar(keys %lastFound) > 200 and warn("Too many matching cities\n"), return();
|
|
565
590
|
unless (@lastByPop) {
|
|
566
591
|
@lastByPop = sort { $lastFound{$b} cmp $lastFound{$a} or $cityList[$a] cmp $cityList[$b] } keys %lastFound;
|
|
567
592
|
}
|
|
568
|
-
|
|
569
|
-
return($n, [ @lastByPop ]) if $n > 1 and $multi;
|
|
570
|
-
return($n, $lastByPop[0]);
|
|
593
|
+
return(\@lastByPop);
|
|
571
594
|
}
|
|
572
595
|
warn "No such city in Geolocation database\n";
|
|
573
|
-
return
|
|
596
|
+
return();
|
|
574
597
|
}
|
|
575
598
|
#
|
|
576
599
|
# determine Geolocation based on GPS coordinates
|
|
@@ -606,9 +629,11 @@ Entry: for (; $i<@cityList; ++$i) {
|
|
|
606
629
|
my ($inc, $end, $n) = (-1, -1, $n0+1);
|
|
607
630
|
my ($p0, $t0) = ($lat*$pi/0x100000 - $pi/2, $lon*$pi/0x080000 - $pi);
|
|
608
631
|
my $cp0 = cos($p0);
|
|
632
|
+
my (@matches, @rtnList, @dist);
|
|
633
|
+
|
|
609
634
|
for (;;) {
|
|
610
635
|
if (($n += $inc) == $end) {
|
|
611
|
-
last if $inc == 1;
|
|
636
|
+
last if $inc == 1 or $n0 == $n1;
|
|
612
637
|
($inc, $end, $n) = (1, $numEntries, $n1);
|
|
613
638
|
}
|
|
614
639
|
my $i = $sorted ? $$sorted[$n] : $n;
|
|
@@ -619,28 +644,56 @@ Entry: for (; $i<@cityList; ++$i) {
|
|
|
619
644
|
abs($lt - $lat) > $minDistC and $n = $end - $inc, next;
|
|
620
645
|
# ignore if population is below threshold
|
|
621
646
|
next if defined $minPop and $minPop ge substr($cityList[$i],6,2);
|
|
622
|
-
next if $
|
|
647
|
+
next if $fcodes and not $fcOK{ord(substr($cityList[$i],12,1)) & 0x3f};
|
|
623
648
|
$ln = ($ln << 4) | ($f & 0x0f);
|
|
624
649
|
# calculate great circle distance to this city on unit sphere
|
|
625
650
|
my ($p1, $t1) = ($lt*$pi/0x100000 - $pi/2, $ln*$pi/0x080000 - $pi);
|
|
626
651
|
my ($sp, $st) = (sin(($p1-$p0)/2), sin(($t1-$t0)/2));
|
|
627
652
|
my $a = $sp * $sp + $cp0 * cos($p1) * $st * $st;
|
|
628
|
-
my $distU = atan2(sqrt($a), sqrt(1-$a));
|
|
653
|
+
my $distU = atan2(sqrt($a), sqrt(1-$a)); # distance on unit sphere
|
|
629
654
|
next if $distU > $minDistU;
|
|
630
|
-
$minDistU = $distU;
|
|
631
|
-
$minDistC = $minDistU * 0x200000 / $pi;
|
|
632
655
|
@matchParms = ($i, $p1, $t1, $distU);
|
|
656
|
+
if ($num <= 1) {
|
|
657
|
+
$minDistU = $distU;
|
|
658
|
+
} else {
|
|
659
|
+
my $j;
|
|
660
|
+
# add this entry into list of matching cities ordered by closest first
|
|
661
|
+
for ($j=0; $j<@matches; ++$j) {
|
|
662
|
+
last if $distU < $matches[$j][3];
|
|
663
|
+
}
|
|
664
|
+
if ($j < $#matches) {
|
|
665
|
+
splice @matches, $j, 0, [ @matchParms ];
|
|
666
|
+
} else {
|
|
667
|
+
$matches[$j] = [ @matchParms ];
|
|
668
|
+
}
|
|
669
|
+
# restrict list to the specified number of nearest cities
|
|
670
|
+
pop @matches if @matches > $num;
|
|
671
|
+
# update minimum distance with furthest match if we satisfied our quota
|
|
672
|
+
$minDistU = $matches[-1][3] if @matches >= $num;
|
|
673
|
+
}
|
|
674
|
+
$minDistC = $minDistU * 0x200000 / $pi; # distance in scaled coordinate units
|
|
633
675
|
}
|
|
634
|
-
@matchParms or warn("No suitable location in Geolocation database\n"), return
|
|
635
|
-
|
|
636
|
-
# calculate distance in km and bearing to matching city
|
|
637
|
-
my ($ii, $p1, $t1, $distU) = @matchParms;
|
|
638
|
-
my $km = sprintf('%.2f', 2 * $earthRadius * $distU);
|
|
639
|
-
my $be = atan2(sin($t1-$t0)*cos($p1-$p0), $cp0*sin($p1)-sin($p0)*cos($p1)*cos($t1-$t0));
|
|
640
|
-
$be = int($be * 180 / $pi + 360.5) % 360; # convert from radians to integer degrees
|
|
676
|
+
@matchParms or warn("No suitable location in Geolocation database\n"), return();
|
|
677
|
+
$num = @matches;
|
|
641
678
|
|
|
642
679
|
@startTime and printf("- Processing time: %.3f sec\n", Time::HiRes::tv_interval(\@startTime));
|
|
643
|
-
|
|
680
|
+
|
|
681
|
+
for (;;) {
|
|
682
|
+
if ($num > 1) {
|
|
683
|
+
last unless @matches;
|
|
684
|
+
@matchParms = @{$matches[0]};
|
|
685
|
+
shift @matches;
|
|
686
|
+
}
|
|
687
|
+
# calculate distance in km and bearing to matching city
|
|
688
|
+
my ($ii, $p1, $t1, $distU) = @matchParms;
|
|
689
|
+
my $km = sprintf('%.2f', 2 * $earthRadius * $distU);
|
|
690
|
+
my $be = atan2(sin($t1-$t0)*cos($p1-$p0), $cp0*sin($p1)-sin($p0)*cos($p1)*cos($t1-$t0));
|
|
691
|
+
$be = int($be * 180 / $pi + 360.5) % 360; # convert from radians to integer degrees
|
|
692
|
+
push @rtnList, $ii;
|
|
693
|
+
push @dist, [ $km, $be ];
|
|
694
|
+
last if $num <= 1;
|
|
695
|
+
}
|
|
696
|
+
return(\@rtnList, \@dist);
|
|
644
697
|
}
|
|
645
698
|
|
|
646
699
|
1; #end
|
|
@@ -691,10 +744,10 @@ True on success.
|
|
|
691
744
|
=head2 ReadAltNames
|
|
692
745
|
|
|
693
746
|
Load the alternate names database. Before calling this method the $altDir
|
|
694
|
-
package variable
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
747
|
+
package variable may be set, otherwise AltNames.dat is loaded from the same
|
|
748
|
+
directory as Geolocation.dat. This method is called automatically by
|
|
749
|
+
L</Geolocate> if the GeolocAltNames option is used and an input city name is
|
|
750
|
+
provided.
|
|
698
751
|
|
|
699
752
|
Image::ExifTool::Geolocation::ReadAltNames();
|
|
700
753
|
|
|
@@ -706,8 +759,8 @@ option is used and an input city name is provided.
|
|
|
706
759
|
|
|
707
760
|
=item Return Value:
|
|
708
761
|
|
|
709
|
-
True on success.
|
|
710
|
-
|
|
762
|
+
True on success. May be called repeatedly, but AltNames.dat is loaded only
|
|
763
|
+
on the first call.
|
|
711
764
|
|
|
712
765
|
=back
|
|
713
766
|
|
|
@@ -833,8 +886,7 @@ Comma-separated string of alternate names for this city.
|
|
|
833
886
|
|
|
834
887
|
=item Notes:
|
|
835
888
|
|
|
836
|
-
|
|
837
|
-
calling this routine.
|
|
889
|
+
L</ReadAltNames> must be called before calling this routine.
|
|
838
890
|
|
|
839
891
|
=back
|
|
840
892
|
|
|
@@ -854,37 +906,46 @@ zero or more of the following in any order, separated by commas: region
|
|
|
854
906
|
name, subregion name, country code, and/or country name. Regular
|
|
855
907
|
expressions in C</expr/> format are also allowed, optionally prefixed by
|
|
856
908
|
"ci", "re", "sr", "cc" or "co" to specifically match City, Region,
|
|
857
|
-
Subregion, CountryCode or Country name.
|
|
858
|
-
|
|
909
|
+
Subregion, CountryCode or Country name. Two special controls may be added
|
|
910
|
+
to the argument list:
|
|
859
911
|
|
|
860
|
-
|
|
912
|
+
'both' - When search input includes both name and GPS coordinates, use
|
|
913
|
+
both to determine the closest city matching the specified
|
|
914
|
+
name(s) instead of using GPS only.
|
|
861
915
|
|
|
862
|
-
|
|
916
|
+
'num=##' - When the search includes GPS coordinates, return the nearest
|
|
917
|
+
## cities instead of just the closest one. Returned cities
|
|
918
|
+
are in the order from nearest to farthest.
|
|
863
919
|
|
|
864
|
-
|
|
865
|
-
GPS position is used
|
|
920
|
+
See L<https://exiftool.org/geolocation.html#Read> for more details.
|
|
866
921
|
|
|
867
|
-
|
|
868
|
-
match. In this case the return value is a list of city
|
|
869
|
-
information lists.
|
|
922
|
+
1) Optional reference to hash of options:
|
|
870
923
|
|
|
871
|
-
|
|
872
|
-
|
|
924
|
+
GeolocMinPop - Minimum population of cities to consider in search.
|
|
925
|
+
Default 0.
|
|
873
926
|
|
|
874
|
-
|
|
875
|
-
|
|
927
|
+
GeolocMaxDist - Maximum distance (km) to search for cities when an
|
|
928
|
+
input GPS position is used. Default infinity.
|
|
876
929
|
|
|
877
|
-
|
|
930
|
+
GeolocMulti - Flag to return multiple cities if there is more than
|
|
931
|
+
one match. Used in the case where no input GPS
|
|
932
|
+
coordinates are provided. Default 0.
|
|
933
|
+
|
|
934
|
+
GeolocFeature - Comma-separated list of feature codes to include in
|
|
935
|
+
search, or exclude if the list starts with a dash (-).
|
|
936
|
+
Default undef.
|
|
878
937
|
|
|
879
|
-
|
|
938
|
+
GeolocAltNames - Flag to search alternate names database if available
|
|
939
|
+
for matching city name (see ALTERNATE DATABASES below).
|
|
940
|
+
Default undef.
|
|
880
941
|
|
|
881
|
-
|
|
882
|
-
reference to a list of entry numbers of matching cities if multiple matches
|
|
883
|
-
were found and the flag was set to return multiple matches
|
|
942
|
+
=item Return Values:
|
|
884
943
|
|
|
885
|
-
|
|
944
|
+
0) Reference to list of database entry numbers for matching cities, or undef
|
|
945
|
+
if no matches were found.
|
|
886
946
|
|
|
887
|
-
|
|
947
|
+
1) Reference to list of distance/bearing pairs for each matching city, or
|
|
948
|
+
undef if the search didn't provide GPS coordinates.
|
|
888
949
|
|
|
889
950
|
=back
|
|
890
951
|
|
|
@@ -896,11 +957,12 @@ contain the Geolocation.dat file, and optionally a GeoLang directory for the
|
|
|
896
957
|
language translations. The $geoDir variable may be set to an empty string
|
|
897
958
|
to disable loading of a database.
|
|
898
959
|
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
a
|
|
903
|
-
|
|
960
|
+
When searching for a city by name, AltNames.dat is checked to provide
|
|
961
|
+
additional possibilities for matches if the GeolocAltNames option is set and
|
|
962
|
+
an AltNames.dat database exists. The package $altDir variable may be set to
|
|
963
|
+
specify a different directory for AltNames.dat, otherwise the
|
|
964
|
+
Geolocation.dat directory is assumed. The entries in AltNames.dat must
|
|
965
|
+
match those in the currently loaded version of Geolocation.dat.
|
|
904
966
|
|
|
905
967
|
=head1 ADDING USER-DEFINED DATABASE ENTRIES
|
|
906
968
|
|
|
@@ -911,7 +973,7 @@ technique before the Geolocation module is loaded.
|
|
|
911
973
|
# city, region, subregion, country code, country, timezone,
|
|
912
974
|
['Sinemorets','burgas','Obshtina Tsarevo','BG','','Europe/Sofia',
|
|
913
975
|
# feature code, population, lat, lon
|
|
914
|
-
'',400,42.06115,27.97833],
|
|
976
|
+
'PPL',400,42.06115,27.97833],
|
|
915
977
|
);
|
|
916
978
|
|
|
917
979
|
Similarly, user-defined language translations may be defined, and will
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
# 2019/11/10 - PH Also write pitch to CameraElevationAngle
|
|
16
16
|
# 2020/12/01 - PH Added ability to read DJI CSV log files
|
|
17
17
|
# 2022/06/21 - PH Added ability to read Google Takeout JSON files
|
|
18
|
+
# 2024/04/23 - PH Added ability to read more OpenTracks GPS tags
|
|
18
19
|
#
|
|
19
20
|
# References: 1) http://www.topografix.com/GPX/1/1/
|
|
20
21
|
# 2) http://www.gpsinformation.org/dale/nmea.htm#GSA
|
|
@@ -29,7 +30,7 @@ use vars qw($VERSION);
|
|
|
29
30
|
use Image::ExifTool qw(:Public);
|
|
30
31
|
use Image::ExifTool::GPS;
|
|
31
32
|
|
|
32
|
-
$VERSION = '1.
|
|
33
|
+
$VERSION = '1.76';
|
|
33
34
|
|
|
34
35
|
sub JITTER() { return 2 } # maximum time jitter
|
|
35
36
|
|
|
@@ -66,6 +67,8 @@ my %xmlTag = (
|
|
|
66
67
|
course => 'dir', # (written by Arduino)
|
|
67
68
|
pitch => 'pitch', # (written by Arduino)
|
|
68
69
|
roll => 'roll', # (written by Arduino)
|
|
70
|
+
speed => 'speed', # (OpenTrack gpx)
|
|
71
|
+
accuracy_horizontal => 'err',#(OpenTrack gpx)
|
|
69
72
|
# XML containers (fix is reset at the opening tag of these properties)
|
|
70
73
|
wpt => '', # GPX
|
|
71
74
|
trkpt => '', # GPX
|
|
@@ -85,6 +88,7 @@ my %fixInfoKeys = (
|
|
|
85
88
|
alt => [ 'alt' ],
|
|
86
89
|
orient => [ 'dir', 'pitch', 'roll' ],
|
|
87
90
|
atemp => [ 'atemp' ],
|
|
91
|
+
err => [ 'err' ],
|
|
88
92
|
);
|
|
89
93
|
|
|
90
94
|
my %isOrient = ( dir => 1, pitch => 1, roll => 1 ); # test for orientation key
|
|
@@ -346,8 +350,8 @@ sub LoadTrackLog($$;$)
|
|
|
346
350
|
# validate altitude
|
|
347
351
|
undef $$fix{alt} if defined $$fix{alt} and $$fix{alt} !~ /^[+-]?\d+\.?\d*/;
|
|
348
352
|
$$has{alt} = 1 if $$fix{alt}; # set "has altitude" flag if appropriate
|
|
349
|
-
} elsif ($tag eq 'atemp') {
|
|
350
|
-
$$has{
|
|
353
|
+
} elsif ($tag eq 'atemp' or $tag eq 'speed' or $tag eq 'err') {
|
|
354
|
+
$$has{$tag} = 1;
|
|
351
355
|
}
|
|
352
356
|
}
|
|
353
357
|
}
|
|
@@ -392,8 +396,8 @@ sub LoadTrackLog($$;$)
|
|
|
392
396
|
# validate altitude
|
|
393
397
|
undef $$fix{alt} if defined $$fix{alt} and $$fix{alt} !~ /^[+-]?\d+\.?\d*/;
|
|
394
398
|
$$has{alt} = 1 if $$fix{alt}; # set "has altitude" flag if appropriate
|
|
395
|
-
} elsif ($tag eq 'atemp') {
|
|
396
|
-
$$has{
|
|
399
|
+
} elsif ($tag eq 'atemp' or $tag eq 'speed' or $tag eq 'err') {
|
|
400
|
+
$$has{$tag} = 1;
|
|
397
401
|
}
|
|
398
402
|
}
|
|
399
403
|
}
|
|
@@ -1126,7 +1130,7 @@ sub SetGeoValues($$;$)
|
|
|
1126
1130
|
# loop through available fix information categories
|
|
1127
1131
|
# (pos, track, alt, orient)
|
|
1128
1132
|
my ($category, $key);
|
|
1129
|
-
Category: foreach $category (qw{pos track alt orient atemp}) {
|
|
1133
|
+
Category: foreach $category (qw{pos track alt orient atemp err}) {
|
|
1130
1134
|
next unless $$has{$category};
|
|
1131
1135
|
my ($f, $p0b, $p1b, $f0b);
|
|
1132
1136
|
# loop through specific fix information keys
|
|
@@ -1236,10 +1240,11 @@ Category: foreach $category (qw{pos track alt orient atemp}) {
|
|
|
1236
1240
|
@r = $et->SetNewValue(GPSLongitude => $$fix{lon}, %opts);
|
|
1237
1241
|
@r = $et->SetNewValue(GPSAltitude => $gpsAlt, %opts);
|
|
1238
1242
|
@r = $et->SetNewValue(GPSAltitudeRef => $gpsAltRef, %opts);
|
|
1239
|
-
if ($$has{track}) {
|
|
1243
|
+
if ($$has{track} or $$has{speed}) {
|
|
1244
|
+
my $type = $$has{track} ? 'track' : 'speed';
|
|
1240
1245
|
my $tFix = $fix;
|
|
1241
|
-
if (not defined $$fix{
|
|
1242
|
-
my $p = FindFix($et
|
|
1246
|
+
if (not defined $$fix{$type} and defined $iExt) {
|
|
1247
|
+
my $p = FindFix($et,$type,$times,$points,$iExt,$iDir,$geoMaxExtSecs);
|
|
1243
1248
|
$tFix = $p if $p;
|
|
1244
1249
|
}
|
|
1245
1250
|
@r = $et->SetNewValue(GPSTrack => $$tFix{track}, %opts);
|
|
@@ -1280,6 +1285,9 @@ Category: foreach $category (qw{pos track alt orient atemp}) {
|
|
|
1280
1285
|
}
|
|
1281
1286
|
@r = $et->SetNewValue(AmbientTemperature => $$tFix{atemp}, %opts);
|
|
1282
1287
|
}
|
|
1288
|
+
if ($$has{err}) {
|
|
1289
|
+
@r = $et->SetNewValue(GPSHPositioningError => $$fix{err}, %opts);
|
|
1290
|
+
}
|
|
1283
1291
|
unless ($xmp) {
|
|
1284
1292
|
my ($latRef, $lonRef);
|
|
1285
1293
|
$latRef = ($$fix{lat} > 0 ? 'N' : 'S') if defined $$fix{lat};
|
|
@@ -1305,7 +1313,7 @@ Category: foreach $category (qw{pos track alt orient atemp}) {
|
|
|
1305
1313
|
GPSAltitude GPSAltitudeRef GPSDateStamp GPSTimeStamp GPSDateTime
|
|
1306
1314
|
GPSTrack GPSTrackRef GPSSpeed GPSSpeedRef GPSImgDirection
|
|
1307
1315
|
GPSImgDirectionRef GPSPitch GPSRoll CameraElevationAngle
|
|
1308
|
-
AmbientTemperature GPSCoordinates))
|
|
1316
|
+
AmbientTemperature GPSHPositioningError GPSCoordinates))
|
|
1309
1317
|
{
|
|
1310
1318
|
my @r = $et->SetNewValue($_, undef, %opts);
|
|
1311
1319
|
}
|
|
@@ -65,7 +65,7 @@ use Image::ExifTool::Exif;
|
|
|
65
65
|
use Image::ExifTool::GPS;
|
|
66
66
|
use Image::ExifTool::XMP;
|
|
67
67
|
|
|
68
|
-
$VERSION = '4.
|
|
68
|
+
$VERSION = '4.33';
|
|
69
69
|
|
|
70
70
|
sub LensIDConv($$$);
|
|
71
71
|
sub ProcessNikonAVI($$$);
|
|
@@ -5506,13 +5506,14 @@ my %nikonFocalConversions = (
|
|
|
5506
5506
|
37 => 'Nikkor Z 600mm f/4 TC VR S', #28
|
|
5507
5507
|
38 => 'Nikkor Z 85mm f/1.2 S', #28
|
|
5508
5508
|
39 => 'Nikkor Z 17-28mm f/2.8', #IB
|
|
5509
|
-
40 => '
|
|
5510
|
-
41 => '
|
|
5509
|
+
40 => 'Nikkor Z 26mm f/2.8', #28
|
|
5510
|
+
41 => 'Nikkor Z DX 12-28mm f/3.5-5.6 PZ VR', #28
|
|
5511
5511
|
42 => 'Nikkor Z 180-600mm f/5.6-6.3 VR', #30
|
|
5512
|
-
43 => '
|
|
5513
|
-
44 => '
|
|
5514
|
-
45 => '
|
|
5512
|
+
43 => 'Nikkor Z DX 24mm f/1.7', #28
|
|
5513
|
+
44 => 'Nikkor Z 70-180mm f/2.8', #28
|
|
5514
|
+
45 => 'Nikkor Z 600mm f/6.3 VR S', #28
|
|
5515
5515
|
46 => 'Nikkor Z 135mm f/1.8 S Plena', #28
|
|
5516
|
+
48 => 'Nikkor Z 28-400mm f/4-8 VR', #30
|
|
5516
5517
|
32768 => 'Nikkor Z 400mm f/2.8 TC VR S TC-1.4x', #28
|
|
5517
5518
|
32769 => 'Nikkor Z 600mm f/4 TC VR S TC-1.4x', #28
|
|
5518
5519
|
},
|