Skip to content

Commit

Permalink
Merge pull request alexreisner#730 from rob-murray/add-postcode-anywh…
Browse files Browse the repository at this point in the history
…ere-uk

Add :postcode_anywhere_uk lookup.
  • Loading branch information
alexreisner committed Oct 11, 2014
2 parents 8b52f44 + 4bb41db commit 381c289
Show file tree
Hide file tree
Showing 13 changed files with 198 additions and 0 deletions.
16 changes: 16 additions & 0 deletions README.md
Expand Up @@ -607,6 +607,22 @@ Data Science Toolkit provides an API whose reponse format is like Google's but w
* **Terms of Service**: http://www.itella.fi/liitteet/palvelutjatuotteet/yhteystietopalvelut/Postinumeropalvelut-Palvelukuvausjakayttoehdot.pdf
* **Limitations**: ?


#### PostcodeAnywhere Uk (`:postcode_anywhere_uk`)

This uses the PostcodeAnywhere UK Geocode service, this will geocode any string from UK postcode, placename, point of interest or location.

* **API key**: required
* **Quota**: Dependant on service plan?
* **Region**: UK
* **SSL support**: yes
* **Languages**: English
* **Documentation**: [http://www.postcodeanywhere.co.uk/Support/WebService/Geocoding/UK/Geocode/2/](http://www.postcodeanywhere.co.uk/Support/WebService/Geocoding/UK/Geocode/2/)
* **Terms of Service**: ?
* **Limitations**: ?
* **Notes**: To use PostcodeAnywhere you must include an API key: `Geocoder.configure(:lookup => :postcode_anywhere_uk, :api_key => 'your_api_key')`.


### IP Address Services

#### FreeGeoIP (`:freegeoip`)
Expand Down
1 change: 1 addition & 0 deletions lib/geocoder/lookup.rb
Expand Up @@ -40,6 +40,7 @@ def street_services
:geocodio,
:smarty_streets,
:okf,
:postcode_anywhere_uk,
:test
]
end
Expand Down
51 changes: 51 additions & 0 deletions lib/geocoder/lookups/postcode_anywhere_uk.rb
@@ -0,0 +1,51 @@
require 'geocoder/lookups/base'
require 'geocoder/results/postcode_anywhere_uk'

module Geocoder::Lookup
class PostcodeAnywhereUk < Base
# API documentation: http://www.postcodeanywhere.co.uk/Support/WebService/Geocoding/UK/Geocode/2/
BASE_URL_GEOCODE_V2_00 = 'services.postcodeanywhere.co.uk/Geocoding/UK/Geocode/v2.00/json.ws'
DAILY_LIMIT_EXEEDED_ERROR_CODES = ['8', '17'] # api docs say these two codes are the same error
INVALID_API_KEY_ERROR_CODE = '2'

def name
'PostcodeAnywhereUk'
end

def required_api_key_parts
%w(key)
end

def query_url(query)
format('%s://%s?%s', protocol, BASE_URL_GEOCODE_V2_00, url_query_string(query))
end

private

def results(query)
response = fetch_data(query)
return [] if response.nil? || !response.is_a?(Array) || response.empty?

raise_exception_for_response(response[0]) if response[0]['Error']
response
end

def raise_exception_for_response(response)
case response['Error']
when *DAILY_LIMIT_EXEEDED_ERROR_CODES
raise_error(Geocoder::OverQueryLimitError, response['Cause']) || warn(response['Cause'])
when INVALID_API_KEY_ERROR_CODE
raise_error(Geocoder::InvalidApiKey, response['Cause']) || warn(response['Cause'])
else # anything else just raise general error with the api cause
raise_error(Geocoder::Error, response['Cause']) || warn(response['Cause'])
end
end

def query_url_params(query)
{
:location => query.sanitized_text,
:key => configuration.api_key
}.merge(super)
end
end
end
42 changes: 42 additions & 0 deletions lib/geocoder/results/postcode_anywhere_uk.rb
@@ -0,0 +1,42 @@
require 'geocoder/results/base'

module Geocoder::Result
class PostcodeAnywhereUk < Base

def coordinates
[@data['Latitude'].to_f, @data['Longitude'].to_f]
end

def blank_result
''
end
alias_method :state, :blank_result
alias_method :state_code, :blank_result
alias_method :postal_code, :blank_result

def address
@data['Location']
end

def city
# is this too big a jump to assume that the API always
# returns a City, County as the last elements?
city = @data['Location'].split(',')[-2] || blank_result
city.strip
end

def os_grid
@data['OsGrid']
end

