exiftool_vendored 12.82.0 → 12.85.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/Changes +59 -0
- data/bin/MANIFEST +2 -18
- data/bin/META.json +1 -1
- data/bin/META.yml +1 -1
- data/bin/README +47 -46
- data/bin/build_geolocation +945 -0
- data/bin/config_files/example.config +2 -2
- data/bin/exiftool +75 -51
- data/bin/fmt_files/gpx.fmt +2 -1
- data/bin/fmt_files/gpx_wpt.fmt +2 -1
- data/bin/lib/Image/ExifTool/Apple.pm +52 -7
- data/bin/lib/Image/ExifTool/BuildTagLookup.pm +5 -2
- data/bin/lib/Image/ExifTool/Canon.pm +11 -2
- data/bin/lib/Image/ExifTool/CanonVRD.pm +18 -5
- data/bin/lib/Image/ExifTool/DJI.pm +29 -0
- data/bin/lib/Image/ExifTool/DPX.pm +3 -3
- data/bin/lib/Image/ExifTool/Exif.pm +19 -2
- data/bin/lib/Image/ExifTool/GM.pm +17 -8
- data/bin/lib/Image/ExifTool/Geolocation.dat +0 -0
- data/bin/lib/Image/ExifTool/Geolocation.pm +170 -104
- data/bin/lib/Image/ExifTool/Geotag.pm +18 -10
- data/bin/lib/Image/ExifTool/ID3.pm +28 -6
- data/bin/lib/Image/ExifTool/Nikon.pm +25 -6
- data/bin/lib/Image/ExifTool/Pentax.pm +64 -13
- data/bin/lib/Image/ExifTool/QuickTime.pm +26 -7
- data/bin/lib/Image/ExifTool/QuickTimeStream.pl +5 -0
- data/bin/lib/Image/ExifTool/Sony.pm +15 -6
- data/bin/lib/Image/ExifTool/TagLookup.pm +3557 -3540
- data/bin/lib/Image/ExifTool/TagNames.pod +61 -13
- data/bin/lib/Image/ExifTool/WriteQuickTime.pl +4 -2
- data/bin/lib/Image/ExifTool/Writer.pl +166 -134
- data/bin/lib/Image/ExifTool/XMP.pm +2 -0
- data/bin/lib/Image/ExifTool/XMP2.pl +3 -0
- data/bin/lib/Image/ExifTool.pm +59 -27
- data/bin/lib/Image/ExifTool.pod +88 -71
- data/bin/perl-Image-ExifTool.spec +45 -45
- data/lib/exiftool_vendored/version.rb +1 -1
- metadata +3 -20
- data/bin/lib/Image/ExifTool/GeoLang/cs.pm +0 -978
- data/bin/lib/Image/ExifTool/GeoLang/de.pm +0 -1975
- data/bin/lib/Image/ExifTool/GeoLang/en_ca.pm +0 -44
- data/bin/lib/Image/ExifTool/GeoLang/en_gb.pm +0 -124
- data/bin/lib/Image/ExifTool/GeoLang/es.pm +0 -2921
- data/bin/lib/Image/ExifTool/GeoLang/fi.pm +0 -1116
- data/bin/lib/Image/ExifTool/GeoLang/fr.pm +0 -3171
- data/bin/lib/Image/ExifTool/GeoLang/it.pm +0 -2750
- data/bin/lib/Image/ExifTool/GeoLang/ja.pm +0 -10256
- data/bin/lib/Image/ExifTool/GeoLang/ko.pm +0 -4499
- data/bin/lib/Image/ExifTool/GeoLang/nl.pm +0 -1270
- data/bin/lib/Image/ExifTool/GeoLang/pl.pm +0 -3019
- data/bin/lib/Image/ExifTool/GeoLang/ru.pm +0 -18220
- data/bin/lib/Image/ExifTool/GeoLang/sk.pm +0 -441
- data/bin/lib/Image/ExifTool/GeoLang/sv.pm +0 -714
- data/bin/lib/Image/ExifTool/GeoLang/tr.pm +0 -452
- data/bin/lib/Image/ExifTool/GeoLang/zh_cn.pm +0 -2225
- data/bin/lib/Image/ExifTool/GeoLang/zh_tw.pm +0 -72
@@ -15,7 +15,7 @@ use vars qw($VERSION);
|
|
15
15
|
use Image::ExifTool qw(:DataAccess :Utils);
|
16
16
|
use Image::ExifTool::GPS;
|
17
17
|
|
18
|
-
$VERSION = '1.
|
18
|
+
$VERSION = '1.01';
|
19
19
|
|
20
20
|
sub Process_marl($$$);
|
21
21
|
sub Process_mrld($$$);
|
@@ -30,6 +30,8 @@ my %convertUnits = (
|
|
30
30
|
ltr => 'L',
|
31
31
|
);
|
32
32
|
|
33
|
+
my $pi = 3.141592653589793;
|
34
|
+
|
33
35
|
# offsets and scaling factors to convert to reasonable units
|
34
36
|
my %changeOffset = (
|
35
37
|
C => -273.15, # K to C
|
@@ -37,8 +39,8 @@ my %changeOffset = (
|
|
37
39
|
my %changeScale = (
|
38
40
|
G => 1 / 9.80665, # m/s2 to G
|
39
41
|
kph => 3.6, # m/s to km/h
|
40
|
-
deg => 180 /
|
41
|
-
'deg/sec' => 180 /
|
42
|
+
deg => 180 / $pi, # radians to degrees
|
43
|
+
'deg/sec' => 180 / $pi, # rad/s to deg/s
|
42
44
|
'%' => 100, # decimal to %
|
43
45
|
kPa => 1/1000, # Pa to kPa
|
44
46
|
rpm => 10, # ? (arbitrary factor of 10)
|
@@ -181,7 +183,7 @@ my %channelStruct = (
|
|
181
183
|
Name => 'GPSTrack',
|
182
184
|
Description => 'GPS Track',
|
183
185
|
Groups => { 2 => 'Location' },
|
184
|
-
PrintConv => 'sprintf("%.2f",$val)',
|
186
|
+
PrintConv => '$val > 360 ? "n/a" : sprintf("%.2f",$val)', # (seen 655.35)
|
185
187
|
},
|
186
188
|
ABSActive => { },
|
187
189
|
AccelPos => { },
|
@@ -209,7 +211,14 @@ my %channelStruct = (
|
|
209
211
|
EngineTorqureReq => { },
|
210
212
|
FuelCapacity => { },
|
211
213
|
FuelLevel => { },
|
212
|
-
Gear => {
|
214
|
+
Gear => {
|
215
|
+
Notes => q{
|
216
|
+
in the PrintCSV output, the value for Neutral is set to -1, and Reverse to
|
217
|
+
-100 for compatibility with RaceRender
|
218
|
+
},
|
219
|
+
CSVConv => { 13 => -1, 14 => -100 },
|
220
|
+
PrintConv => { 1=>1, 2=>2, 3=>3, 4=>4, 5=>5, 6=>6, 13=>'N', 14=>'R' }
|
221
|
+
},
|
213
222
|
GPSFix => { },
|
214
223
|
InfotainOpMode => { },
|
215
224
|
IntakeAirTemperature => { },
|
@@ -261,7 +270,7 @@ my %channelStruct = (
|
|
261
270
|
sub PrintCSV($;$)
|
262
271
|
{
|
263
272
|
my ($et, $ts) = @_;
|
264
|
-
my $csv = $$et{GMCsv} or return;
|
273
|
+
my $csv = $$et{GMCsv} or return; # get the list of channels with measurements
|
265
274
|
@$csv or return;
|
266
275
|
my $vals = $$et{GMVals};
|
267
276
|
my $gmDict = $$et{GMDictionary};
|
@@ -271,7 +280,7 @@ sub PrintCSV($;$)
|
|
271
280
|
foreach (@$csv) {
|
272
281
|
my $gmChan = $$gmDict[$_];
|
273
282
|
$items[$_] = $$vals[$_] * $$gmChan{Mult} + $$gmChan{Off};
|
274
|
-
# apply
|
283
|
+
# apply CSV conversion if applicable (ie. Gear)
|
275
284
|
next unless $$gmChan{Conv} and defined $$gmChan{Conv}{$items[$_]};
|
276
285
|
$items[$_] = $$gmChan{Conv}{$items[$_]};
|
277
286
|
}
|
@@ -397,7 +406,7 @@ sub Process_mrld($$$)
|
|
397
406
|
my $init = int(($a[6] + $a[7]) / 2); # initial value for difference readings
|
398
407
|
# save information about this channel necessary for processing the marl data
|
399
408
|
$$gmDict[$chan] = { Name => $name, Mult => $mult, Off => $off, Init => $init };
|
400
|
-
$$gmDict[$chan]{Conv} = $$tagInfo{
|
409
|
+
$$gmDict[$chan]{Conv} = $$tagInfo{CSVConv};
|
401
410
|
$csv and $$csv[$chan] = $a[12] . ($a[3] ? " ($a[3])" : '');
|
402
411
|
}
|
403
412
|
# channel 0 must not be defined because we use it for the TimeStamp
|
Binary file
|
@@ -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, optional space-followed-by-feature-name, then 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.08'; # (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, %featureTypes);
|
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
|
+
$featureTypes{$line} = $1 if $line =~ s/ (.*)//;
|
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
|
+
$featureTypes{$fc} = $1 if $fc =~ s/ (.*)//;
|
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) {
|
@@ -349,34 +372,35 @@ sub AddEntry(@)
|
|
349
372
|
# Inputs: 0) entry number or index into sorted database,
|
350
373
|
# 1) optional language code, 2) flag to use index into sorted database
|
351
374
|
# Returns: 0-10) city,region,subregion,country_code,country,timezone,
|
352
|
-
# feature_code,pop,lat,lon,
|
375
|
+
# feature_code,pop,lat,lon,feature_type
|
353
376
|
sub GetEntry($;$$)
|
354
377
|
{
|
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
|
+
my $ft = $featureTypes{$fc};
|
399
|
+
if ($lang and $lang ne 'en') {
|
376
400
|
my $xlat = $langLookup{$lang};
|
377
401
|
# load language lookups if not done already
|
378
402
|
if (not defined $xlat) {
|
379
|
-
if (eval "require '$
|
403
|
+
if (eval "require '$geoDir/GeoLang/$lang.pm'") {
|
380
404
|
my $trans = "Image::ExifTool::GeoLang::${lang}::Translate";
|
381
405
|
no strict 'refs';
|
382
406
|
$xlat = \%$trans if %$trans;
|
@@ -406,9 +430,10 @@ sub GetEntry($;$$)
|
|
406
430
|
$sub = $$xlat{"$cc$rgn,$sub,"} || $$xlat{$sub} || $sub;
|
407
431
|
$rgn = $$xlat{"$cc$rgn,"} || $$xlat{$rgn} || $rgn;
|
408
432
|
$country = $$xlat{"$cc,"} || $$xlat{$country} || $country;
|
433
|
+
$ft = $$xlat{$fc} if $$xlat{$fc};
|
409
434
|
}
|
410
435
|
}
|
411
|
-
return($city,$rgn,$sub,$cc,$country,$timezoneList[$tn],$fc,$pop,$lt,$ln);
|
436
|
+
return($city,$rgn,$sub,$cc,$country,$timezoneList[$tn],$fc,$pop,$lt,$ln,$ft);
|
412
437
|
}
|
413
438
|
|
414
439
|
#------------------------------------------------------------------------------
|
@@ -429,17 +454,16 @@ sub GetAltNames($;$)
|
|
429
454
|
# Look up lat,lon or city in geolocation database
|
430
455
|
# Inputs: 0) "lat,lon", "city,region,country", etc, (city must be first)
|
431
456
|
# 1) options hash reference (or undef for no options)
|
432
|
-
# Options: GeolocMinPop, GeolocMaxDist, GeolocMulti, GeolocFeature, GeolocAltNames
|
433
|
-
#
|
434
|
-
#
|
435
|
-
#
|
436
|
-
#
|
437
|
-
|
438
|
-
sub Geolocate($;$$$$$)
|
457
|
+
# Options: GeolocMinPop, GeolocMaxDist, GeolocMulti, GeolocFeature, GeolocAltNames,
|
458
|
+
# GeolocNearby
|
459
|
+
# Returns: List of matching city information, empty if none found.
|
460
|
+
# Each element in the list is an array with 0=index of city in database,
|
461
|
+
# 1=distance in km (or undef if no distance), 2=compass bearing (or undef)
|
462
|
+
sub Geolocate($;$)
|
439
463
|
{
|
440
464
|
my ($arg, $opts) = @_;
|
441
465
|
my ($city, @exact, %regex, @multiCity, $other, $idx, @cargs, $useLastFound);
|
442
|
-
my ($minPop, $minDistU, $minDistC, @matchParms, @coords,
|
466
|
+
my ($minPop, $minDistU, $minDistC, @matchParms, @coords, %fcOK, $both);
|
443
467
|
my ($pop, $maxDist, $multi, $fcodes, $altNames, @startTime);
|
444
468
|
|
445
469
|
$opts and ($pop, $maxDist, $multi, $fcodes, $altNames) =
|
@@ -449,7 +473,7 @@ sub Geolocate($;$$$$$)
|
|
449
473
|
require Time::HiRes;
|
450
474
|
@startTime = Time::HiRes::gettimeofday();
|
451
475
|
}
|
452
|
-
@cityList or warn('No Geolocation database'), return
|
476
|
+
@cityList or warn('No Geolocation database'), return();
|
453
477
|
# make population code for comparing with 2 bytes at offset 6 in database
|
454
478
|
if ($pop) {
|
455
479
|
$pop = sprintf('%.1e', $pop);
|
@@ -457,17 +481,18 @@ sub Geolocate($;$$$$$)
|
|
457
481
|
}
|
458
482
|
if ($fcodes) {
|
459
483
|
my $neg = $fcodes =~ s/^-//;
|
460
|
-
my @fcodes = split /\s
|
484
|
+
my @fcodes = split /\s*,-?\s*/, lc $fcodes; # (allow leading dash on subsequent codes)
|
461
485
|
if ($neg) {
|
462
|
-
$
|
463
|
-
defined $featureCodes{
|
486
|
+
$fcOK{$_} = 1 foreach 0..$#featureCodes;
|
487
|
+
defined $featureCodes{$_} and delete $fcOK{$featureCodes{$_}} foreach @fcodes;
|
464
488
|
} else {
|
465
|
-
defined $featureCodes{
|
489
|
+
defined $featureCodes{$_} and $fcOK{$featureCodes{$_}} = 1 foreach @fcodes;
|
466
490
|
}
|
467
491
|
}
|
468
492
|
#
|
469
493
|
# process input argument
|
470
494
|
#
|
495
|
+
my $num = 1;
|
471
496
|
$arg =~ s/^\s+//; $arg =~ s/\s+$//; # remove leading/trailing spaces
|
472
497
|
my @args = split /\s*,\s*/, $arg;
|
473
498
|
my %ri = ( cc => 0, co => 1, re => 2, sr => 3, ci => 8, '' => 9 );
|
@@ -486,6 +511,8 @@ sub Geolocate($;$$$$$)
|
|
486
511
|
push @coords, $_ if @coords < 2;
|
487
512
|
} elsif (lc $_ eq 'both') {
|
488
513
|
$both = 1;
|
514
|
+
} elsif ($_ =~ /^num=(\d+)$/i) {
|
515
|
+
$num = $1;
|
489
516
|
} elsif ($_) {
|
490
517
|
push @cargs, $_;
|
491
518
|
if ($city) {
|
@@ -497,7 +524,7 @@ sub Geolocate($;$$$$$)
|
|
497
524
|
}
|
498
525
|
unless (defined $city or @coords == 2) {
|
499
526
|
warn("Insufficient information to determine geolocation\n");
|
500
|
-
return
|
527
|
+
return();
|
501
528
|
}
|
502
529
|
# sort database by logitude if finding entry based on coordinates
|
503
530
|
SortDatabase('Latitude') if @coords == 2 and ($both or not defined $city);
|
@@ -551,7 +578,7 @@ Entry: for (; $i<@cityList; ++$i) {
|
|
551
578
|
$str !~ $_ or next Entry foreach @{$regex{19}};
|
552
579
|
}
|
553
580
|
# test feature code and population
|
554
|
-
next if $
|
581
|
+
next if $fcodes and not $fcOK{ord(substr($cityList[$i],12,1)) & 0x3f};
|
555
582
|
my $pc = substr($cityList[$i],6,2);
|
556
583
|
if (not defined $minPop or $pc ge $minPop) {
|
557
584
|
$lastFound{$i} = $pc;
|
@@ -561,16 +588,14 @@ Entry: for (; $i<@cityList; ++$i) {
|
|
561
588
|
@startTime and printf("= Processing time: %.3f sec\n", Time::HiRes::tv_interval(\@startTime));
|
562
589
|
if (%lastFound) {
|
563
590
|
@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
|
591
|
+
scalar(keys %lastFound) > 200 and warn("Too many matching cities\n"), return();
|
565
592
|
unless (@lastByPop) {
|
566
593
|
@lastByPop = sort { $lastFound{$b} cmp $lastFound{$a} or $cityList[$a] cmp $cityList[$b] } keys %lastFound;
|
567
594
|
}
|
568
|
-
|
569
|
-
return($n, [ @lastByPop ]) if $n > 1 and $multi;
|
570
|
-
return($n, $lastByPop[0]);
|
595
|
+
return(\@lastByPop);
|
571
596
|
}
|
572
597
|
warn "No such city in Geolocation database\n";
|
573
|
-
return
|
598
|
+
return();
|
574
599
|
}
|
575
600
|
#
|
576
601
|
# determine Geolocation based on GPS coordinates
|
@@ -606,9 +631,11 @@ Entry: for (; $i<@cityList; ++$i) {
|
|
606
631
|
my ($inc, $end, $n) = (-1, -1, $n0+1);
|
607
632
|
my ($p0, $t0) = ($lat*$pi/0x100000 - $pi/2, $lon*$pi/0x080000 - $pi);
|
608
633
|
my $cp0 = cos($p0);
|
634
|
+
my (@matches, @rtnList, @dist);
|
635
|
+
|
609
636
|
for (;;) {
|
610
637
|
if (($n += $inc) == $end) {
|
611
|
-
last if $inc == 1;
|
638
|
+
last if $inc == 1 or $n0 == $n1;
|
612
639
|
($inc, $end, $n) = (1, $numEntries, $n1);
|
613
640
|
}
|
614
641
|
my $i = $sorted ? $$sorted[$n] : $n;
|
@@ -619,28 +646,56 @@ Entry: for (; $i<@cityList; ++$i) {
|
|
619
646
|
abs($lt - $lat) > $minDistC and $n = $end - $inc, next;
|
620
647
|
# ignore if population is below threshold
|
621
648
|
next if defined $minPop and $minPop ge substr($cityList[$i],6,2);
|
622
|
-
next if $
|
649
|
+
next if $fcodes and not $fcOK{ord(substr($cityList[$i],12,1)) & 0x3f};
|
623
650
|
$ln = ($ln << 4) | ($f & 0x0f);
|
624
651
|
# calculate great circle distance to this city on unit sphere
|
625
652
|
my ($p1, $t1) = ($lt*$pi/0x100000 - $pi/2, $ln*$pi/0x080000 - $pi);
|
626
653
|
my ($sp, $st) = (sin(($p1-$p0)/2), sin(($t1-$t0)/2));
|
627
654
|
my $a = $sp * $sp + $cp0 * cos($p1) * $st * $st;
|
628
|
-
my $distU = atan2(sqrt($a), sqrt(1-$a));
|
655
|
+
my $distU = atan2(sqrt($a), sqrt(1-$a)); # distance on unit sphere
|
629
656
|
next if $distU > $minDistU;
|
630
|
-
$minDistU = $distU;
|
631
|
-
$minDistC = $minDistU * 0x200000 / $pi;
|
632
657
|
@matchParms = ($i, $p1, $t1, $distU);
|
658
|
+
if ($num <= 1) {
|
659
|
+
$minDistU = $distU;
|
660
|
+
} else {
|
661
|
+
my $j;
|
662
|
+
# add this entry into list of matching cities ordered by closest first
|
663
|
+
for ($j=0; $j<@matches; ++$j) {
|
664
|
+
last if $distU < $matches[$j][3];
|
665
|
+
}
|
666
|
+
if ($j < $#matches) {
|
667
|
+
splice @matches, $j, 0, [ @matchParms ];
|
668
|
+
} else {
|
669
|
+
$matches[$j] = [ @matchParms ];
|
670
|
+
}
|
671
|
+
# restrict list to the specified number of nearest cities
|
672
|
+
pop @matches if @matches > $num;
|
673
|
+
# update minimum distance with furthest match if we satisfied our quota
|
674
|
+
$minDistU = $matches[-1][3] if @matches >= $num;
|
675
|
+
}
|
676
|
+
$minDistC = $minDistU * 0x200000 / $pi; # distance in scaled coordinate units
|
633
677
|
}
|
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
|
678
|
+
@matchParms or warn("No suitable location in Geolocation database\n"), return();
|
679
|
+
$num = @matches;
|
641
680
|
|
642
681
|
@startTime and printf("- Processing time: %.3f sec\n", Time::HiRes::tv_interval(\@startTime));
|
643
|
-
|
682
|
+
|
683
|
+
for (;;) {
|
684
|
+
if ($num > 1) {
|
685
|
+
last unless @matches;
|
686
|
+
@matchParms = @{$matches[0]};
|
687
|
+
shift @matches;
|
688
|
+
}
|
689
|
+
# calculate distance in km and bearing to matching city
|
690
|
+
my ($ii, $p1, $t1, $distU) = @matchParms;
|
691
|
+
my $km = sprintf('%.2f', 2 * $earthRadius * $distU);
|
692
|
+
my $be = atan2(sin($t1-$t0)*cos($p1-$p0), $cp0*sin($p1)-sin($p0)*cos($p1)*cos($t1-$t0));
|
693
|
+
$be = int($be * 180 / $pi + 360.5) % 360; # convert from radians to integer degrees
|
694
|
+
push @rtnList, $ii;
|
695
|
+
push @dist, [ $km, $be ];
|
696
|
+
last if $num <= 1;
|
697
|
+
}
|
698
|
+
return(\@rtnList, \@dist);
|
644
699
|
}
|
645
700
|
|
646
701
|
1; #end
|
@@ -691,10 +746,10 @@ True on success.
|
|
691
746
|
=head2 ReadAltNames
|
692
747
|
|
693
748
|
Load the alternate names database. Before calling this method the $altDir
|
694
|
-
package variable
|
695
|
-
|
696
|
-
|
697
|
-
|
749
|
+
package variable may be set, otherwise AltNames.dat is loaded from the same
|
750
|
+
directory as Geolocation.dat. This method is called automatically by
|
751
|
+
L</Geolocate> if the GeolocAltNames option is used and an input city name is
|
752
|
+
provided.
|
698
753
|
|
699
754
|
Image::ExifTool::Geolocation::ReadAltNames();
|
700
755
|
|
@@ -706,8 +761,8 @@ option is used and an input city name is provided.
|
|
706
761
|
|
707
762
|
=item Return Value:
|
708
763
|
|
709
|
-
True on success.
|
710
|
-
|
764
|
+
True on success. May be called repeatedly, but AltNames.dat is loaded only
|
765
|
+
on the first call.
|
711
766
|
|
712
767
|
=back
|
713
768
|
|
@@ -810,6 +865,8 @@ item Return Values:
|
|
810
865
|
|
811
866
|
9) GPS longitude
|
812
867
|
|
868
|
+
10) Feature type, or undef
|
869
|
+
|
813
870
|
=back
|
814
871
|
|
815
872
|
=head2 GetAltNames
|
@@ -833,8 +890,7 @@ Comma-separated string of alternate names for this city.
|
|
833
890
|
|
834
891
|
=item Notes:
|
835
892
|
|
836
|
-
|
837
|
-
calling this routine.
|
893
|
+
L</ReadAltNames> must be called before calling this routine.
|
838
894
|
|
839
895
|
=back
|
840
896
|
|
@@ -854,37 +910,46 @@ zero or more of the following in any order, separated by commas: region
|
|
854
910
|
name, subregion name, country code, and/or country name. Regular
|
855
911
|
expressions in C</expr/> format are also allowed, optionally prefixed by
|
856
912
|
"ci", "re", "sr", "cc" or "co" to specifically match City, Region,
|
857
|
-
Subregion, CountryCode or Country name.
|
858
|
-
|
913
|
+
Subregion, CountryCode or Country name. Two special controls may be added
|
914
|
+
to the argument list:
|
859
915
|
|
860
|
-
|
916
|
+
'both' - When search input includes both name and GPS coordinates, use
|
917
|
+
both to determine the closest city matching the specified
|
918
|
+
name(s) instead of using GPS only.
|
861
919
|
|
862
|
-
|
920
|
+
'num=##' - When the search includes GPS coordinates, return the nearest
|
921
|
+
## cities instead of just the closest one. Returned cities
|
922
|
+
are in the order from nearest to farthest.
|
863
923
|
|
864
|
-
|
865
|
-
GPS position is used
|
924
|
+
See L<https://exiftool.org/geolocation.html#Read> for more details.
|
866
925
|
|
867
|
-
|
868
|
-
match. In this case the return value is a list of city
|
869
|
-
information lists.
|
926
|
+
1) Optional reference to hash of options:
|
870
927
|
|
871
|
-
|
872
|
-
|
928
|
+
GeolocMinPop - Minimum population of cities to consider in search.
|
929
|
+
Default 0.
|
873
930
|
|
874
|
-
|
875
|
-
|
931
|
+
GeolocMaxDist - Maximum distance (km) to search for cities when an
|
932
|
+
input GPS position is used. Default infinity.
|
876
933
|
|
877
|
-
|
934
|
+
GeolocMulti - Flag to return multiple cities if there is more than
|
935
|
+
one match. Used in the case where no input GPS
|
936
|
+
coordinates are provided. Default 0.
|
937
|
+
|
938
|
+
GeolocFeature - Comma-separated list of feature codes to include in
|
939
|
+
search, or exclude if the list starts with a dash (-).
|
940
|
+
Default undef.
|
878
941
|
|
879
|
-
|
942
|
+
GeolocAltNames - Flag to search alternate names database if available
|
943
|
+
for matching city name (see ALTERNATE DATABASES below).
|
944
|
+
Default undef.
|
880
945
|
|
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
|
946
|
+
=item Return Values:
|
884
947
|
|
885
|
-
|
948
|
+
0) Reference to list of database entry numbers for matching cities, or undef
|
949
|
+
if no matches were found.
|
886
950
|
|
887
|
-
|
951
|
+
1) Reference to list of distance/bearing pairs for each matching city, or
|
952
|
+
undef if the search didn't provide GPS coordinates.
|
888
953
|
|
889
954
|
=back
|
890
955
|
|
@@ -896,11 +961,12 @@ contain the Geolocation.dat file, and optionally a GeoLang directory for the
|
|
896
961
|
language translations. The $geoDir variable may be set to an empty string
|
897
962
|
to disable loading of a database.
|
898
963
|
|
899
|
-
|
900
|
-
|
901
|
-
|
902
|
-
a
|
903
|
-
|
964
|
+
When searching for a city by name, AltNames.dat is checked to provide
|
965
|
+
additional possibilities for matches if the GeolocAltNames option is set and
|
966
|
+
an AltNames.dat database exists. The package $altDir variable may be set to
|
967
|
+
specify a different directory for AltNames.dat, otherwise the
|
968
|
+
Geolocation.dat directory is assumed. The entries in AltNames.dat must
|
969
|
+
match those in the currently loaded version of Geolocation.dat.
|
904
970
|
|
905
971
|
=head1 ADDING USER-DEFINED DATABASE ENTRIES
|
906
972
|
|
@@ -911,7 +977,7 @@ technique before the Geolocation module is loaded.
|
|
911
977
|
# city, region, subregion, country code, country, timezone,
|
912
978
|
['Sinemorets','burgas','Obshtina Tsarevo','BG','','Europe/Sofia',
|
913
979
|
# feature code, population, lat, lon
|
914
|
-
'',400,42.06115,27.97833],
|
980
|
+
'PPL',400,42.06115,27.97833],
|
915
981
|
);
|
916
982
|
|
917
983
|
Similarly, user-defined language translations may be defined, and will
|