line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Geo::Coder::Navteq; |
2
|
|
|
|
|
|
|
|
3
|
2
|
|
|
2
|
|
139769
|
use strict; |
|
2
|
|
|
|
|
6
|
|
|
2
|
|
|
|
|
239
|
|
4
|
2
|
|
|
2
|
|
13
|
use warnings; |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
70
|
|
5
|
|
|
|
|
|
|
|
6
|
2
|
|
|
2
|
|
11
|
use Carp qw(croak); |
|
2
|
|
|
|
|
8
|
|
|
2
|
|
|
|
|
168
|
|
7
|
2
|
|
|
2
|
|
2109
|
use Encode (); |
|
2
|
|
|
|
|
68127
|
|
|
2
|
|
|
|
|
339
|
|
8
|
2
|
|
|
2
|
|
5156
|
use LWP::UserAgent; |
|
2
|
|
|
|
|
388291
|
|
|
2
|
|
|
|
|
194
|
|
9
|
2
|
|
|
2
|
|
29
|
use URI; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
54
|
|
10
|
2
|
|
|
2
|
|
3847
|
use XML::Simple (); |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
our $VERSION = '0.03'; |
13
|
|
|
|
|
|
|
$VERSION = eval $VERSION; |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
sub new { |
16
|
|
|
|
|
|
|
my ($class, @params) = @_; |
17
|
|
|
|
|
|
|
my %params = (@params % 2) ? (appkey => @params) : @params; |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
croak q('appkey' is required) unless $params{appkey}; |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
my $self = bless \ %params, $class; |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
if ($params{ua}) { |
24
|
|
|
|
|
|
|
$self->ua($params{ua}); |
25
|
|
|
|
|
|
|
} |
26
|
|
|
|
|
|
|
else { |
27
|
|
|
|
|
|
|
$self->{ua} = LWP::UserAgent->new(agent => "$class/$VERSION"); |
28
|
|
|
|
|
|
|
} |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
if ($self->{debug}) { |
31
|
|
|
|
|
|
|
my $dump_sub = sub { $_[0]->dump(maxlength => 0); return }; |
32
|
|
|
|
|
|
|
$self->ua->set_my_handler(request_send => $dump_sub); |
33
|
|
|
|
|
|
|
$self->ua->set_my_handler(response_done => $dump_sub); |
34
|
|
|
|
|
|
|
} |
35
|
|
|
|
|
|
|
elsif ($self->{compress}) { |
36
|
|
|
|
|
|
|
$self->ua->default_header(accept_encoding => 'gzip,deflate'); |
37
|
|
|
|
|
|
|
} |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
# Each appkey has this url aautomatically added on registration. |
40
|
|
|
|
|
|
|
$self->{url} ||= 'http://localhost'; |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
return $self; |
43
|
|
|
|
|
|
|
} |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
sub response { $_[0]->{response} } |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
sub ua { |
48
|
|
|
|
|
|
|
my ($self, $ua) = @_; |
49
|
|
|
|
|
|
|
if ($ua) { |
50
|
|
|
|
|
|
|
croak q('ua' must be (or derived from) an LWP::UserAgent') |
51
|
|
|
|
|
|
|
unless ref $ua and $ua->isa(q(LWP::UserAgent)); |
52
|
|
|
|
|
|
|
$self->{ua} = $ua; |
53
|
|
|
|
|
|
|
} |
54
|
|
|
|
|
|
|
return $self->{ua}; |
55
|
|
|
|
|
|
|
} |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
sub geocode { |
58
|
|
|
|
|
|
|
my ($self, @params) = @_; |
59
|
|
|
|
|
|
|
my %params = (@params % 2) ? (location => @params) : @params; |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
$self->_authenticate or return; |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
my $location = $params{location} or return; |
64
|
|
|
|
|
|
|
$location = Encode::decode('utf-8', $location); |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
my $uri = URI->new('http:/map24/webservices1.5'); |
67
|
|
|
|
|
|
|
$uri->host($self->_hostname); |
68
|
|
|
|
|
|
|
$uri->query_form( |
69
|
|
|
|
|
|
|
action => 'soap', |
70
|
|
|
|
|
|
|
bdom => $self->_bdom($location), |
71
|
|
|
|
|
|
|
fromAjax => 1, |
72
|
|
|
|
|
|
|
gzip => 1, |
73
|
|
|
|
|
|
|
mid => '***', |
74
|
|
|
|
|
|
|
request_id => ++$self->{request_id}, |
75
|
|
|
|
|
|
|
sid => $self->_session_id, |
76
|
|
|
|
|
|
|
writeTypeAttributes => 'false', |
77
|
|
|
|
|
|
|
xsltdir => 'ajax/2.3.0.4700/bdom_wb/', |
78
|
|
|
|
|
|
|
); |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
my $res = $self->{response} = $self->ua->get( |
81
|
|
|
|
|
|
|
$uri, referer => $self->{url}, |
82
|
|
|
|
|
|
|
); |
83
|
|
|
|
|
|
|
return unless $res->is_success; |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
my $xml = $res->decoded_content; |
86
|
|
|
|
|
|
|
return unless $xml; |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
my $data = eval { $self->_parser->xml_in(\$xml) }; |
89
|
|
|
|
|
|
|
return unless $data; |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
my $body = $data->{'soapenv:Body'}; |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
if (my $err = $body->{'soapenv:Fault'}{faultstring}) { |
94
|
|
|
|
|
|
|
if ($err =~ /RequestHeader NOT authenticated/) { |
95
|
|
|
|
|
|
|
$self->_authenticate(1); |
96
|
|
|
|
|
|
|
return &geocode; |
97
|
|
|
|
|
|
|
} |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
return; |
100
|
|
|
|
|
|
|
} |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
my @results = @{ |
103
|
|
|
|
|
|
|
$body->{'tns:searchFreeResponse'}{MapSearchResponse}{Alternatives} |
104
|
|
|
|
|
|
|
|| [] |
105
|
|
|
|
|
|
|
}; |
106
|
|
|
|
|
|
|
if (@results) { |
107
|
|
|
|
|
|
|
$#results = 0 unless wantarray; |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
# Convert from decimal minutes to decimal degrees. |
110
|
|
|
|
|
|
|
for my $result (@results) { |
111
|
|
|
|
|
|
|
do { $_ /= 60 if defined $_ } for |
112
|
|
|
|
|
|
|
@{$result->{Coordinate}}{qw(Latitude Longitude)}, |
113
|
|
|
|
|
|
|
@{$result->{PropertiesMinor}}{qw(X0 X1 Y0 Y1)}, |
114
|
|
|
|
|
|
|
} |
115
|
|
|
|
|
|
|
} |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
return wantarray ? @results : $results[0]; |
118
|
|
|
|
|
|
|
} |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
sub _authenticate { |
121
|
|
|
|
|
|
|
my ($self, $force) = @_; |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
return 1 if not $force and $self->{auth_time}; |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
# TODO: determine if there is a standard timeout when sessions need |
126
|
|
|
|
|
|
|
# to be reauthed. That would avoid a single doomed geocode request. |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
my $uri = URI->new('http:/map24/webservices1.5'); |
129
|
|
|
|
|
|
|
$uri->host($self->_hostname); |
130
|
|
|
|
|
|
|
$uri->query_form( |
131
|
|
|
|
|
|
|
action => 'GetMap24Application', |
132
|
|
|
|
|
|
|
applicationkey => $self->{appkey}, |
133
|
|
|
|
|
|
|
cgi => 'Map24AuthenticationService', |
134
|
|
|
|
|
|
|
requestid => ++$self->{request_id}, |
135
|
|
|
|
|
|
|
sid => $self->_session_id, |
136
|
|
|
|
|
|
|
); |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
my $res = $self->{response} = $self->ua->get( |
139
|
|
|
|
|
|
|
$uri, referer => $self->{url}, |
140
|
|
|
|
|
|
|
); |
141
|
|
|
|
|
|
|
return unless $res->is_success; |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
my $xml = $res->decoded_content; |
144
|
|
|
|
|
|
|
return unless $xml; |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
my $data = eval { $self->_parser->xml_in(\$xml) }; |
147
|
|
|
|
|
|
|
return unless $data; |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
return unless $data->{'soapenv:Body'}{'tns:getMap24ApplicationResponse'} |
150
|
|
|
|
|
|
|
->{GetMap24ApplicationResponse}; |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
$self->{auth_time} = time; |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
return 1; |
155
|
|
|
|
|
|
|
} |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
sub _parser { |
158
|
|
|
|
|
|
|
$_[0]->{parser} ||= XML::Simple->new( |
159
|
|
|
|
|
|
|
ContentKey => '-Value', |
160
|
|
|
|
|
|
|
ForceArray => ['item'], |
161
|
|
|
|
|
|
|
GroupTags => { |
162
|
|
|
|
|
|
|
Alternatives => 'item', |
163
|
|
|
|
|
|
|
PropertiesMajor => 'item', |
164
|
|
|
|
|
|
|
PropertiesMinor => 'item', |
165
|
|
|
|
|
|
|
}, |
166
|
|
|
|
|
|
|
KeyAttr => ['Key'], |
167
|
|
|
|
|
|
|
NoAttr => 1, |
168
|
|
|
|
|
|
|
); |
169
|
|
|
|
|
|
|
} |
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
{ |
172
|
|
|
|
|
|
|
my @chars = (0..9, 'a'..'z'); |
173
|
|
|
|
|
|
|
sub _hostname { |
174
|
|
|
|
|
|
|
my $rnd = join '', map { $chars[rand 36] } (1..8); |
175
|
|
|
|
|
|
|
return $rnd . '.tl.maptp50.map24.com'; |
176
|
|
|
|
|
|
|
} |
177
|
|
|
|
|
|
|
} |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
sub _session_id { |
180
|
|
|
|
|
|
|
return $_[0]->{session_id} ||= 'AJAXSESS_' . time . '123_' . rand; |
181
|
|
|
|
|
|
|
} |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
# The encoding scheme takes a SOAP message and converts it into a binary |
184
|
|
|
|
|
|
|
# representation of the resulting DOM. Only the location and session id will |
185
|
|
|
|
|
|
|
# vary between messages, so the bulk of the message is pre-encoded. |
186
|
|
|
|
|
|
|
sub _bdom { |
187
|
|
|
|
|
|
|
my ($self, $location) = @_; |
188
|
|
|
|
|
|
|
return '.74fsearchFree.7n_basicZ75Ltns.0WsearchFreeZ78vurn.0W' |
189
|
|
|
|
|
|
|
. 'Map24Geocoder51Z7D_.0G.0G.0GZ' |
190
|
|
|
|
|
|
|
. _encode_string($self->_session_id) . 'Z' |
191
|
|
|
|
|
|
|
. _encode_string($location) . 'XgzWgAgBWgCgDWgEgFXgJXgMWgGgNWgHgIX' |
192
|
|
|
|
|
|
|
. 'gLXgaVgOUXgbVgPUUXg0Xg1VD4fUXg8VgQUUUUU'; |
193
|
|
|
|
|
|
|
} |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
{ |
196
|
|
|
|
|
|
|
my @encode_table = (0..9, 'a'..'z', 'A'..'Z', qw(. _)); |
197
|
|
|
|
|
|
|
my %decode_table = do { my $i = 0; map { $_ => $i++ } @encode_table }; |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
sub _encode_string { |
200
|
|
|
|
|
|
|
my ($str) = @_; |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
return 0 unless defined $str; |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
$str=~ s{ ([^0-9A-Za-z]) }{ |
205
|
|
|
|
|
|
|
my $ord = ord $1; |
206
|
|
|
|
|
|
|
if (4096 > $ord) { |
207
|
|
|
|
|
|
|
join '', '.', @encode_table[$ord >> 6, $ord & 63]; |
208
|
|
|
|
|
|
|
} |
209
|
|
|
|
|
|
|
else { |
210
|
|
|
|
|
|
|
join '', '_', @encode_table[ |
211
|
|
|
|
|
|
|
$ord >> 24, $ord >> 18 & 63, $ord >> 12 & 63, |
212
|
|
|
|
|
|
|
$ord >> 16 & 63, $ord & 63 |
213
|
|
|
|
|
|
|
]; |
214
|
|
|
|
|
|
|
} |
215
|
|
|
|
|
|
|
}egx; |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
my $prefix = _encode_number(length $str); |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
return $encode_table[ $decode_table{ substr($prefix, 0, 1) } & 15] |
220
|
|
|
|
|
|
|
. substr($prefix, 1) . $str; |
221
|
|
|
|
|
|
|
} |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
sub _encode_number { |
224
|
|
|
|
|
|
|
my ($num) = @_; |
225
|
|
|
|
|
|
|
return $encode_table[32] unless $num; |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
my $len = length($num); |
228
|
|
|
|
|
|
|
my $chunks = int(($len - 1) / 3) + 2; |
229
|
|
|
|
|
|
|
my @s = ('D'); |
230
|
|
|
|
|
|
|
my $end = 0; |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
for my $chunk (0 .. $chunks - 1) { |
233
|
|
|
|
|
|
|
my $i = $chunk * 3; |
234
|
|
|
|
|
|
|
my @c = (0, 0, 0); |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
for my $j (0 .. 2) { |
237
|
|
|
|
|
|
|
if ($i >= $len) { |
238
|
|
|
|
|
|
|
$c[$j] = 15; |
239
|
|
|
|
|
|
|
$end = 1; |
240
|
|
|
|
|
|
|
} |
241
|
|
|
|
|
|
|
else { |
242
|
|
|
|
|
|
|
$c[$j] = ord(substr $num, $i, 1) - 48; |
243
|
|
|
|
|
|
|
$c[$j] = 0 if $c[$j] < 0 or $c[$j] > 9; |
244
|
|
|
|
|
|
|
} |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
$i++ |
247
|
|
|
|
|
|
|
} |
248
|
|
|
|
|
|
|
my $val = $c[0] << 8 | $c[1] << 4 | $c[2]; |
249
|
|
|
|
|
|
|
push @s, @encode_table[$val >> 6, $val & 63]; |
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
last if $end; |
252
|
|
|
|
|
|
|
} |
253
|
|
|
|
|
|
|
unless ($end) { |
254
|
|
|
|
|
|
|
$s[-1] = $encode_table[ $decode_table{$s[-1]} | 15 ]; |
255
|
|
|
|
|
|
|
} |
256
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
return join '', @s; |
258
|
|
|
|
|
|
|
} |
259
|
|
|
|
|
|
|
} |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
1; |
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
__END__ |