package Geo::EZLocate; use 5.006; use strict; use warnings; use Geo::EZLocate::Interfaces::Authentication::Authentication; use Geo::EZLocate::Interfaces::Geocoding::Geocoding; =head1 NAME Geo::EZLocate - TomTom EZLocate geocoding service interface =head1 VERSION Version 0.02 =cut our $VERSION = '0.02'; our $timeout = 10; # in minutes =head1 SYNOPSIS use Geo::EZLocate; my $geo = Geo::EZLocate->new(); my $result = $geo->login('myusername', 'mypassword'); $ ... =head1 METHODS =head2 login USERNAME PASSWORD Authenticates and caches a login token. Returns the result code, which is zero on success and nonzero on failure. =cut # shorthand class names my $Interfaces = 'Geo::EZLocate::Interfaces'; my $Auth = $Interfaces.'::Authentication::Authentication'; my $EZClient = $Interfaces.'::EZClient::EZClient'; my $Geocoding = $Interfaces.'::Geocoding::Geocoding'; sub new { my $class = shift; # mimic the official API my $username = shift; my $password = shift; return bless { username => $username, password => $password, @_ }, 'Geo::EZLocate'; } sub login { my $self = shift; if ( $self->{identity} and (time - $self->{authtime}) < $timeout ) { return; # no need to reauth } my ($username, $password) = @_; if ( $username ) { $self->{username} = $username; $self->{password} = $password; } # preprocess key my $key = 0; for (split(//, $self->{password})) { $key = ($key << 4) + ord($_); my $bits = $key & 0xf0000000; $bits >>= 24; $key = ($key ^ $bits); } $key = $key & 0x0fffffff; my $client = $self->{'auth'} ||= $Auth->new; my $r_requestChallenge = $client->requestChallenge({ userName => $self->{username}, minutesValid => $timeout, }); my $eid = $r_requestChallenge->get_encryptedID; # XOR $eid with key, multiply it by the magic number, XOR it back my $ans = $eid ^ $key; $ans = ($ans * 39371) % 0x3fffffff; $ans = $ans ^ $key; my $r_answerChallenge = $client->answerChallenge({ originalChallenge => $eid, encryptedResponse => $ans, }); my $id = $r_answerChallenge->get_credential; if ( $id ) { $self->{identity} = $id; $self->{authtime} = time; } return $r_answerChallenge->get_resultCode; } =head2 findAddress ADDRESS, CITY, STATE, ZIP, [OPTION => VALUE] ... Validate the specified address. The only OPTION that matters currently is 'country', which can be set to "CA" to search for addresses in Canada. =cut sub findAddress { my $self = shift; my %params; # again, imitate the positional parameters in the official SDK $params{Addr} = shift; $params{City} = shift; $params{State} = shift; $params{ZIP} = shift; my %opt = @_; my $client = $self->{geocoding} ||= $Geocoding->new; $self->login(); die "authentication failed" unless $self->{identity}; my $service = 'USA_Geo_004'; if ( lc($opt{'country'} || '') eq 'ca' ) { $service = 'CAN_Geo_001'; } my $input = { nv => [ map { +{ name => $_, value => $params{$_} } } keys %params ] }; my $r_findAddress = $client->findAddress({ 'service' => $service, 'identity' => $self->{identity}, 'input' => $input, }); my $nvs = $r_findAddress->get_result->get_mAttributes->get_nv; my $match = {}; foreach my $nv (@$nvs) { $match->{$nv->get_name} = '' . $nv->get_value; #force stringification } $match; } =head1 AUTHOR Mark Wells, C<< >> =head1 BUGS Much of the API is unsupported. The Geo::EZLocate::Interfaces::* packages have autogenerated wrappers for all of the API methods, and they work, but I haven't documented or tested most of them. This module shouldn't even be necessary, but the EZLocate SDK has a restrictive license which makes it impractical for open-source development. This is a shame, because their geocoding service is very good. =head1 SUPPORT You can find documentation for this module with the perldoc command. perldoc Geo::EZLocate This library is not supported by TomTom. Commercial support is available from Freeside Internet Services, L. =head1 LICENSE AND COPYRIGHT Copyright 2012 Mark Wells. This program is free software; you can redistribute it and/or modify it under the terms of either: the GNU General Public License as published by the Free Software Foundation; or the Artistic License. See http://dev.perl.org/licenses/ for more information. =cut 1; # End of Geo::EZLocate