# This is a UK only API; all results are UK specific and
# so ommitted from API response.
def country
'United Kingdom'
end

def country_code
'UK'
end
end
end
1 change: 1 addition & 0 deletions test/fixtures/postcode_anywhere_uk_geocode_v2_00_WR26NJ
@@ -0,0 +1 @@
[{"Location":"Moseley Road, Hallow, Worcester","Easting":"381676","Northing":"259425","Latitude":"52.2327","Longitude":"-2.2697","OsGrid":"SO 81676 59425","Accuracy":"Standard"}]
@@ -0,0 +1 @@
[{"Error":"9999","Description":"A generic error","Cause":"A generic error occured.","Resolution":"Fix the unknown error."}]
1 change: 1 addition & 0 deletions test/fixtures/postcode_anywhere_uk_geocode_v2_00_hampshire
@@ -0,0 +1 @@
[{"Location":"Hampshire","Easting":"448701","Northing":"126642","Latitude":"51.037","Longitude":"-1.3068","OsGrid":"SU 48701 26642","Accuracy":"Standard"}]
@@ -0,0 +1 @@
[{"Error":"8","Description":"Key daily limit exceeded","Cause":"The daily limit on the key has been exceeded.","Resolution":"Alter the daily limit on the key. Check the usage details first to see if usage is normal."}]
@@ -0,0 +1 @@
[]
1 change: 1 addition & 0 deletions test/fixtures/postcode_anywhere_uk_geocode_v2_00_romsey
@@ -0,0 +1 @@
[{"Location":"Romsey, Hampshire","Easting":"435270","Northing":"121182","Latitude":"50.9889","Longitude":"-1.4989","OsGrid":"SU 35270 21182","Accuracy":"Standard"}]
@@ -0,0 +1 @@
[{"Error":"2","Description":"Unknown key","Cause":"The key you are using to access the service was not found.","Resolution":"Please check that the key is correct. It should be in the form AA11-AA11-AA11-AA11."}]
11 changes: 11 additions & 0 deletions test/test_helper.rb
Expand Up @@ -214,6 +214,17 @@ def default_fixture_filename
"okf_kirstinmaki"
end
end

class PostcodeAnywhereUk
private
def fixture_prefix
'postcode_anywhere_uk_geocode_v2_00'
end

def default_fixture_filename
"#{fixture_prefix}_romsey"
end
end
end
end

Expand Down
70 changes: 70 additions & 0 deletions test/unit/lookups/postcode_anywhere_uk_test.rb
@@ -0,0 +1,70 @@
# encoding: utf-8
$: << File.join(File.dirname(__FILE__), '..', '..')
require 'test_helper'

class PostcodeAnywhereUkTest < GeocoderTestCase

def setup
Geocoder.configure(lookup: :postcode_anywhere_uk)
set_api_key!(:postcode_anywhere_uk)
end

def test_result_components_with_placename_search
results = Geocoder.search('Romsey')

assert_equal 1, results.size
assert_equal 'Romsey, Hampshire', results.first.address
assert_equal 'SU 35270 21182', results.first.os_grid
assert_equal [50.9889, -1.4989], results.first.coordinates
assert_equal 'Romsey', results.first.city
end

def test_result_components_with_postcode
results = Geocoder.search('WR26NJ')

assert_equal 1, results.size
assert_equal 'Moseley Road, Hallow, Worcester', results.first.address
assert_equal 'SO 81676 59425', results.first.os_grid
assert_equal [52.2327, -2.2697], results.first.coordinates
assert_equal 'Hallow', results.first.city
end

def test_result_components_with_county
results = Geocoder.search('hampshire')

assert_equal 1, results.size
assert_equal 'Hampshire', results.first.address
assert_equal 'SU 48701 26642', results.first.os_grid
assert_equal [51.037, -1.3068], results.first.coordinates
assert_equal '', results.first.city
end

def test_no_results
assert_equal [], Geocoder.search('no results')
end

def test_key_limit_exceeded_error
Geocoder.configure(always_raise: [Geocoder::OverQueryLimitError])

assert_raises Geocoder::OverQueryLimitError do
Geocoder.search('key limit exceeded')
end
end

def test_unknown_key_error
Geocoder.configure(always_raise: [Geocoder::InvalidApiKey])

assert_raises Geocoder::InvalidApiKey do
Geocoder.search('unknown key')
end
end

def test_generic_error
Geocoder.configure(always_raise: [Geocoder::Error])

exception = assert_raises(Geocoder::Error) do
Geocoder.search('generic error')
end
assert_equal 'A generic error occured.', exception.message
end
end

0 comments on commit 381c289

Please sign in to comment.