From 76035e3319ddb484606aa98535281f590770e101 Mon Sep 17 00:00:00 2001 From: Andrew Stevens <astevens@tstmedia.com> Date: Fri, 22 Apr 2011 17:14:05 -0500 Subject: [PATCH] adds Bing support --- README.rdoc | 4 +- lib/geocoder.rb | 2 +- lib/geocoder/lookups/bing.rb | 29 +++++++++++ lib/geocoder/results/bing.rb | 51 +++++++++++++++++++ test/fixtures/bing_madison_square_garden.json | 40 +++++++++++++++ test/fixtures/bing_no_results.json | 16 ++++++ test/fixtures/bing_reverse.json | 42 +++++++++++++++ test/geocoder_test.rb | 17 +++++++ test/test_helper.rb | 17 +++++++ 9 files changed, 216 insertions(+), 2 deletions(-) create mode 100644 lib/geocoder/lookups/bing.rb create mode 100644 lib/geocoder/results/bing.rb create mode 100644 test/fixtures/bing_madison_square_garden.json create mode 100644 test/fixtures/bing_no_results.json create mode 100644 test/fixtures/bing_reverse.json diff --git a/README.rdoc b/README.rdoc index 4c50d882..469662e8 100644 --- a/README.rdoc +++ b/README.rdoc @@ -227,6 +227,7 @@ Street address geocoding services currently supported (valid settings for the ab * Google: <tt>:google</tt> * Yahoo: <tt>:yahoo</tt> * Geocoder.ca: <tt>:geocoder_ca</tt> (US and Canada only) +* Bing: <tt>:bing</tt> Note that the result objects returned by different geocoding services all implement the methods listed above. Beyond that, however, you must be familiar with your particular subclass of <tt>Geocoder::Result</tt> and the geocoding service's result structure: @@ -235,7 +236,7 @@ Note that the result objects returned by different geocoding services all implem * Geocoder.ca: (???) * Yandex: http://api.yandex.ru/maps/geocoder/doc/desc/concepts/response_structure.xml * FreeGeoIP: http://github.com/fiorix/freegeoip/blob/master/README.rst - +* Bing: http://msdn.microsoft.com/en-us/library/ff701715.aspx === API Keys To use your Google API key or Yahoo app ID: @@ -247,6 +248,7 @@ To obtain an API key: * Yahoo: https://developer.apps.yahoo.com/wsregapp (not required) * Google: http://code.google.com/apis/maps/signup.html (not required) * Yandex: http://api.yandex.ru/maps/intro/concepts/intro.xml#apikey (required) +* Bing: http://www.bingmapsportal.com/ === Timeout diff --git a/lib/geocoder.rb b/lib/geocoder.rb index ad090b66..a119b32e 100644 --- a/lib/geocoder.rb +++ b/lib/geocoder.rb @@ -76,7 +76,7 @@ module Geocoder # Array of valid Lookup names. # def valid_lookups - [:google, :yahoo, :geocoder_ca, :yandex, :freegeoip] + [:google, :yahoo, :geocoder_ca, :yandex, :freegeoip, :bing] end def version diff --git a/lib/geocoder/lookups/bing.rb b/lib/geocoder/lookups/bing.rb new file mode 100644 index 00000000..843a413b --- /dev/null +++ b/lib/geocoder/lookups/bing.rb @@ -0,0 +1,29 @@ +require 'geocoder/lookups/base' +require "geocoder/results/bing" + +module Geocoder::Lookup + class Bing < Base + + private # --------------------------------------------------------------- + + def results(query, reverse = false) + return [] unless doc = fetch_data(query, reverse) + + if doc['statusDescription'] == "OK" + return doc['resourceSets'].first['estimatedTotal'] > 0 ? doc['resourceSets'].first['resources'] : [] + else + warn "Bing Geocoding API error: #{doc['statusCode']} (#{doc['statusDescription']})." + return [] + end + end + + def query_url(query, reverse = false) + params = {:key => Geocoder::Configuration.api_key} + params[:query] = query unless reverse + + base_url = "http://dev.virtualearth.net/REST/v1/Locations" + url_tail = reverse ? "/#{query}?" : "?" + base_url + url_tail + hash_to_query(params) + end + end +end diff --git a/lib/geocoder/results/bing.rb b/lib/geocoder/results/bing.rb new file mode 100644 index 00000000..70b696b8 --- /dev/null +++ b/lib/geocoder/results/bing.rb @@ -0,0 +1,51 @@ +require 'geocoder/results/base' + +module Geocoder::Result + class Bing < Base + + def address(format = :full) + data_address['formattedAddress'] + end + + def city + data_address['locality'] + end + + def country + data_address['countryRegion'] + end + + def country_code + # Bing does not return a contry code + "" + end + + def postal_code + data_address['postalCode'] + end + + def coordinates + data_coordinates['coordinates'] + end + + def data_address + @data['address'] + end + + def data_coordinates + @data['point'] + end + + def address_line + data_address['addressLine'] + end + + def state + data_address['adminDistrict'] + end + + def confidence + @data['confidence'] + end + end +end diff --git a/test/fixtures/bing_madison_square_garden.json b/test/fixtures/bing_madison_square_garden.json new file mode 100644 index 00000000..36311593 --- /dev/null +++ b/test/fixtures/bing_madison_square_garden.json @@ -0,0 +1,40 @@ +{ + "authenticationResultCode":"ValidCredentials", + "brandLogoUri":"http:\/\/dev.virtualearth.net\/Branding\/logo_powered_by.png", + "copyright":"Copyright © 2011 Microsoft and its suppliers. All rights reserved. This API cannot be accessed and the content and any results may not be used, reproduced or transmitted in any manner without express written permission from Microsoft Corporation.", + "resourceSets":[ + { + "estimatedTotal":1, + "resources":[ + { + "__type":"Location:http:\/\/schemas.microsoft.com\/search\/local\/ws\/rest\/v1", + "bbox":[ + 40.744944289326668, + -74.002353921532631, + 40.755675807595253, + -73.983625397086143 + ], + "name":"Madison Square Garden, NY", + "point":{ + "type":"Point", + "coordinates":[ + 40.75031, + -73.99299 + ] + }, + "address":{ + "adminDistrict":"NY", + "countryRegion":"United States", + "formattedAddress":"Madison Square Garden, NY", + "locality":"New York" + }, + "confidence":"High", + "entityType":"Stadium" + } + ] + } + ], + "statusCode":200, + "statusDescription":"OK", + "traceId":"55094ee53c8d45e789794014666328cd|CH1M001466|02.00.82.2800|CH1MSNVM001396, CH1MSNVM001370, CH1MSNVM001397" +} \ No newline at end of file diff --git a/test/fixtures/bing_no_results.json b/test/fixtures/bing_no_results.json new file mode 100644 index 00000000..c42760e3 --- /dev/null +++ b/test/fixtures/bing_no_results.json @@ -0,0 +1,16 @@ +{ + "authenticationResultCode":"ValidCredentials", + "brandLogoUri":"http:\/\/dev.virtualearth.net\/Branding\/logo_powered_by.png", + "copyright":"Copyright © 2011 Microsoft and its suppliers. All rights reserved. This API cannot be accessed and the content and any results may not be used, reproduced or transmitted in any manner without express written permission from Microsoft Corporation.", + "resourceSets":[ + { + "estimatedTotal":0, + "resources":[ + + ] + } + ], + "statusCode":200, + "statusDescription":"OK", + "traceId":"907b76a307bc49129a489de3d4c992ea|CH1M001463|02.00.82.2800|CH1MSNVM001383, CH1MSNVM001358, CH1MSNVM001397" +} \ No newline at end of file diff --git a/test/fixtures/bing_reverse.json b/test/fixtures/bing_reverse.json new file mode 100644 index 00000000..5ea86d06 --- /dev/null +++ b/test/fixtures/bing_reverse.json @@ -0,0 +1,42 @@ +{ + "authenticationResultCode":"ValidCredentials", + "brandLogoUri":"http:\/\/dev.virtualearth.net\/Branding\/logo_powered_by.png", + "copyright":"Copyright © 2011 Microsoft and its suppliers. All rights reserved. This API cannot be accessed and the content and any results may not be used, reproduced or transmitted in any manner without express written permission from Microsoft Corporation.", + "resourceSets":[ + { + "estimatedTotal":1, + "resources":[ + { + "__type":"Location:http:\/\/schemas.microsoft.com\/search\/local\/ws\/rest\/v1", + "bbox":[ + 45.419835111675845, + -75.683656128790716, + 45.4275605468172, + -75.66898098334994 + ], + "name":"291 Rue Somerset E, Ottawa, ON, K1N", + "point":{ + "type":"Point", + "coordinates":[ + 45.423697829246521, + -75.676318556070328 + ] + }, + "address":{ + "addressLine":"291 Rue Somerset E", + "adminDistrict":"ON", + "countryRegion":"Canada", + "formattedAddress":"291 Rue Somerset E, Ottawa, ON, K1N", + "locality":"Ottawa", + "postalCode":"K1N" + }, + "confidence":"Medium", + "entityType":"Address" + } + ] + } + ], + "statusCode":200, + "statusDescription":"OK", + "traceId":"27bd5ed659e64ba6970c4144f1d4ea94|CH1M001470|02.00.82.2800|CH1MSNVM001396, CH1MSNVM001374" +} \ No newline at end of file diff --git a/test/geocoder_test.rb b/test/geocoder_test.rb index 1d18853e..d3e69d84 100644 --- a/test/geocoder_test.rb +++ b/test/geocoder_test.rb @@ -403,6 +403,23 @@ class GeocoderTest < Test::Unit::TestCase end + # --- Bing --- + + def test_bing_result_components + Geocoder::Configuration.lookup = :bing + result = Geocoder.search("Madison Square Garden, New York, NY").first + assert_equal "Madison Square Garden, NY", result.address + assert_equal "NY", result.state + assert_equal "New York", result.city + end + + def test_bing_no_results + Geocoder::Configuration.lookup = :bing + results = Geocoder.search("no results") + assert_equal 0, results.length + end + + # --- search queries --- def test_hash_to_query diff --git a/test/test_helper.rb b/test/test_helper.rb index 958fad52..d9a1463a 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -119,6 +119,23 @@ module Geocoder read_fixture "freegeoip_74_200_247_59.json" end end + + class Bing < Base + private #----------------------------------------------------------------- + def fetch_raw_data(query, reverse = false) + raise TimeoutError if query == "timeout" + if reverse + read_fixture "bing_reverse.json" + else + file = case query + when "no results"; :no_results + else :madison_square_garden + end + read_fixture "bing_#{file}.json" + end + end + end + end end -- GitLab