diff --git a/README.md b/README.md index 213c23dcc298d4a4aa2da0322a9c051ea8b27bf4..5c63e2411b37ed2506151643214a49af812579bb 100644 --- a/README.md +++ b/README.md @@ -500,6 +500,18 @@ The [Google Places Details API](https://developers.google.com/places/documentati * **Terms of Service**: http://geocoder.us/terms.shtml * **Limitations**: ? +#### Mapbox (`:mapbox`) + +* **API key**: required +* **Key signup**: https://www.mapbox.com/pricing/ +* **Quota**: 1 Geocode per request +* **Region**: US, Canada, and Europe are stable, rest of the world is closes +* **SSL support**: no +* **Languages**: English +* **Documentation**: https://www.mapbox.com/developers/api/geocoding/ +* **Terms of Service**: https://www.mapbox.com/tos/ +* **Limitations**: ? Must be displayed on a Mapbox map. Cache results for up to 30 days. + #### Mapquest (`:mapquest`) * **API key**: required diff --git a/lib/geocoder/lookup.rb b/lib/geocoder/lookup.rb index a704e570602172f1bf24b7b532ba7ac46875216e..f80d55284fdc18746a4f045f18de34b8b7eefc16 100644 --- a/lib/geocoder/lookup.rb +++ b/lib/geocoder/lookup.rb @@ -32,6 +32,7 @@ module Geocoder :geocoder_us, :yandex, :nominatim, + :mapbox, :mapquest, :opencagedata, :ovi, diff --git a/lib/geocoder/lookups/mapbox.rb b/lib/geocoder/lookups/mapbox.rb new file mode 100644 index 0000000000000000000000000000000000000000..8fa84884561b9a3a6251298bb876f3756859a251 --- /dev/null +++ b/lib/geocoder/lookups/mapbox.rb @@ -0,0 +1,47 @@ +require 'geocoder/lookups/base' +require "geocoder/results/mapbox" + +module Geocoder::Lookup + class Mapbox < Base + + def name + "Mapbox" + end + + def query_url(query) + "#{protocol}://api.mapbox.com/geocoding/v5/#{dataset}/#{url_query_string(query)}.json?access_token=#{configuration.api_key}" + end + + private # --------------------------------------------------------------- + + def results(query) + return [] unless data = fetch_data(query) + if data['features'] + sort_relevant_feature(data['features']) + elsif data['message'] =~ /Invalid\sToken/ + raise_error(Geocoder::InvalidApiKey, data['message']) + else + [] + end + end + + def url_query_string(query) + require 'cgi' unless defined?(CGI) && defined?(CGI.escape) + CGI.escape query.text.to_s + end + + def dataset + "mapbox.places" + end + + def supported_protocols + [:https] + end + + def sort_relevant_feature(features) + features.sort_by do |feature| + feature["relevance"] + end.reverse + end + end +end diff --git a/lib/geocoder/results/mapbox.rb b/lib/geocoder/results/mapbox.rb new file mode 100644 index 0000000000000000000000000000000000000000..695bbfaacc18f96fabd5824cb720423058638725 --- /dev/null +++ b/lib/geocoder/results/mapbox.rb @@ -0,0 +1,53 @@ +require 'geocoder/results/base' + +module Geocoder::Result + class Mapbox < Base + + def latitude + @latitude ||= @data["geometry"]["coordinates"].first.to_f + end + + def longitude + @longitude ||= @data["geometry"]["coordinates"].last.to_f + end + + def coordinates + [latitude, longitude] + end + + def city + @data['context'].map {|c| c['text'] if c['id'] =~ /place/ }.compact.first || '' + end + + def street + @data['properties']['address'] || '' + end + + def state + @data['context'].map {|c| c['text'] if c['id'] =~ /region/ }.compact.first || '' + end + + alias_method :state_code, :state + + def postal_code + @data['context'].map {|c| c['text'] if c['id'] =~ /postcode/ }.compact.first || '' + end + + def country + @data['context'].map {|c| c['text'] if c['id'] =~ /country/ }.compact.first || '' + end + + def country_code + country + end + + def place_name + @data['text'] || '' + end + + def address + [place_name, street, city, state, postal_code, country].reject{|s| s.length == 0 }.join(", ") + end + end +end + diff --git a/test/fixtures/mapbox_invalid_api_key b/test/fixtures/mapbox_invalid_api_key new file mode 100644 index 0000000000000000000000000000000000000000..41a8d6f4325761870ccf87ba03242ae80f1a4419 --- /dev/null +++ b/test/fixtures/mapbox_invalid_api_key @@ -0,0 +1,3 @@ +{ + "message": "Not Authorized - Invalid Token" +} diff --git a/test/fixtures/mapbox_madison_square_garden b/test/fixtures/mapbox_madison_square_garden new file mode 100644 index 0000000000000000000000000000000000000000..643ccb78d510c2ad162339eb0bc3c80ebb61b198 --- /dev/null +++ b/test/fixtures/mapbox_madison_square_garden @@ -0,0 +1,237 @@ +{ + "attribution": "NOTICE: \u00a9 2015 Mapbox and its suppliers. All rights reserved. Use of this data is subject to the Mapbox Terms of Service (https://www.mapbox.com/about/maps/). This response and the information it contains may not be retained.", + "features": [ + { + "center": [ + -73.991566, + 40.749688 + ], + "context": [ + { + "id": "neighborhood.12643", + "text": "Garment District" + }, + { + "id": "place.37501", + "text": "New York" + }, + { + "id": "postcode.573304472", + "text": "10001" + }, + { + "id": "region.628083222", + "text": "New York" + }, + { + "id": "country.4150104525", + "text": "United States" + } + ], + "geometry": { + "coordinates": [ + -73.991566, + 40.749688 + ], + "type": "Point" + }, + "id": "poi.3734776765", + "place_name": "Madison Square Garden, 4 Penn Plz, New York, New York 10001, United States", + "properties": { + "address": "4 Penn Plz", + "category": "stadium, arena", + "maki": "baseball" + }, + "relevance": 0.998, + "text": "Madison Square Garden", + "type": "Feature" + }, + { + "center": [ + -73.778678, + 41.036928 + ], + "context": [ + { + "id": "neighborhood.21227", + "text": "Ferris Avenue" + }, + { + "id": "place.59673", + "text": "White Plains" + }, + { + "id": "postcode.546749122", + "text": "10607" + }, + { + "id": "region.628083222", + "text": "New York" + }, + { + "id": "country.4150104525", + "text": "United States" + } + ], + "geometry": { + "coordinates": [ + -73.778678, + 41.036928 + ], + "type": "Point" + }, + "id": "poi.4036603621", + "place_name": "Madison Square Garden, 198 Central Ave, White Plains, New York 10607, United States", + "properties": { + "address": "198 Central Ave", + "category": null, + "maki": null + }, + "relevance": 0.996, + "text": "Madison Square Garden", + "type": "Feature" + }, + { + "center": [ + -73.983913, + 40.739413 + ], + "context": [ + { + "id": "neighborhood.21161", + "text": "Gramercy-Flatiron" + }, + { + "id": "place.37501", + "text": "New York" + }, + { + "id": "postcode.34207172", + "text": "10010" + }, + { + "id": "region.628083222", + "text": "New York" + }, + { + "id": "country.4150104525", + "text": "United States" + } + ], + "geometry": { + "coordinates": [ + -73.983913, + 40.739413 + ], + "type": "Point" + }, + "id": "poi.3013644540", + "place_name": "United States Government Postal Service, 149 E 23rd St, New York, New York 10010, United States", + "properties": { + "address": "149 E 23rd St", + "category": "government agency,", + "maki": "town-hall" + }, + "relevance": 0.748, + "text": "United States Government Postal Service", + "type": "Feature" + }, + { + "center": [ + -78.854005, + 38.426163 + ], + "context": [ + { + "id": "neighborhood.13118", + "text": "Preston Heights" + }, + { + "id": "place.22836", + "text": "Harrisonburg" + }, + { + "id": "postcode.912852583", + "text": "22801" + }, + { + "id": "region.1248462799", + "text": "Virginia" + }, + { + "id": "country.4150104525", + "text": "United States" + } + ], + "geometry": { + "coordinates": [ + -78.854005, + 38.426163 + ], + "type": "Point" + }, + "id": "poi.2867869361", + "place_name": "Madison Square Garden, 420 Neff Ave, Harrisonburg, Virginia 22801, United States", + "properties": { + "address": "420 Neff Ave", + "category": "insurance", + "maki": "commercial" + }, + "relevance": 0.74, + "text": "Madison Square Garden", + "type": "Feature" + }, + { + "center": [ + -85.384994, + 40.196998 + ], + "context": [ + { + "id": "neighborhood.2355", + "text": "Creston" + }, + { + "id": "place.36166", + "text": "Muncie" + }, + { + "id": "postcode.4261634171", + "text": "47305" + }, + { + "id": "region.3248846408", + "text": "Indiana" + }, + { + "id": "country.4150104525", + "text": "United States" + } + ], + "geometry": { + "coordinates": [ + -85.384994, + 40.196998 + ], + "type": "Point" + }, + "id": "poi.1361420161", + "place_name": "Madison Square Garden, 211 E Wysor St, Muncie, Indiana 47305, United States", + "properties": { + "address": "211 E Wysor St", + "category": "restaurant", + "maki": "restaurant" + }, + "relevance": 0.74, + "text": "Madison Square Garden", + "type": "Feature" + } + ], + "query": [ + "madison", + "square", + "garden", + "ny" + ], + "type": "FeatureCollection" +} diff --git a/test/fixtures/mapbox_no_results b/test/fixtures/mapbox_no_results new file mode 100644 index 0000000000000000000000000000000000000000..ff814f902e90d3e596bac8d9ae7534ccfe7f9e09 --- /dev/null +++ b/test/fixtures/mapbox_no_results @@ -0,0 +1,8 @@ +{ + "attribution": "NOTICE: \u00a9 2015 Mapbox and its suppliers. All rights reserved. Use of this data is subject to the Mapbox Terms of Service (https://www.mapbox.com/about/maps/). This response and the information it contains may not be retained.", + "features": [], + "query": [ + "No Results" + ], + "type": "FeatureCollection" +} diff --git a/test/unit/lookup_test.rb b/test/unit/lookup_test.rb index 5b6b4605d7d5a1e85d9341c169d3518c9e8629fc..45279ddd383923cfe3e20cee0beb3bf336d0f44c 100644 --- a/test/unit/lookup_test.rb +++ b/test/unit/lookup_test.rb @@ -24,7 +24,7 @@ class LookupTest < GeocoderTestCase 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].include? l # does not use query string + next if [:freegeoip, :maxmind_local, :telize, :pointpin, :geoip2, :maxmind_geoip2, :mapbox].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/mapbox_test.rb b/test/unit/lookups/mapbox_test.rb new file mode 100644 index 0000000000000000000000000000000000000000..2b5fb73f7f82ff8e5f3da8d62d01c7f0292e708e --- /dev/null +++ b/test/unit/lookups/mapbox_test.rb @@ -0,0 +1,33 @@ +# encoding: utf-8 +require 'test_helper' + +class MapboxTest < GeocoderTestCase + + def setup + Geocoder.configure(lookup: :mapbox) + set_api_key!(:mapbox) + end + + def test_url_contains_api_key + Geocoder.configure(mapbox: {api_key: "abc123"}) + query = Geocoder::Query.new("Leadville, CO") + assert_equal "https://api.mapbox.com/geocoding/v5/mapbox.places/Leadville%2C+CO.json?access_token=abc123", query.url + end + + def test_result_components + result = Geocoder.search("Madison Square Garden, New York, NY").first + assert_equal "10001", result.postal_code + assert_equal "Madison Square Garden, 4 Penn Plz, New York, New York, 10001, United States", result.address + end + + def test_no_results + assert_equal [], Geocoder.search("no results") + end + + def test_raises_exception_with_invalid_api_key + Geocoder.configure(always_raise: [Geocoder::InvalidApiKey]) + assert_raises Geocoder::InvalidApiKey do + Geocoder.search("invalid API key") + end + end +end