The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package Geo::Coder::Many::Googlev3;

use strict;
use warnings;
use Carp;
use Geo::Coder::Many::Util;
use base 'Geo::Coder::Many::Generic';

=head1 NAME

Geo::Coder::Many::Googlev3 - Plugin for version 3 of the google maps geocoder

=head1 VERSION

Version 0.02

=cut

our $VERSION = '0.02';

# Requires Geo::Coder::Googlev3 0.07 or above
sub _MIN_MODULE_VERSION { return '0.07'; }

=head1 SYNOPSIS

This class wraps Geo::Coder::Googlev3 such that it can be used in
Geo::Coder::Many, by converting the results to a standard form.

Note: this module supports v3 of the Google geocoder. There is also
Geo::Coder::Google (also supported by Geo::Coder::Many) which supports
the older version 2.

=head1 METHODS

=head2 geocode

Takes a location string, geocodes it using Geo::Coder::Googlev3, and returns the
result in a form understandable to Geo::Coder::Many

=cut

# see details of Google's response format here:
# v3: http://code.google.com/apis/maps/documentation/geocoding/

sub geocode {
    my $self = shift;
    my $location = shift;
    defined $location or croak "Geo::Coder::Many::Googlev3::geocode 
                                method must be given a location.";

    my $raw = $self->{GeoCoder}->geocode( location => $location,
                                          raw      => 1,
                                        );
    # was there a response
    if (!defined($raw)){
        carp "no response from googlev3 when requesting $location";
	return;
    }

    # was response any good?
    if ($raw->{status} ne 'OK'){
        # carp $raw->{status} . " when requesting $location";
	return;
    }

    my $Response = Geo::Coder::Many::Response->new({ location => $location});

    foreach my $raw_reply ( @{$raw->{results}} ){
	my $precision = 0; # unknown

	if (defined($raw_reply->{geometry}) 
	    && defined($raw_reply->{geometry}{viewport}) ){
	    
	    my $box = $raw_reply->{geometry}{viewport};
	    # lng and lat in decimal degree format            
	    
	    $precision = 
		Geo::Coder::Many::Util::determine_precision_from_bbox({
		    'lon1' => $box->{southwest}{lng},
		    'lat1' => $box->{southwest}{lat},
		    'lon2' => $box->{northeast}{lng},
		    'lat2' => $box->{northeast}{lat},
                });

	}
	# which country?
	# need to scan the address components
	my $country = undef;
	foreach my $rh_address_component (@{$raw_reply->{address_components}}){
	    my $ra_types = $rh_address_component->{types};
	    my $p = 0;
	    my $c = 0;
	    foreach my $x (@$ra_types){
		$p = 1 if ($x eq 'political');
		$c = 1 if ($x eq 'country');
		if ($c && $p){
		    $country = $rh_address_component->{long_name};
		}
	    }
	}

	my $tmp = {
	    address   => $raw_reply->{formatted_address},
	    country   => $country,
	    latitude  => $raw_reply->{geometry}{location}{lat},
	    longitude => $raw_reply->{geometry}{location}{lng},
	    precision => $precision,
	};
	$Response->add_response( $tmp, $self->get_name());
    }
    return $Response;
}

=head2 get_name

The short name by which Geo::Coder::Many can refer to this geocoder.

=cut

sub get_name { my $self = shift; return 'googlev3 ' . $self->{GeoCoder}->VERSION; }

1;