diff --git a/README.md b/README.md index cf6b03d0c6b55be1668d0747257a2df1fb2fe1c9..131076883d62d2ae036ae4897c296e106cb6040e 100644 --- a/README.md +++ b/README.md @@ -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`) diff --git a/lib/geocoder/lookup.rb b/lib/geocoder/lookup.rb index 1002f40c0e1eb8e1e3b37cfb0e0e4024022c2593..1ec88606595435af439f5dd6361b02573df6dbbf 100644 --- a/lib/geocoder/lookup.rb +++ b/lib/geocoder/lookup.rb @@ -40,6 +40,7 @@ module Geocoder :geocodio, :smarty_streets, :okf, + :postcode_anywhere_uk, :test ] end diff --git a/lib/geocoder/lookups/postcode_anywhere_uk.rb b/lib/geocoder/lookups/postcode_anywhere_uk.rb new file mode 100644 index 0000000000000000000000000000000000000000..be8fe93df265ca2339ef723513eef9e3520e9a15 --- /dev/null +++ b/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 diff --git a/lib/geocoder/results/postcode_anywhere_uk.rb b/lib/geocoder/results/postcode_anywhere_uk.rb new file mode 100644 index 0000000000000000000000000000000000000000..0ebd65db7a13e10595e772434d277f1d4b80dab1 --- /dev/null +++ b/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 diff --git a/test/fixtures/postcode_anywhere_uk_geocode_v2_00_WR26NJ b/test/fixtures/postcode_anywhere_uk_geocode_v2_00_WR26NJ new file mode 100644 index 0000000000000000000000000000000000000000..32234681622cc0d2868eef8e7d2fc3a0f18f06c8 --- /dev/null +++ b/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"}] \ No newline at end of file diff --git a/test/fixtures/postcode_anywhere_uk_geocode_v2_00_generic_error b/test/fixtures/postcode_anywhere_uk_geocode_v2_00_generic_error new file mode 100644 index 0000000000000000000000000000000000000000..4dd3044b3965b474e2a622f1503ec89783fd70c0 --- /dev/null +++ b/test/fixtures/postcode_anywhere_uk_geocode_v2_00_generic_error @@ -0,0 +1 @@ +[{"Error":"9999","Description":"A generic error","Cause":"A generic error occured.","Resolution":"Fix the unknown error."}] \ No newline at end of file diff --git a/test/fixtures/postcode_anywhere_uk_geocode_v2_00_hampshire b/test/fixtures/postcode_anywhere_uk_geocode_v2_00_hampshire new file mode 100644 index 0000000000000000000000000000000000000000..6eb2a4a60abc9255f03329b9cd4cee001bba0eaf --- /dev/null +++ b/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"}] \ No newline at end of file diff --git a/test/fixtures/postcode_anywhere_uk_geocode_v2_00_key_limit_exceeded b/test/fixtures/postcode_anywhere_uk_geocode_v2_00_key_limit_exceeded new file mode 100644 index 0000000000000000000000000000000000000000..479b8697f8ebdfefe06f6a8eb6b4731b35ef9bc7 --- /dev/null +++ b/test/fixtures/postcode_anywhere_uk_geocode_v2_00_key_limit_exceeded @@ -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."}] \ No newline at end of file diff --git a/test/fixtures/postcode_anywhere_uk_geocode_v2_00_no_results b/test/fixtures/postcode_anywhere_uk_geocode_v2_00_no_results new file mode 100644 index 0000000000000000000000000000000000000000..0637a088a01e8ddab3bf3fa98dbe804cbde1a0dc --- /dev/null +++ b/test/fixtures/postcode_anywhere_uk_geocode_v2_00_no_results @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/test/fixtures/postcode_anywhere_uk_geocode_v2_00_romsey b/test/fixtures/postcode_anywhere_uk_geocode_v2_00_romsey new file mode 100644 index 0000000000000000000000000000000000000000..340b18a5baa6b42eaff19cff32fdda1dce098f74 --- /dev/null +++ b/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"}] \ No newline at end of file diff --git a/test/fixtures/postcode_anywhere_uk_geocode_v2_00_unknown_key b/test/fixtures/postcode_anywhere_uk_geocode_v2_00_unknown_key new file mode 100644 index 0000000000000000000000000000000000000000..091c3a6347d00acf43629c20151cdf439933c6fe --- /dev/null +++ b/test/fixtures/postcode_anywhere_uk_geocode_v2_00_unknown_key @@ -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."}] \ No newline at end of file diff --git a/test/test_helper.rb b/test/test_helper.rb index e57c779ac614dc5c1d818838eea215a38acad638..b6a68f76e57241cb9d1cb7af832f6c192d0fbc4b 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -214,6 +214,17 @@ module Geocoder "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 diff --git a/test/unit/lookups/postcode_anywhere_uk_test.rb b/test/unit/lookups/postcode_anywhere_uk_test.rb new file mode 100644 index 0000000000000000000000000000000000000000..06000239d875f37cc50bfcd75672e39ab10d8822 --- /dev/null +++ b/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