alternate address standardization method (TeleAtlas), #13763
[freeside.git] / FS / FS / geocode_cache.pm
1 package FS::geocode_cache;
2
3 use strict;
4 use vars qw($conf $DEBUG);
5 use base qw( FS::geocode_Mixin );
6 use FS::Record qw( qsearch qsearchs );
7 use FS::Conf;
8 use FS::Misc::Geo;
9
10 use Data::Dumper;
11
12 FS::UID->install_callback( sub { $conf = new FS::Conf; } );
13
14 $DEBUG = 0;
15
16 =head1 NAME
17
18 FS::geocode_cache - An address undergoing the geocode process.
19
20 =head1 SYNOPSIS
21
22   use FS::geocode_cache;
23
24   $record = FS::geocode_cache->standardize(%location_hash);
25
26 =head1 DESCRIPTION
27
28 An FS::geocode_cache object represents a street address in the process of 
29 being geocoded.  FS::geocode_cache inherits from FS::geocode_Mixin.
30
31 Most methods on this object throw an exception on error.
32
33 FS::geocode_cache has the following fields, with the same meaning as in 
34 L<FS::cust_location>:
35
36 =over 4
37
38 All other fields have the same meaning as in L<FS::cust_main> and 
39 L<FS::cust_location>:
40
41 =item address1
42
43 =item address2
44
45 =item city
46
47 =item county
48
49 =item state
50
51 =item zip
52
53 =item latitude
54
55 =item longitude
56
57 =item addr_clean
58
59 =item country
60
61 =item censustract
62
63 =item geocode
64
65 =item district
66
67 =back
68
69 =head1 METHODS
70
71 =over 4
72
73 =item new HASHREF
74
75 Creates a new cache object.  For internal use.  See C<standardize>.
76
77 =cut
78
79 # minimalist constructor
80 sub new {
81   my $class = shift;
82   my $self = {
83     company     => '',
84     address1    => '',
85     address2    => '',
86     city        => '',
87     state       => '',
88     zip         => '',
89     country     => '',
90     latitude    => '',
91     longitude   => '',
92     addr_clean  => '',
93     censustract => '',
94     @_
95   };
96   bless $self, $class;
97 }
98
99 # minimalist accessor, for compatibility with geocode_Mixin
100 sub get {
101   $_[0]->{$_[1]}
102 }
103
104 sub set {
105   $_[0]->{$_[1]} = $_[2];
106 }
107
108 sub location_hash { %{$_[0]} };
109
110 =item set_censustract
111
112 Look up the censustract, if it's not already filled in, and return it.
113 On error, sets 'error' and returns nothing.
114
115 This uses the "get_censustract_*" methods in L<FS::Misc::Geo>; currently
116 the only one is 'ffiec'.
117
118 =cut
119
120 sub set_censustract {
121   my $self = shift;
122
123   if ( $self->get('censustract') =~ /^\d{9}\.\d{2}$/ ) {
124     return $self->get('censustract');
125   }
126   my $censusyear = $conf->config('census_year');
127   return if !$censusyear;
128
129   my $method = 'ffiec';
130   # configurable censustract-only lookup goes here if it's ever needed.
131   $method = "get_censustract_$method";
132   my $censustract = eval { FS::Misc::Geo->$method($self, $censusyear) };
133   $self->set("censustract_error", $@);
134   $self->set("censustract", $censustract);
135 }
136
137 =item set_coord
138
139 Set the latitude and longitude fields if they're not already set.  Returns
140 those values, in order.
141
142 =cut
143
144 sub set_coord { # the one in geocode_Mixin will suffice
145   my $self = shift;
146   if ( !$self->get('latitude') || !$self->get('longitude') ) {
147     $self->SUPER::set_coord;
148     $self->set('coord_error', $@);
149   }
150   return $self->get('latitude'), $self->get('longitude');
151 }
152
153 =head1 CLASS METHODS
154
155 =over 4
156
157 =item standardize LOCATION
158
159 Given a location hash or L<FS::geocode_Mixin> object, standardize the 
160 address using the configured method and return an L<FS::geocode_cache> 
161 object.
162
163 The methods are the "standardize_*" functions in L<FS::Geo::Misc>.
164
165 =cut
166
167 sub standardize {
168   my $class = shift;
169   my $location = shift;
170   $location = { $location->location_hash }
171     if UNIVERSAL::can($location, 'location_hash');
172
173   local $Data::Dumper::Terse = 1;
174   warn "standardizing location:\n".Dumper($location) if $DEBUG;
175
176   my $method = $conf->config('address_standardize_method');
177
178   if ( $method ) {
179     $method = "standardize_$method";
180     my $new_location = eval { FS::Misc::Geo->$method( $location ) };
181     if ( $new_location ) {
182       $location = {
183         addr_clean => 'Y',
184         %$new_location
185         # standardize_* can return an address with addr_clean => '' if
186         # the address is somehow questionable
187       }
188     }
189     else {
190       # XXX need an option to decide what to do on error
191       $location->{'addr_clean'} = '';
192       $location->{'error'} = $@;
193     }
194     warn "result:\n".Dumper($location) if $DEBUG;
195   }
196   # else $location = $location
197   my $cache = $class->new(%$location);
198   return $cache;
199 }
200
201 =back
202
203 =head1 BUGS
204
205 =head1 SEE ALSO
206
207 L<FS::Record>, schema.html from the base documentation.
208
209 =cut
210
211 1;
212