Import original source of Geo-USCensus-Geocoding 0.01
authorMark Wells <mark@freeside.biz>
Thu, 11 Sep 2014 22:54:45 +0000 (15:54 -0700)
committerMark Wells <mark@freeside.biz>
Thu, 11 Sep 2014 22:54:45 +0000 (15:54 -0700)
.gitignore [new file with mode: 0644]
Changes [new file with mode: 0644]
Geocoding.pm [new file with mode: 0644]
Geocoding/Match.pm [new file with mode: 0644]
MANIFEST [new file with mode: 0644]
Makefile.PL [new file with mode: 0644]
README [new file with mode: 0644]
ignore.txt [new file with mode: 0644]
t/00-load.t [new file with mode: 0644]
t/01-lookup.t [new file with mode: 0644]
t/02-fail.t [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..9788afa
--- /dev/null
@@ -0,0 +1,6 @@
+blib/
+*.sw?
+Makefile
+Makefile.old
+MYMETA.yml
+pm_to_blib
diff --git a/Changes b/Changes
new file mode 100644 (file)
index 0000000..3514195
--- /dev/null
+++ b/Changes
@@ -0,0 +1,3 @@
+Revision history for Geo-USCensus-Geocoding
+
+unreleased
diff --git a/Geocoding.pm b/Geocoding.pm
new file mode 100644 (file)
index 0000000..c64c590
--- /dev/null
@@ -0,0 +1,182 @@
+package Geo::USCensus::Geocoding;
+
+use strict;
+use warnings;
+
+use LWP::UserAgent;
+use JSON;
+use URI;
+use Geo::USCensus::Geocoding::Match;
+
+=head1 NAME
+
+Geo::USCensus::Geocoding - The U.S. Census Bureau geocoding service
+
+=head1 VERSION
+
+Version 0.01
+
+=cut
+
+our $VERSION = '0.01';
+our $DEBUG = 1;
+
+=head1 SYNOPSIS
+
+    use Geo::USCensus::Geocoding;
+
+    my $request = {
+      # required fields
+      street  => '123 Main Street',
+      city    => 'San Francisco',   # city
+      state   => 'CA',              # state/province
+      zip     => '93102',           # zip/postal code
+      # optional fields
+      benchmark => 'Public_AR_ACS2013', # default is "Public_AR_Current"
+      vintage   => 'Census2010_ACS2013', # default is "Current_Current"
+    };
+    my $result = Geo::USCensus::Geocoding->query($request);
+
+    if ($result->matches) {
+      my $match = $result->match(0);
+      print $match->matchedAddress,"\n",
+            $match->coordinates->{x},',',$match->coordinates->{y},"\n",
+            $match->censustract,"\n";
+    }
+
+=head1 CLASS METHODS
+
+=head2 query HASHREF
+
+Send a request to the web service.  See
+L<http://geocoding.geo.census.gov/geocoder> for API documentation. This 
+package will always use the JSON data format and the Geographies return type.
+
+Returns an object of class Geo::USCensus::Geocoding.
+
+=cut
+
+my $ua = LWP::UserAgent->new;
+my $api_uri = 'http://geocoding.geo.census.gov/geocoder/geographies/address';
+
+sub query {
+  my $class = shift;
+  my %opt = (
+    benchmark => 'Public_AR_Current',
+    vintage   => 'Current_Current',
+  );
+  if (ref $_[0] eq 'HASH') {
+    %opt = (%opt, %{ $_[0] });
+  } else {
+    %opt = (%opt, @_);
+  }
+
+  $opt{format} = 'json';
+
+  foreach (qw(street city state zip)) {
+    die "$_ required\n" unless length($opt{$_});
+  }
+
+  my $uri = URI->new($api_uri);
+  $uri->query_form(\%opt);
+  warn "$class->query\n$uri\n\n" if $DEBUG;
+  my $http_req = HTTP::Request->new(GET => $uri->as_string);
+  my $resp = $ua->request($http_req);
+  my $self = { addr_response => $resp };
+  bless $self, $class;
+  if ( $resp->is_success ) {
+    local $@;
+    my $tree = eval { from_json($resp->content) };
+    if ($@) {
+      $self->message("Unable to parse response:\n$@");
+      return $self;
+    }
+    if (!exists $tree->{result}) {
+      $self->message("Response does not contain geocoding results.");
+      warn $self->message. "\n".$resp->content."\n\n";
+      return $self;
+    }
+    $tree = $tree->{result};
+
+    my @matches;
+    if (exists( $tree->{addressMatches} )) {
+      foreach my $am (@{ $tree->{addressMatches} }) {
+        push @matches, Geo::USCensus::Geocoding::Match->new($am);
+      }
+    } # else what? does this happen if there's no match? a proper REST 
+      # interface should throw a 404
+    $self->{matches} = \@matches;
+  } else {
+    $self->message( $resp->status_line );
+  }
+  $self;
+}
+
+=head1 METHODS
+
+=head2 message
+
+Sets/gets an explicit error status.
+
+=cut
+
+sub message {
+  my $self = shift;
+  if (@_) {
+    $self->{_message} = shift;
+  }
+  $self->{_message} || '';
+}
+
+=head2 matches
+
+Returns the number of matches found. 
+
+=cut
+
+sub matches {
+  my $self = shift;
+  $self->{matches} ? scalar @{ $self->{matches} } : 0;
+}
+
+=head2 match NUMBER
+
+Returns a specific match (starting from zero). Matches are returned 
+as L<Geo::USCensus::Geocoding::Match> objects, in the order they were 
+returned by the service.
+
+=cut
+
+sub match {
+  my $self = shift;
+  my $i = shift;
+  $self->{matches}->[$i];
+}
+
+=head1 AUTHOR
+
+Mark Wells, C<< <mark at freeside.biz> >>
+
+=head1 SUPPORT
+
+Commercial support for this module is available from Freeside Internet 
+Services:
+
+    L<http://www.freeside.biz/>
+
+=back
+
+
+=head1 LICENSE AND COPYRIGHT
+
+Copyright (C) 2014 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;
diff --git a/Geocoding/Match.pm b/Geocoding/Match.pm
new file mode 100644 (file)
index 0000000..6584203
--- /dev/null
@@ -0,0 +1,43 @@
+package Geo::USCensus::Geocoding::Match;
+
+use strict;
+
+sub new {
+  my $class = shift;
+  my $address = shift;
+  my $census = shift;
+
+  my $self = { %$address };
+  bless $self, $class;
+}
+
+sub matchedAddress {
+  my $self = shift;
+  $self->{matchedAddress};
+}
+
+sub coordinates {
+  my $self = shift;
+  $self->{coordinates};
+}
+
+sub addressComponents {
+  my $self = shift;
+  $self->{addressComponents};
+}
+
+sub geographies {
+  my $self = shift;
+  $self->{geographies};
+}
+
+sub censustract {
+  my $self = shift;
+  return '' unless $self->geographies
+                and exists($self->geographies->{'Census Tracts'})
+                and exists($self->geographies->{'Census Tracts'}->[0]);
+  my $ct = $self->geographies->{'Census Tracts'}->[0];
+  return $ct->{STATE} . $ct->{COUNTY} . $ct->{TRACT};
+}
+
+1;
diff --git a/MANIFEST b/MANIFEST
new file mode 100644 (file)
index 0000000..2c57785
--- /dev/null
+++ b/MANIFEST
@@ -0,0 +1,7 @@
+Changes
+Geocoding.pm
+Geocoding/Match.pm
+Makefile.PL
+MANIFEST                       This list of files
+README
+t/00-load.t
diff --git a/Makefile.PL b/Makefile.PL
new file mode 100644 (file)
index 0000000..fa04a9a
--- /dev/null
@@ -0,0 +1,22 @@
+use 5.006;
+use strict;
+use warnings;
+use ExtUtils::MakeMaker;
+
+WriteMakefile(
+    NAME                => 'Geo::USCensus::Geocoding',
+    AUTHOR              => q{Mark Wells <mark@freeside.biz>},
+    VERSION_FROM        => 'Geocoding.pm',
+    ABSTRACT_FROM       => 'Geocoding.pm',
+    ($ExtUtils::MakeMaker::VERSION >= 6.3002
+      ? ('LICENSE'=> 'perl')
+      : ()),
+    PL_FILES            => {},
+    PREREQ_PM => {
+        'Test::More'     => 0,
+        'XML::LibXML'    => 2,
+        'LWP::UserAgent' => 0,
+    },
+    dist                => { COMPRESS => 'gzip -9f', SUFFIX => 'gz', },
+    clean               => { FILES => 'Geo-USCensus-Geocoding-*' },
+);
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..84ea1ef
--- /dev/null
+++ b/README
@@ -0,0 +1,45 @@
+Geo-USCensus-Geocoding
+
+Interface to the U.S. Census Bureau geocoding service.
+
+INSTALLATION
+
+To install this module, run the following commands:
+
+       perl Makefile.PL
+       make
+       make test
+       make install
+
+SUPPORT AND DOCUMENTATION
+
+After installing, you can find documentation for this module with the
+perldoc command.
+
+    perldoc Geo::USCensus::Geocoding
+
+You can also look for information at:
+
+    RT, CPAN's request tracker (report bugs here)
+        http://rt.cpan.org/NoAuth/Bugs.html?Dist=Geo-USCensus-Geocoding
+
+    AnnoCPAN, Annotated CPAN documentation
+        http://annocpan.org/dist/Geo-USCensus-Geocoding
+
+    CPAN Ratings
+        http://cpanratings.perl.org/d/Geo-USCensus-Geocoding
+
+    Search CPAN
+        http://search.cpan.org/dist/Geo-USCensus-Geocoding/
+
+
+LICENSE AND COPYRIGHT
+
+Copyright (C) 2014 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.
+
diff --git a/ignore.txt b/ignore.txt
new file mode 100644 (file)
index 0000000..f08d1d7
--- /dev/null
@@ -0,0 +1,12 @@
+blib*
+Makefile
+Makefile.old
+Build
+Build.bat
+_build*
+pm_to_blib*
+*.tar.gz
+.lwpcookies
+cover_db
+pod2htm*.tmp
+Geo-USCensus-Geocoding-*
diff --git a/t/00-load.t b/t/00-load.t
new file mode 100644 (file)
index 0000000..46f282f
--- /dev/null
@@ -0,0 +1,9 @@
+#!perl -T
+
+use Test::More tests => 1;
+
+BEGIN {
+    use_ok( 'Geo::USCensus::Geocoding' ) || print "Bail out!\n";
+}
+
+diag( "Testing Geo::USCensus::Geocoding $Geo::USCensus::Geocoding, Perl $], $^X" );
diff --git a/t/01-lookup.t b/t/01-lookup.t
new file mode 100644 (file)
index 0000000..11c8b37
--- /dev/null
@@ -0,0 +1,19 @@
+#!perl -T
+
+use Test::More tests => 2;
+use Data::Dumper;
+use Geo::USCensus::Geocoding;
+
+diag( "Testing lookup of a known good address" );
+my $result = Geo::USCensus::Geocoding->query(
+  street  => '1526 H St', # the California Governor's Mansion
+  city    => 'Sacramento',
+  state   => 'CA',
+  zip     => '95814',
+);
+
+is( $result->message, '', 'error status' );
+is( $result->matches, 1, 'number of matches' );
+diag($result->match(0)->matchedAddress);
+diag('Census tract '.$result->match(0)->censustract);
+
diff --git a/t/02-fail.t b/t/02-fail.t
new file mode 100644 (file)
index 0000000..72fc6da
--- /dev/null
@@ -0,0 +1,16 @@
+#!perl -T
+
+use Test::More tests => 1;
+use Data::Dumper;
+use Geo::USCensus::Geocoding;
+
+diag( "Testing lookup of a known nonexistent address" );
+my $result = Geo::USCensus::Geocoding->query(
+  street  => '1000 Z St', # there is no Z street
+  city    => 'Sacramento',
+  state   => 'CA',
+  zip     => '95814',
+);
+
+is( $result->matches, 0, 'number of matches' );
+