source: freethepostcode.org/html/ruby/sanity_check.rb @ 726

Last change on this file since 726 was 726, checked in by Dominic Hargreaves, 10 years ago

initial import

File size: 3.9 KB
Line 
1# Written by Paul Kustov <pavel.kustov@keble.ox.ac.uk>
2# and supplied with no licence by email, Fri, 11 Jul 2008
3
4def sanity_check(part1, part2)
5  sanity_check "#{part1} #{part2}"
6end
7
8def sanity_check(postcode, lat, lon)
9# Checks the postcode for correctness
10# Returns: Array (empty if no errors occurred, or with error strings if there was one or more errors)
11
12  postcode_zone = postcode.match(/([A-Z]+)/)
13  error_report = Array.new
14 
15  if postcode_zone.nil? then
16    error_report.push("Invalid postcode zone") 
17    return error_report
18  end
19
20  coord_check = CoordCheck.new
21
22  postcode_zone = postcode_zone[1]
23
24  error_report.push("Invalid postcode") unless postcode.match(/([A-PR-UWYZ]([0-9]{1,2}|([A-HK-Y][0-9]|[A-HK-Y][0-9]([0-9]|[ABEHMNPRV-Y]))|[0-9][A-HJKS-UW]) [0-9][ABD-HJLNP-UW-Z]{2})/)         # this regexp comes from BS7666 (http://www.govtalk.gov.uk/gdsc/schemas/bs7666-v2-0.xsd) but EXCLUDES GIR 0AA
25  error_report.push("Non-existent postcode zone") unless postcode_zone.match(/A[BL]|B[ABDHLNRST]*|C[ABFHMORTVW]|D[ADEGHLNTY]|E[CHNX]*|F[KY]|G[LUY]*|H[ADGPRSUX]|I[GMPV]|JE|K[ATWY]|L[ADELNSU]*|M[EKL]*|N[EGNPRW]*|O[LX]|P[AEHLOR]|R[GHM]|S[AEGKLMNOPRSTWY]*|T[ADFNQRSW]|UB|W[ACDFNRSV]*|YO|ZE/)         # enumerates all postcode zones from the table at http://en.wikipedia.org/wiki/List_of_postcode_areas_in_the_United_Kingdom
26  error_report.push("Incorrect co-ordinates for the given postcode zone. Please check the longitude and latitude values and signs") unless coord_check.coords_correct?(postcode_zone, lat, lon)
27  return error_report
28end
29
30class CoordCheck
31  attr_reader :zones
32 
33  def initialize
34    @zones = Hash.new
35    class << @zones
36    # extending zones[] to handle multiple indices
37    def []= (*ar)
38      val = ar.last
39      to_add = ar.take(ar.length-1)
40
41      if to_add.size > 1 then
42        to_add.each { |elem| store(elem,val) }
43      else
44        store(to_add[0],val)
45      end
46    end
47
48    end # @zones extension
49
50    # Bounds Declaration Start
51    western_hemisphere = Bounds.new(:max_lon => 0)
52    eastern_hemisphere = Bounds.new(:min_lon => 0)
53    shetland_islands = Bounds.new(:max_lat => 61) & western_hemisphere  # Example of combining 2 Bounds objects together.
54    channel_islands = Bounds.new(:max_lat => 50, :min_lon => -3, :max_lon => -2)
55    northern_ireland = Bounds.new(:max_lon => -5.3, :min_lat => 54, :max_lat => 55.4)
56    isle_of_man = Bounds.new(:min_lon => -4.9, :max_lon => -4.2, :min_lat => 54, :max_lat => 54.5)
57    # Bounds Declaration End
58
59
60    # Bounds Assignment Start
61    @zones["WD"]=Bounds.new(:max_lon => -0.1)
62    @zones["EC","WC","SW","NW", "WD","HA","UB","TW","KT","SM"]=western_hemisphere # Greater London postcodes entirely to the west of Greenwich
63#    @zones["GY", "JE"]=channel_islands
64    @zones["BT"]=northern_ireland
65    @zones["IM"]=isle_of_man
66    @zones["NR", "IP", "CO", "SS", "ME", "CT"] = eastern_hemisphere
67    # Bounds Assignment End
68
69
70  end
71
72  def coords_correct?(postcode_zone, lat, lon)
73    if !@zones.has_key? postcode_zone then
74      return Bounds.new({}).is_in?(lat, lon)
75    else
76      return @zones[postcode_zone].is_in?(lat, lon)
77    end
78     
79  end
80end
81
82class Bounds
83  attr_reader :min_lat,  :max_lat, :min_lon, :max_lon
84  def initialize(params)
85    @min_lat = params[:min_lat] || 49
86    @max_lat = params[:max_lat] || 61
87    @min_lon = params[:min_lon] || -9
88    @max_lon = params[:max_lon] || 2
89  end
90
91  def & (other)
92  # Function & combines two boundaries together, making the resulting boundary as tight as possible
93    def min(a,b)
94      return b if a.nil?
95      return a if b.nil?
96      (a < b) ? a : b
97    end
98
99    def max(a,b)
100      return b if a.nil?
101      return a if b.nil?
102      (a > b) ? a : b
103    end
104     
105    Bounds.new(:min_lat=> max(other.min_lat, @min_lat),
106               :max_lat=> min(other.max_lat, @max_lat),
107               :min_lon=> max(other.min_lon, @min_lon),
108               :max_lon=> min(other.max_lon, @max_lon))
109  end
110
111  def is_in?(lat, lon)
112    lat >= @min_lat && lat <= @max_lat && lon >= @min_lon && lon <= @max_lon
113  end
114end
Note: See TracBrowser for help on using the repository browser.