diff --git a/README_API_GUIDE.md b/README_API_GUIDE.md index 62264ed38..066c62b66 100644 --- a/README_API_GUIDE.md +++ b/README_API_GUIDE.md @@ -466,6 +466,18 @@ Regional Street Address Lookups * **Terms of Service**: http://wiki.geoportail.lu/doku.php?id=en:mcg_1 * **Limitations**: ? +#### GetAddressUk (`:get_address__uk`) + +* **API key**: required +* **Quota**: 20/day for free through 5,000/day for £20/mo +* **Region**: UK +* **SSL support**: yes +* **Languages**: English +* **Documentation**: https://getaddress.io/Documentation +* **Terms of Service**: https://getaddress.io/#faq +* **Limitations**: No restrictions on use +* **Notes**: To use GetAddress you must include an API key: `Geocoder.configure(:lookup => :get_address_uk, :api_key => 'your_api_key')`. + ### LatLon.io (`:latlon`) * **API key**: required diff --git a/lib/geocoder/lookup.rb b/lib/geocoder/lookup.rb index bef2f3494..fce722162 100644 --- a/lib/geocoder/lookup.rb +++ b/lib/geocoder/lookup.rb @@ -68,7 +68,8 @@ def street_services :geoapify, :photon, :twogis, - :pc_miler + :pc_miler, + :get_address_uk ] end diff --git a/lib/geocoder/lookups/get_address_uk.rb b/lib/geocoder/lookups/get_address_uk.rb new file mode 100644 index 000000000..2016f046f --- /dev/null +++ b/lib/geocoder/lookups/get_address_uk.rb @@ -0,0 +1,53 @@ +require 'geocoder/lookups/base' +require 'geocoder/results/get_address_uk' + +module Geocoder::Lookup + class GetAddressUk < Base + # Documentation: https://getaddress.io/Documentation + + def name + 'GetAddressUk' + end + + private + + def base_query_url(query) + "#{protocol}://api.getaddress.io/v2/uk/#{query.to_s.split.join}?api-key=#{configuration.api_key}" + end + + def supported_protocols + [:https] + end + + def results(query) + response = fetch_data query + return [] if response.nil? || !response.is_a?(Hash) || response.empty? + if response['Message'] + raise_exception_for_response response + return [] + end + [response] + end + + def raise_exception_for_response(response) + return if response['Message'] == 'Not Found' + case response['Message'] + when 'Bad Request' + raise_error(Geocoder::InvalidRequest, response['Message']) || + Geocoder.log(:warn, 'getaddress.io error: Your postcode is not valid') + when 'Unauthorized' + raise_error(Geocoder::InvalidApiKey) || + Geocoder.log(:warn, 'Invalid getaddress.io API key.') + when 'Too Many Requests' + raise_error(Geocoder::OverQueryLimitError) || + Geocoder.log(:warn, 'getaddress.io error: You have made more requests than your allowed limit.') + when 'Internal Server Error' + raise_error(Geocoder::ServiceUnavailable) || + Geocoder.log(:warn, 'getaddress.io error: Internal Server Error.') + else # anything else just raise general error with the api message + raise_error(Geocoder::Error, response['Message']) || + Geocoder.log(:warn, response['Message']) + end + end + end +end diff --git a/lib/geocoder/results/get_address_uk.rb b/lib/geocoder/results/get_address_uk.rb new file mode 100644 index 000000000..4728518e7 --- /dev/null +++ b/lib/geocoder/results/get_address_uk.rb @@ -0,0 +1,51 @@ +require 'geocoder/results/base' + +module Geocoder::Result + class GetAddressUk < Base + + def coordinates + [@data['Latitude'].to_f, @data['Longitude'].to_f] + end + alias_method :to_coordinates, :coordinates + + def blank_result + '' + end + alias_method :postal_code, :blank_result + alias_method :os_grid, :blank_result + + def address + @data['Addresses'].first + end + + def city + city = @data['Addresses'].first.split(',')[-2] || blank_result + city.strip + end + + def state + state = @data['Addresses'].first.split(',')[-1] || blank_result + state.strip + end + alias_method :state_code, :state + + # This is a UK only API; all results are UK specific, hence omitted from API response. + def country + 'United Kingdom' + end + + def country_code + 'UK' + end + + def self.response_attributes + %w[Latitude Longitude Addresses] + end + + response_attributes.each do |a| + define_method a do + @data[a] + end + end + end +end diff --git a/test/fixtures/get_address_uk_invalid_key b/test/fixtures/get_address_uk_invalid_key new file mode 100644 index 000000000..ad5df64f5 --- /dev/null +++ b/test/fixtures/get_address_uk_invalid_key @@ -0,0 +1 @@ +{"Message":"Unauthorized"} diff --git a/test/fixtures/get_address_uk_invalid_postcode b/test/fixtures/get_address_uk_invalid_postcode new file mode 100644 index 000000000..9a419b500 --- /dev/null +++ b/test/fixtures/get_address_uk_invalid_postcode @@ -0,0 +1 @@ +{"Message":"Bad Request"} diff --git a/test/fixtures/get_address_uk_mk11df b/test/fixtures/get_address_uk_mk11df new file mode 100644 index 000000000..b2f10f951 --- /dev/null +++ b/test/fixtures/get_address_uk_mk11df @@ -0,0 +1 @@ +{"Latitude":52.00535583496094, "Longitude":-0.7367798686027527, "Addresses":["3 Denbigh Road, , , , Bletchley, Milton Keynes, Buckinghamshire", "A L D Automotives Ltd, Denbigh Road, , , Bletchley, Milton Keynes, Buckinghamshire", "Aero Tec Laboratories, A T L Technology Centre, Denbigh Road, , Bletchley, Milton Keynes, Buckinghamshire", "International Car Rental Ltd, Denbigh House, Denbigh Road, , Bletchley, Milton Keynes, Buckinghamshire"]} diff --git a/test/fixtures/get_address_uk_no_results b/test/fixtures/get_address_uk_no_results new file mode 100644 index 000000000..d574c1508 --- /dev/null +++ b/test/fixtures/get_address_uk_no_results @@ -0,0 +1 @@ +{"Message":"Not Found"} diff --git a/test/fixtures/get_address_uk_over_query_limit b/test/fixtures/get_address_uk_over_query_limit new file mode 100644 index 000000000..c206db1f7 --- /dev/null +++ b/test/fixtures/get_address_uk_over_query_limit @@ -0,0 +1 @@ +{"Message":"Too Many Requests"} diff --git a/test/fixtures/get_address_uk_server_error b/test/fixtures/get_address_uk_server_error new file mode 100644 index 000000000..f985fd693 --- /dev/null +++ b/test/fixtures/get_address_uk_server_error @@ -0,0 +1 @@ +{"Message":"Internal Server Error"} diff --git a/test/test_helper.rb b/test/test_helper.rb index cf63d39a2..1d58d1cc8 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -462,6 +462,18 @@ def default_fixture_filename end end + require 'geocoder/lookups/get_address_uk' + class GetAddressUk + private + def fixture_prefix + 'get_address_uk' + end + + def default_fixture_filename + "#{fixture_prefix}_mk11df" + end + end + require 'geocoder/lookups/latlon' class Latlon private diff --git a/test/unit/lookup_test.rb b/test/unit/lookup_test.rb index ab4dc0b5c..5610b0a56 100644 --- a/test/unit/lookup_test.rb +++ b/test/unit/lookup_test.rb @@ -32,7 +32,7 @@ def test_search_returns_empty_array_when_no_results def test_query_url_contains_values_in_params_hash Geocoder::Lookup.all_services_except_test.each do |l| - next if [:freegeoip, :maxmind_local, :telize, :pointpin, :geoip2, :maxmind_geoip2, :mapbox, :ipdata_co, :ipinfo_io, :ipapi_com, :ipregistry, :ipstack, :postcodes_io, :uk_ordnance_survey_names, :amazon_location_service, :ipbase, :ip2location_lite].include? l # does not use query string + next if [:freegeoip, :maxmind_local, :telize, :pointpin, :geoip2, :maxmind_geoip2, :mapbox, :ipdata_co, :ipinfo_io, :ipapi_com, :ipregistry, :ipstack, :postcodes_io, :uk_ordnance_survey_names, :amazon_location_service, :ipbase, :ip2location_lite, :get_address_uk].include? l # does not use query string set_api_key!(l) url = Geocoder::Lookup.get(l).query_url(Geocoder::Query.new( "test", :params => {:one_in_the_hand => "two in the bush"} diff --git a/test/unit/lookups/get_address_uk_test.rb b/test/unit/lookups/get_address_uk_test.rb new file mode 100644 index 000000000..0252f0d92 --- /dev/null +++ b/test/unit/lookups/get_address_uk_test.rb @@ -0,0 +1,54 @@ +# encoding: utf-8 +require 'test_helper' + +class GetAddressUkTest < GeocoderTestCase + + def setup + Geocoder.configure(lookup: :get_address_uk) + set_api_key!(:get_address_uk) + end + + def test_result_components_with_postcode + results = Geocoder.search('mk11df') + assert_equal 1, results.size + assert_equal '3 Denbigh Road, , , , Bletchley, Milton Keynes, Buckinghamshire', results.first.address + assert_equal [52.00535583496094, -0.7367798686027527], results.first.coordinates + assert_equal 'Milton Keynes', results.first.city + end + + def test_no_results + assert_equal [], Geocoder.search('no results') + end + + def test_invalid_key + Geocoder.configure(always_raise: [Geocoder::InvalidApiKey]) + + assert_raises Geocoder::InvalidApiKey do + Geocoder.search('invalid key') + end + end + + def test_invalid_postcode + Geocoder.configure(always_raise: [Geocoder::InvalidRequest]) + + assert_raises Geocoder::InvalidRequest do + Geocoder.search('invalid postcode') + end + end + + def test_over_query_limit + Geocoder.configure(always_raise: [Geocoder::OverQueryLimitError]) + + assert_raises Geocoder::OverQueryLimitError do + Geocoder.search('over query limit') + end + end + + def test_server_error + Geocoder.configure(always_raise: [Geocoder::ServiceUnavailable]) + + assert_raises(Geocoder::ServiceUnavailable) do + Geocoder.search('server error') + end + end +end diff --git a/test/unit/result_test.rb b/test/unit/result_test.rb index 78097abe2..1c67a4771 100644 --- a/test/unit/result_test.rb +++ b/test/unit/result_test.rb @@ -6,6 +6,7 @@ class ResultTest < GeocoderTestCase def test_forward_geocoding_result_has_required_attributes Geocoder::Lookup.all_services_except_test.each do |l| next if [ + :get_address_uk, # Doesn't search by coordinates :ip2location, # has pay-per-attribute pricing model :ip2location_io, # has pay-per-attribute pricing model :ip2location_lite, # no forward geocoding