File Coverage

blib/lib/Geo/Postcode.pm
Criterion Covered Total %
statement 132 146 90.4
branch 76 124 61.2
condition 33 63 52.3
subroutine 28 33 84.8
pod 21 28 75.0
total 290 394 73.6


line stmt bran cond sub pod time code
1             package Geo::Postcode;
2              
3 1     1   50438 use strict;
  1         3  
  1         44  
4 1     1   7 use vars qw($VERSION);
  1         2  
  1         67  
5              
6             use overload
7 1         6 '""' => '_as_string',
8 1     1   1858 'eq' => '_as_string';
  1         1205  
9              
10             $VERSION = '0.17';
11              
12             =head1 NAME
13              
14             Geo::Postcode - UK Postcode validation and location
15              
16             =head1 SYNOPSIS
17              
18             use Geo::Postcode;
19             my $postcode = Geo::Postcode->new('SW1 1AA');
20              
21             return unless $postcode->valid;
22             my ($n, $e) = ($postcode->gridn, $postcode->gride);
23              
24             # is the same as
25              
26             my ($n, $e) = $postcode->coordinates;
27              
28             # and alternative to
29            
30             my @location = ($postcode->lat, $postcode->long);
31            
32             # or the impatient can skip the construction step:
33            
34             my ($n, $e) = Geo::Postcode->coordinates('SW1 1AA');
35            
36             my $clean_postcode = Geo::Postcode->valid( $postcode );
37              
38             my ($unit, $sector, $district, $area) = Geo::Postcode->analyse('SW1 1AA');
39              
40             =head1 DESCRIPTION
41              
42             Geo::Postcode will accept full or partial UK postcodes, validate them against the official spec, separate them into their significant parts, translate them into map references or co-ordinates and calculate distances between them.
43              
44             It does not check whether the supplied postcode exists: only whether it is well-formed according to British Standard 7666, which you can find here:
45              
46             http://www.govtalk.gov.uk/gdsc/html/frames/PostCode.htm
47              
48             Geo::Postcode will also work with partial codes, ie areas, districts and sectors. They won't validate, but you can test them for legitimacy with a call to C, and you can still turn them into grid references.
49              
50             To work with US zipcodes, you need Geo::Postalcode instead.
51              
52             =head1 GRID REFERENCES AND DATA FILES
53              
54             Any postcode, whether fully or partly specified, can be turned into a grid reference. The Post Office calls it a centroid, and it marks the approximate centre of the area covered by the code.
55              
56             Unfortunately, and inexplicably, this information is not public domain: unless you're prepared to work at a very crude level, you have to buy location data either from the Post Office or a data shop.
57              
58             This module comes with with a basic set of publicly-available coordinates that covers nearly all the postcode districts (ie it maps the first block of the postcode but not the second).
59              
60             This means that the coordinates we return and the distances we calculate are a bit crude, being based at best on the postcode area. See the POD for Geo::Delivery::Location for how to override the standard data set something more comprehensive.
61              
62             =head1 INTERFACE
63              
64             This is a mostly vanilla OOP module, but for quick and dirty work you can skip the object construction step and call a method directly with a postcode string. It will build the necessary object behind the scenes and return the result of the operation.
65              
66             my @coordinates = Geo::Postcode->coordinates('LA23 3PA');
67             my $postcode = Geo::Postcode->valid($input->param('postcode'));
68            
69             The object will not be available for any more requests, of course.
70            
71             =head1 INTERNALS
72              
73             The main Geo::Postcode object is very simple blessed hashref. The postcode information is stored as a four-element listref in $self->{postcode}. Location information is retrieved by the separate L, which by default uses SQLite but can easily be overridden to use the database or other source of your choice. The location machinery is not loaded until it's needed, so you can validate and parse postcodes very cheaply.
74              
75             =head1 CONSTRUCTION
76              
77             =head2 new ( postcode_string, location_class )
78              
79             Constructs and returns the very simple postcode object. All other processing and loading is deferred.
80              
81             You can also pass in a couple of parameters up front, as a hashref after the postcode:
82              
83             my $postcode = Geo::Postcode->new('SW1 1AA', {
84             location_class => 'My::Location::Data::Class',
85             distance_units => 'miles',
86             })
87              
88             This list will probably grow.
89              
90             =cut
91              
92             sub new {
93 20     20 1 61 my ($class, $postcode, $parameters) = @_;
94 20   66     110 $class = ref $class || $class;
95 20         115 my $self = {
96             postcode_string => $postcode,
97             postcode => [],
98             location => undef,
99             reformatted => undef,
100             };
101 20         129 $self->{$_} = $parameters->{$_} for qw(location_class distance_units);
102 20         92 return bless $self, $class;
103             }
104              
105             =head2 postcode_string ( )
106              
107             Always returns the (uppercased) postcode string with which the object was constructed. Cannot be set after construction.
108              
109             =cut
110              
111             sub postcode_string {
112 20     20 1 149 return uc(shift->{postcode_string});
113             }
114              
115             =head2 fragments ( )
116              
117             Breaks the postcode into its significant parts, eg:
118              
119             EC1R 8DH --> | EC | 1R | 8 | DH |
120              
121             then stores the parts for later reference and returns them as a listref. Most other methods in this class call C first to get their raw material.
122              
123             =cut
124              
125             sub fragments {
126 63     63 1 94 my $self = shift;
127 63 100 66     189 return $self->{postcode} if $self->{postcode} && @{ $self->{postcode} };
  63         361  
128 20         53 my $code = $self->postcode_string;
129 20         32 my ($a, $d, $s, $u);
130 20 100       178 if ($code =~ s/ *(\d)([A-Z]{2})$//) {
    100          
131 17         45 $s = $1;
132 17         39 $u = $2;
133             } elsif ($code =~ s/ (\d)$//) {
134 1         5 $s = $1;
135             }
136 20 100       104 if ($code =~ /^([A-Z]{1,2})(\d{1,2}[A-Z]{0,1})/) {
137 18         35 $a = $1;
138 18         35 $d = $2;
139             }
140 20         143 return $self->{postcode} = [$a, $d, $s, $u];
141             }
142              
143             =head1 LOCATION
144              
145             The first call to a location-related method of Geo::Postcode will cause the location class - normally L - to be loaded along with its data file, and a location object to be associated with this postcode object. We then pass all location-related queries on to the location object.
146              
147             The accuracy of the information returned by location methods depends on the resolution of the location data file: see the POD for Geo::Postcode::Location for how to supply your own dataset instead of using the crude set that comes with this module.
148              
149             =head2 location ()
150              
151             Returns - and if necessary, creates - the location object associated with this postcode object. This operation is avoided until explicitly requested, so that simple postcode-validation can be as economical as possible. The location object does all the work of looking up map reference data, calculating distances and translating into other forms.
152              
153             =head2 location_class ()
154              
155             Sets and/or returns the full name of the class that should be called to get a location object. Calling C after a location object has been constructed will cause that object to be destroyed, so that the next call to a location-dependent method constructs a new object of the newly-specified class.
156              
157             =head2 default_location_class ()
158              
159             Returns the name of the location class we'll use if no other is specified. The default default is L, but if you're subclassing you will probably want to replace that with one of your own.
160              
161             =cut
162              
163             sub location_class {
164 7     7 1 13 my $self = shift;
165 7         12 my $class = shift;
166 7 100 66     23 if (defined $class && $class ne $self->{location_class}) {
167 1         2 $self->{location} = undef;
168 1         5 return $self->{location_class} = $class;
169             }
170 6   66     23 return $self->{location_class} ||= $self->default_location_class;
171             }
172            
173 4     4 1 13 sub default_location_class { 'Geo::Postcode::Location' }
174              
175             sub location {
176 25     25 1 368 my $self = shift;
177 25 100       157 return $self->{location} if $self->{location};
178 5         10 my $class = $self->location_class;
179 5         296 eval "require $class";
180 5 50       20 die "Failed to load location class '$class': $@" if $@;
181 5         20 return $self->{location} = $class->new($self);
182             }
183              
184             =head2 gridn () gride ()
185              
186             Return the OS grid reference coordinates of the centre of this postcode.
187              
188             =head2 gridref ()
189              
190             Return the proper OS grid reference for this postcode, in classic AA123456 style.
191              
192             =cut
193              
194 7     7 1 15 sub gridn { return shift->location->gridn(@_); }
195 7     7 1 13 sub gride { return shift->location->gride(@_); }
196 2     2 1 6 sub gridref { return shift->location->gridref(@_); }
197              
198             =head2 lat () long ()
199              
200             Return the latitude and longitude of the centre of this postcode.
201              
202             =cut
203              
204 1     1 1 3 sub lat { return shift->location->latitude(@_); }
205 1     1 1 3 sub long { return shift->location->longitude(@_); }
206              
207             =head2 placename () ward () nhsarea ()
208              
209             These return information from other fields that may or may not be present in your dataset. The default set supplied with this module doesn't have these extra fields but a set derived from the PAF normally will.
210              
211             =cut
212              
213 0     0 1 0 sub placename { return shift->location->placename(@_); }
214 0     0 1 0 sub ward { return shift->location->ward(@_); }
215 0     0 1 0 sub nhsarea { return shift->location->nhsarea(@_); }
216              
217             =head2 coordinates ()
218              
219             Return the grid reference x, y coordinates of this postcode as two separate values. The grid reference we use here are completely numerical: the usual OS prefix is omitted and an absolute coordinate value returned unless you get a stringy version from C.
220              
221             =cut
222              
223             sub coordinates {
224 0     0 1 0 my $self = shift;
225 0         0 return ($self->gridn, $self->gride);
226             }
227              
228             =head2 distance_from ( postcode object or string, unit )
229              
230             Accepts a postcode object or string, and returns the distance from here to there.
231              
232             As usual, you can call this method directly (ie without first constructing an object), or with any combination of postcode strings and objects:
233              
234             my $distance = Geo::Postcode->distance_from('LA23 3PA', 'EC1Y 8PQ');
235             my $distance = Geo::Postcode->distance_from($postcode, 'EC1Y 8PQ');
236             my $distance = Geo::Postcode->distance_from('EC1Y 8PQ', $postcode);
237              
238             Will do what you would expect, and the last two should be exactly the same. C is provided as a synonym of C to make that read more sensibly:
239              
240             my $distance = Geo::Postcode->distance_between('LA23 3PA', 'EC1Y 8PQ');
241              
242             In any of these cases you can supply an additional parameter dictating the units of distance: the options are currently 'miles', 'm' or 'km' (the default).
243              
244             my $distance = Geo::Postcode->distance_between('LA23 3PA', 'EC1Y 8PQ', 'miles');
245              
246             The same thing can be accomplished by supplying a 'distance_units' parameter at construction time or, if you don't mind acting global, by setting C<$Geo::Postcode::Location::units>.
247              
248             =cut
249              
250             sub distance_from {
251 4     4 1 12 my $self = shift;
252 4 50       13 $self = $self->new(shift) unless ref $self;
253 4         7 my $other = shift;
254 4   100     16 my $units = shift || $self->{distance_units};
255 4 100       17 $other = ref($other) ? $other : $self->new($other);
256 4         9 return $self->location->distance_from( $other, $units );
257             }
258              
259             sub distance_between {
260 1     1 0 3 return shift->distance_from(@_);
261             }
262              
263             =head2 bearing_to ( postcode objects or strings)
264              
265             Accepts a list of postcode objects and/or strings, and returns a corresponding list of the bearings from here to there, as degrees clockwise from grid North.
266              
267             =cut
268              
269             sub bearing_to {
270 1     1 1 2 my $self = shift;
271 1 50       4 $self = $self->new(@_) unless ref $self;
272 1 50       4 return $self->location->bearing_to( ref($_[0]) ? $_[0] : $self->new($_[0]) ) unless wantarray;
    50          
273 0 0       0 return map { $self->location->bearing_to( ref($_) ? $_ : $self->new($_) ) } @_;
  0         0  
274             }
275              
276             =head2 friendly_bearing_to ( postcode objects or strings)
277              
278             Accepts a list of postcode objects and/or strings, and returns a corresponding list of rough directions from here to there. 'NW', 'ESE', that sort of thing.
279              
280             print "That's " . $postcode1->distance_to($postcode2) . " km " .
281             $postcode1->friendly_bearing_to($postcode2) . " of here.";
282              
283             =cut
284              
285             sub friendly_bearing_to {
286 1     1 1 2 my $self = shift;
287 1 50       5 $self = $self->new(@_) unless ref $self;
288 1 50       4 return $self->location->friendly_bearing_to( ref($_[0]) ? $_[0] : $self->new($_[0]) ) unless wantarray;
    50          
289 0 0       0 return map { $self->location->friendly_bearing_to( ref($_) ? $_ : $self->new($_) ) } @_;
  0         0  
290             }
291              
292             =head1 VALIDATION
293              
294             Postcodes are checked against BS7666, which specifies the various kinds of sequences allowed and the characters which may appear in each position.
295              
296             =head2 valid ()
297              
298             If the postcode is well-formed and complete, this method returns true (in the useful form of the postcode itself, properly formatted). Otherwise, returns false.
299              
300             =cut
301              
302             sub valid {
303 12     12 1 5002 my $self = shift;
304 12 100       62 $self = $self->new(@_) unless ref $self;
305 12 50       33 return $self if $self->_special_case;
306 12         21 my ($a, $d, $s, $u) = @{ $self->fragments };
  12         27  
307              
308 12 50 66     129 return unless $a && defined $d && defined $s && $u;
      66        
      33        
309 10 50       33 return if length($a) > 2;
310 10 50       32 return if $a =~ /[\W\d]/;
311 10 100       40 return if $a =~ /^[QVX]/;
312 9 100       29 return if $a =~ /^.[IJZ]/;
313 8 50 66     56 return if length($a) == 1 && $d =~ /[^\dABCDEFGHJKSTUW]$/;
314 8 100 100     53 return if length($a) == 2 && $d =~ /[^\dABEHMNPRVWXY]$/;
315 7 50       17 return if length($s) > 1;
316 7 50       21 return if $s =~ /\D/;
317 7 50       18 return if length($u) != 2;
318 7 50       18 return if $u =~ /[^A-Z]/;
319 7 100       28 return if $u =~ /[CIKMOV]/;
320 5         15 return $self->_as_string;
321             }
322              
323             =head2 valid_fragment ()
324              
325             A looser check that doesn't mind incomplete postcodes. It will test that area, district or sector codes follow the rules for valid characters in that part of the postcode, and return true unless it finds anything that's not allowed.
326              
327             =cut
328              
329             sub valid_fragment {
330 3     3 1 9 my $self = shift;
331 3 50       21 $self = $self->new(@_) unless ref $self;
332 3 50       14 return 1 if $self->_special_case;
333 3         7 my ($a, $d, $s, $u) = @{ $self->fragments };
  3         11  
334            
335 3 50       14 return unless $a;
336 3 50       16 return if length($a) > 2;
337 3 50       15 return if $a =~ /[\W\d]/;
338 3 100       23 return if $a =~ /^[QVX]/;
339 2 50       12 return if $a =~ /^.[IJZ]/;
340 2 0 33     11 return 1 unless defined $d || defined $s || $u;
      33        
341            
342 2 50 33     11 return if length($a) == 1 && $d !~ /\d[\dABCDEFGHJKSTUW]?/;
343 2 50 33     31 return if length($a) == 2 && $d !~ /\d[\dABEHMNPRVWXY]?/;
344 2 100 66     27 return 1 unless defined $s || $u;
345            
346 1 50       7 return if length($s) > 1;
347 1 50       21 return if $s =~ /\D/;
348 1 50       13 return 1 unless $u;
349            
350 0 0       0 return if length($u) != 2;
351 0 0       0 return if $u =~ /[^A-Z]/;
352 0 0       0 return if $u =~ /[CIKMOV]/;
353 0         0 return 1;
354              
355             }
356              
357             =head1 SEGMENTATION
358              
359             These methods provide the various sector, area and district codes that can be derived from a full postcode, each of which identifies a larger area that encloses the postcode area.
360              
361             =head1 analyse ()
362              
363             Returns a list of all the codes present in this postcode, in descending order of specificity. So:
364              
365             Geo::Postcode->analyse('EC1Y8PQ');
366              
367             will return:
368            
369             ('EC1Y 8PQ', 'EC1Y 8', 'EC1Y', 'EC')
370            
371             which is useful mostly for dealing with situations where you don't know what resolution will be available and need to try alternatives. We do this when location-finding, so as to be able to work with data of unpredictable or variable specificity (ie we are cheap and only buy very rough data sets, but people enter exact postcodes).
372              
373             =cut
374              
375             sub analyse {
376 6     6 0 12 my $self = shift;
377 6 50       20 $self = $self->new(@_) unless ref $self;
378             return [
379 6         15 $self->unit,
380             $self->sector,
381             $self->district,
382             $self->area,
383             ];
384             }
385 0     0 0 0 sub analyze { return shift->analyse(@_); }
386              
387             =head1 area ()
388              
389             Returns the area code part of this postcode. This is the broadest area of all and is identified by the first one or two letters of the code: 'E' or 'EC' or 'LA' or whatever.
390              
391             =cut
392              
393             sub area {
394 7     7 0 9 my $self = shift;
395 7 50       15 $self = $self->new(@_) unless ref $self;
396 7         14 return $self->fragments->[0];
397             }
398              
399             =head1 district ()
400              
401             Returns the district code part of this postcode. This is also called the 'outward' part, by the post office: it consists of the first two or three characters and identifies the delivery office for this address. It will look like 'LA23' or 'EC1Y'.
402              
403             =cut
404              
405             sub district {
406 7     7 0 12 my $self = shift;
407 7 50       19 $self = $self->new(@_) unless ref $self;
408 7         8 my ($a, $d, $s, $u) = @{ $self->fragments };
  7         10  
409 7 50 33     45 return unless defined $a && defined $d;
410 7         25 return "$a$d";
411             }
412              
413             =head1 sector ()
414              
415             Returns the sector code part of this postcode. This is getting more local: it includes the first part of the code and the first digit of the second part, and is apparent used by the delivery office to sort the package. It will look something like 'EC1Y 8' or 'E1 7', and note that the space *is* meaningful. 'E1 7' and 'E17' are not the same thing.
416              
417             =cut
418              
419             sub sector {
420 8     8 0 14 my $self = shift;
421 8 100       29 $self = $self->new(@_) unless ref $self;
422 8         10 my ($a, $d, $s, $u) = @{ $self->fragments };
  8         20  
423 8 50 33     58 return unless defined $a && defined $d && defined $s;
      33        
424 8         55 return "$a$d $s";
425             }
426              
427             =head1 unit ()
428              
429             Returns the whole postcode, properly formatted (ie in caps and with a space in the right place, regardless of how it came in).
430              
431             This is similar to what you get just by stringifying the postcode object, with the important difference that unit() will only work for a well-formed postcode:
432              
433             print Geo::Postcode->unit('LA233PA'); # prints LA23 3PA
434             print Geo::Postcode->new('LA233PA'); # prints LA23 3PA
435             print Geo::Postcode->unit('LA23333'); # prints nothing
436             print Geo::Postcode->new('LA23333'); # prints LA23
437            
438             Whereas normal stringification - which calls C<_as_string> will print all the valid parts of a postcode.
439              
440             =cut
441              
442             sub unit {
443 7     7 1 11 my $self = shift;
444 7 50       19 $self = $self->new(@_) unless ref $self;
445 7         9 my ($a, $d, $s, $u) = @{ $self->fragments };
  7         15  
446 7 50 33     75 return unless defined $a && defined $d && defined $s;
      33        
447 7         32 return "$a$d $s$u";
448             }
449              
450             sub _as_string {
451 39     39   121 my $self = shift;
452 39 100       228 return $self->{reformatted} if $self->{reformatted};
453 19         27 my ($a, $d, $s, $u) = @{ $self->fragments };
  19         47  
454 19 100       61 $a = "" if not defined $a;
455 19 100       43 $d = "" if not defined $d;
456 19 100       49 $s = "" if not defined $s;
457 19 100       44 $u = "" if not defined $u;
458 19         83 return $self->{reformatted} = "$a$d $s$u";
459             }
460              
461             =head1 special_cases ()
462              
463             Returns a list of known valid but non-conformist postcodes. The only official one is 'G1R 0AA', the old girobank address, but you can override this method to extend the list.
464              
465             =cut
466              
467             sub special_cases {
468 15     15 0 36 return ('G1R 0AA');
469             }
470              
471             sub _special_case {
472 15     15   26 my $self = shift;
473 15         43 my $pc = $self->_as_string;
474 15 50 33     81 return 1 if $pc && grep { $pc eq $_ } $self->special_cases;
  15         124  
475             }
476              
477             =head1 PLANS
478              
479             The next majorish version of this module will support (but not require) the interface offered by Geo::Postalcode, so that one can be dropped into the place of the other. Some methods will not be relevant, but I'll try and keep as close a match as I can.
480              
481             =head1 AUTHOR
482              
483             William Ross, wross@cpan.org
484              
485             Development of this library is kindly supported by Amnesty International UK, who are pleased to see it distributed for public use but should not be held responsible for any shortcomings (or inadvertent copyright violations :).
486              
487             =head1 COPYRIGHT
488              
489             Copyright 2004 William Ross, spanner ltd.
490              
491             This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
492              
493             =cut
494              
495             1;
496