more geocoder stuff
[rails.git] / app / controllers / geocoder_controller.rb
1 class GeocoderController < ApplicationController
2   layout 'site'
3
4   require 'net/http'
5   require 'rexml/document'
6
7   def search
8     @postcode_arr = []
9
10     if params[:query][:postcode] 
11       postcode = params[:query][:postcode].upcase
12       escaped_postcode = postcode.sub(/\s/,'%20')
13
14       if postcode.match(/(^\d{5}$)|(^\d{5}-\d{4}$)/)
15         # Its a zip code - ask geocoder.us
16         # (They have a non commerical use api)
17         Net::HTTP.start('rpc.geocoder.us') do |http|
18           resp = http.get("/service/csv?zip=#{postcode}")
19           if resp.body.match(/couldn't find this zip/)
20             redirect_to "/index.html?error=invalid_zip_code"
21             return
22           end
23           data = resp.body.split(/, /) # lat,long,town,state,zip
24           lat = data[0] 
25           lon = data[1]
26           redirect_to "/index.html?lat=#{lat}&lon=#{lon}&zoom=14"
27           return
28         end
29       elsif postcode.match(/^([A-Z]{1,2}\d+[A-Z]?\s*\d[A-Z]{2})/)
30         # It matched our naive UK postcode regexp
31         # Ask npemap to do a combined npemap + freethepostcode search
32         Net::HTTP.start('www.npemap.org.uk') do |http|
33           resp = http.get("/cgi/geocoder.fcgi?format=text&postcode=#{escaped_postcode}")
34           dataline = resp.body.split(/\n/)[1]
35           data = dataline.split(/,/) # easting,northing,postcode,lat,long
36           lat = data[3] 
37           lon = data[4]
38           redirect_to "/index.html?lat=#{lat}&lon=#{lon}&zoom=14"
39           return
40         end
41       elsif postcode.match(/^[A-Z]\d[A-Z]\s*\d[A-Z]\d/)
42         # It's a canadian postcode
43         # Ask geocoder.ca (note - they have a per-day limit)
44         postcode = postcode.sub(/\s/,'')
45         Net::HTTP.start('geocoder.ca') do |http|
46           resp = http.get("?geoit=XML&postal=#{postcode}")
47           data_lat = resp.body.slice(/latt>.*?</)
48           data_lon = resp.body.slice(/longt>.*?</)
49           lat = data_lat.split(/[<>]/)[1]
50           lon = data_lon.split(/[<>]/)[1]
51           redirect_to "/index.html?lat=#{lat}&lon=#{lon}&zoom=14"
52           return
53         end
54       elsif postcode.match(/(GIR 0AA|[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})
55         /)
56         #its a UK postcode
57         begin
58           Net::HTTP.start('www.freethepostcode.org') do |http|
59             resp = http.get("/geocode?postcode=#{postcode}")
60             lat = resp.body.scan(/[4-6][0-9]\.?[0-9]+/)
61             lon = resp.body.scan(/[-+][0-9]\.?[0-9]+/)
62             redirect_to "/index.html?lat=#{lat}&lon=#{lon}&zoom=14"
63             return
64           end
65         rescue
66           redirect_to "/index.html"
67           #redirect to somewhere else
68         end
69         redirect_to "/index.html?lat=#{lat}&lon=#{lon}&zoom=14"
70         #redirect_to "/index.html?error=unknown_postcode_or_zip"
71       else
72         # Some other postcode / zip code
73         # Throw it at geonames, and see if they have any luck with it
74         Net::HTTP.start('ws.geonames.org') do |http|
75           resp = http.get("/postalCodeSearch?postalcode=#{escaped_postcode}&maxRows=1")
76           hits = resp.body.slice(/totalResultsCount>.*?</).split(/[<>]/)[1]
77           if hits == "0"
78             # Geonames doesn't know, it's probably wrong
79             redirect_to "/index.html?error=unknown_postcode_or_zip"
80             return
81           end
82           data_lat = resp.body.slice(/lat>.*?</)
83           data_lon = resp.body.slice(/lng>.*?</)
84           lat = data_lat.split(/[<>]/)[1]
85           lon = data_lon.split(/[<>]/)[1]
86           redirect_to "/index.html?lat=#{lat}&lon=#{lon}&zoom=14"
87         end
88       end
89     else
90       # Some other postcode / zip file
91       redirect_to "/index.html?error=unknown_postcode_or_zip"
92       return
93     end
94
95     if params[:query][:place_name]  
96       @place_name = params[:query][:place_name]
97       redirect_to :controller => 'geocoder', :action => 'results', :params => {:place_name => @place_name}
98     end 
99   end
100
101   def results
102     @place_name = params[:place_name]
103     res_hash = {}
104     @res_ary = []
105     begin
106       Net::HTTP.start('ws.geonames.org') do |http|
107         res = http.get("/search?q=#{@place_name}&maxRows=10")
108         xml = REXML::Document.new(res.body)
109         xml.elements.each("geonames/geoname") do |ele|
110           res_hash = {}
111           ele.elements.each("name"){ |n| res_hash['name'] = n.text }
112           ele.elements.each("countryCode"){ |n| res_hash['countrycode'] = n.text }
113           ele.elements.each("countryName"){ |n| res_hash['countryname'] = n.text }
114           ele.elements.each("lat"){ |n| res_hash['lat'] = n.text }
115           ele.elements.each("lng"){ |n| res_hash['lon']= n.text }
116           @res_ary << res_hash
117         end 
118       end
119     rescue
120       #Flash a notice to say that geonames is broken
121       redirect_to "/index.html"
122     end
123   end
124 end