diff --git a/README.md b/README.md index 6a9bae9ca..a5b07baa6 100644 --- a/README.md +++ b/README.md @@ -422,7 +422,7 @@ Similar to `:google`, with the following differences: #### Google Places Details (`:google_places_details`) -The [Google Places Details API](https://developers.google.com/places/documentation/details) is not, strictly speaking, a geocoding service. It accepts a Google `place_id` and returns address information, ratings and reviews. A `place_id` can be obtained from the Google Places Autocomplete API and should be passed to Geocoder as the first search argument: `Geocoder.search("ChIJhRwB-yFawokR5Phil-QQ3zM", :lookup => :google_places_details)`. +The [Google Places Details API](https://developers.google.com/places/documentation/details) is not, strictly speaking, a geocoding service. It accepts a Google `place_id` and returns address information, ratings and reviews. A `place_id` can be obtained from the Google Places Search lookup or Google Places Autocomplete API and should be passed to Geocoder as the first search argument: `Geocoder.search("ChIJhRwB-yFawokR5Phil-QQ3zM", :lookup => :google_places_details)`. * **API key**: required * **Key signup**: https://code.google.com/apis/console/ @@ -434,6 +434,11 @@ The [Google Places Details API](https://developers.google.com/places/documentati * **Terms of Service**: https://developers.google.com/places/policies * **Limitations**: "If your application displays Places API data on a page or view that does not also display a Google Map, you must show a "Powered by Google" logo with that data." +#### Google Places Search (`:google_places_search`) +The [Google Places Search API](https://developers.google.com/places/web-service/search) is the geocoding service of Google Places API. +It only returns limited location data, however it also returns corresponding `place_id` for a given location. You can use Google Place Details if you need more detailed information using obtained `place_id`. You should use this service instead of regular Google Geocoding API if your queries are ambiguos or incomplete addresses. For more comparison between this and regular Google Geocoding API, see https://maps-apis.googleblog.com/2016/11/address-geocoding-in-google-maps-apis.html +* See points in the previous Google Places Details section. + #### Bing (`:bing`) * **API key**: required (set `Geocoder.configure(:lookup => :bing, :api_key => key)`) diff --git a/lib/geocoder/lookup.rb b/lib/geocoder/lookup.rb index b5e9275a5..04a6ef44b 100644 --- a/lib/geocoder/lookup.rb +++ b/lib/geocoder/lookup.rb @@ -29,6 +29,7 @@ def street_services :google, :google_premier, :google_places_details, + :google_places_search, :bing, :geocoder_ca, :geocoder_us, diff --git a/lib/geocoder/lookups/google.rb b/lib/geocoder/lookups/google.rb index 4c8157069..4b1b3cbb6 100644 --- a/lib/geocoder/lookups/google.rb +++ b/lib/geocoder/lookups/google.rb @@ -50,13 +50,13 @@ def results(query) return doc['results'] when "OVER_QUERY_LIMIT" raise_error(Geocoder::OverQueryLimitError) || - Geocoder.log(:warn, "Google Geocoding API error: over query limit.") + Geocoder.log(:warn, "#{name} API error: over query limit.") when "REQUEST_DENIED" raise_error(Geocoder::RequestDenied) || - Geocoder.log(:warn, "Google Geocoding API error: request denied.") + Geocoder.log(:warn, "#{name} API error: request denied.") when "INVALID_REQUEST" raise_error(Geocoder::InvalidRequest) || - Geocoder.log(:warn, "Google Geocoding API error: invalid request.") + Geocoder.log(:warn, "#{name} API error: invalid request.") end return [] end diff --git a/lib/geocoder/lookups/google_places_search.rb b/lib/geocoder/lookups/google_places_search.rb new file mode 100644 index 000000000..c8761d9a1 --- /dev/null +++ b/lib/geocoder/lookups/google_places_search.rb @@ -0,0 +1,33 @@ +require "geocoder/lookups/google" +require "geocoder/results/google_places_search" + +module Geocoder + module Lookup + class GooglePlacesSearch < Google + def name + "Google Places Search" + end + + def required_api_key_parts + ["key"] + end + + def supported_protocols + [:https] + end + + def query_url(query) + "#{protocol}://maps.googleapis.com/maps/api/place/textsearch/json?#{url_query_string(query)}" + end + + private + + def query_url_google_params(query) + { + query: query.text, + language: query.language || configuration.language + } + end + end + end +end diff --git a/lib/geocoder/results/google_places_search.rb b/lib/geocoder/results/google_places_search.rb new file mode 100644 index 000000000..77e658bd2 --- /dev/null +++ b/lib/geocoder/results/google_places_search.rb @@ -0,0 +1,52 @@ +require "geocoder/results/google" + +module Geocoder + module Result + class GooglePlacesSearch < Google + + def types + @data["types"] || [] + end + + def rating + @data["rating"] + end + + def photos + @data["photos"] + end + + def city + "" + end + + def state + "" + end + + def state_code + "" + end + + def province + "" + end + + def province_code + "" + end + + def postal_code + "" + end + + def country + "" + end + + def country_code + "" + end + end + end +end diff --git a/test/fixtures/google_places_search_madison_square_garden b/test/fixtures/google_places_search_madison_square_garden new file mode 100644 index 000000000..e037ac3ea --- /dev/null +++ b/test/fixtures/google_places_search_madison_square_garden @@ -0,0 +1,42 @@ +{ + "html_attributions" : [], + "results" : [ + { + "formatted_address" : "4 Pennsylvania Plaza, New York, NY 10001, United States", + "geometry" : { + "location" : { + "lat" : 40.75050450000001, + "lng" : -73.9934387 + }, + "viewport" : { + "northeast" : { + "lat" : 40.7523981, + "lng" : -73.98975269999997 + }, + "southwest" : { + "lat" : 40.7485721, + "lng" : -73.9963951 + } + } + }, + "icon" : "https://maps.gstatic.com/mapfiles/place_api/icons/generic_business-71.png", + "id" : "55e3174d410b31da010030a7dfc0c9819027445a", + "name" : "Madison Square Garden", + "photos" : [ + { + "height" : 3904, + "html_attributions" : [ + "\u003ca href=\"https://maps.google.com/maps/contrib/117796018192827964147/photos\"\u003eAntonio Vera\u003c/a\u003e" + ], + "photo_reference" : "CoQBdwAAAJSRc-oTDFp3lkRwkyDN85h49Inw9YRC8U2sPUDeV0FSKzQNMxKfswp27o4Eh8gt1U6ZLPYES3RCP-2zJPswcVMtQOE9NxxM9Yg7SJllFYcC1GR_yjCJw6OGTP3_OCaL-gFI1_r54-04veyCc-UNtzjF836se6yQlSGd643zBy3_EhCYKtc3tY024iyO-6xPZUzzGhRcgC0A-itlFq-U2qmYbPde4gJU7Q", + "width" : 3152 + } + ], + "place_id" : "ChIJhRwB-yFawokR5Phil-QQ3zM", + "rating" : 4.5, + "reference" : "CmRRAAAAqb-Y-BFyZh54ipS97aLZCfQYVVI_l87_7HQxJAXMx3rI29XzscexUUiwt7kLbr4YeDaggMQ78coK-V_yGztNvhsGbq2OsrdR-BmVpecrNGbiE9fNDPsPGvdxKcB3SPbAEhDTgnyzjLY1p7IPh2M4L9KnGhS7ZZMAtvVKPnjoCnaWP9IzdzRC4w", + "types" : [ "stadium", "point_of_interest", "establishment" ] + } + ], + "status" : "OK" +} diff --git a/test/fixtures/google_places_search_no_results b/test/fixtures/google_places_search_no_results new file mode 100644 index 000000000..6a1ce6ac6 --- /dev/null +++ b/test/fixtures/google_places_search_no_results @@ -0,0 +1,5 @@ +{ + "html_attributions" : [], + "results" : [], + "status" : "ZERO_RESULTS" +} diff --git a/test/unit/lookups/google_places_search_test.rb b/test/unit/lookups/google_places_search_test.rb new file mode 100644 index 000000000..908c70ad7 --- /dev/null +++ b/test/unit/lookups/google_places_search_test.rb @@ -0,0 +1,50 @@ +# encoding: utf-8 +require 'test_helper' + +class GooglePlacesSearchTest < GeocoderTestCase + + def setup + Geocoder.configure(lookup: :google_places_search) + set_api_key!(:google_places_search) + end + + def test_google_places_search_result_contains_place_id + assert_equal "ChIJhRwB-yFawokR5Phil-QQ3zM", madison_square_garden.place_id + end + + def test_google_places_search_result_contains_latitude + assert_equal madison_square_garden.latitude, 40.75050450000001 + end + + def test_google_places_search_result_contains_longitude + assert_equal madison_square_garden.longitude, -73.9934387 + end + + def test_google_places_search_result_contains_rating + assert_equal 4.5, madison_square_garden.rating + end + + def test_google_places_search_result_contains_types + assert_equal madison_square_garden.types, %w(stadium point_of_interest establishment) + end + + def test_google_places_search_query_url_contains_language + url = lookup.query_url(Geocoder::Query.new("some-address", language: "de")) + assert_match(/language=de/, url) + end + + def test_google_places_search_query_url_always_uses_https + url = lookup.query_url(Geocoder::Query.new("some-address")) + assert_match(%r(^https://), url) + end + + private + + def lookup + Geocoder::Lookup::GooglePlacesSearch.new + end + + def madison_square_garden + Geocoder.search("Madison Square Garden").first + end +end diff --git a/test/unit/result_test.rb b/test/unit/result_test.rb index f33e1efa9..ecb31377d 100644 --- a/test/unit/result_test.rb +++ b/test/unit/result_test.rb @@ -26,7 +26,7 @@ def test_result_has_coords_in_reasonable_range_for_madison_square_garden def test_result_accepts_reverse_coords_in_reasonable_range_for_madison_square_garden Geocoder::Lookup.street_services.each do |l| next unless File.exist?(File.join("test", "fixtures", "#{l.to_s}_madison_square_garden")) - next if [:bing, :esri, :geocoder_ca, :geocoder_us, :geoportail_lu].include? l # Reverse fixture does not match forward + next if [:bing, :esri, :geocoder_ca, :google_places_search, :geocoder_us, :geoportail_lu].include? l # Reverse fixture does not match forward Geocoder.configure(:lookup => l) set_api_key!(l) result = Geocoder.search([40.750354, -73.993371]).first