bw-geocoder 1.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (233) hide show
  1. data/.gitignore +6 -0
  2. data/.travis.yml +31 -0
  3. data/CHANGELOG.md +377 -0
  4. data/LICENSE +20 -0
  5. data/README.md +1041 -0
  6. data/Rakefile +25 -0
  7. data/bin/geocode +5 -0
  8. data/examples/autoexpire_cache_dalli.rb +62 -0
  9. data/examples/autoexpire_cache_redis.rb +28 -0
  10. data/examples/cache_bypass.rb +48 -0
  11. data/gemfiles/Gemfile.mongoid-2.4.x +16 -0
  12. data/lib/generators/geocoder/config/config_generator.rb +14 -0
  13. data/lib/generators/geocoder/config/templates/initializer.rb +21 -0
  14. data/lib/generators/geocoder/maxmind/geolite_city_generator.rb +28 -0
  15. data/lib/generators/geocoder/maxmind/geolite_country_generator.rb +28 -0
  16. data/lib/generators/geocoder/maxmind/templates/migration/geolite_city.rb +30 -0
  17. data/lib/generators/geocoder/maxmind/templates/migration/geolite_country.rb +17 -0
  18. data/lib/geocoder/cache.rb +90 -0
  19. data/lib/geocoder/calculations.rb +428 -0
  20. data/lib/geocoder/cli.rb +121 -0
  21. data/lib/geocoder/configuration.rb +124 -0
  22. data/lib/geocoder/configuration_hash.rb +11 -0
  23. data/lib/geocoder/exceptions.rb +21 -0
  24. data/lib/geocoder/ip_address.rb +21 -0
  25. data/lib/geocoder/lookup.rb +100 -0
  26. data/lib/geocoder/lookups/baidu.rb +55 -0
  27. data/lib/geocoder/lookups/baidu_ip.rb +54 -0
  28. data/lib/geocoder/lookups/base.rb +302 -0
  29. data/lib/geocoder/lookups/bing.rb +59 -0
  30. data/lib/geocoder/lookups/dstk.rb +20 -0
  31. data/lib/geocoder/lookups/esri.rb +48 -0
  32. data/lib/geocoder/lookups/freegeoip.rb +47 -0
  33. data/lib/geocoder/lookups/geocoder_ca.rb +54 -0
  34. data/lib/geocoder/lookups/geocoder_us.rb +39 -0
  35. data/lib/geocoder/lookups/geocodio.rb +42 -0
  36. data/lib/geocoder/lookups/google.rb +67 -0
  37. data/lib/geocoder/lookups/google_places_details.rb +50 -0
  38. data/lib/geocoder/lookups/google_premier.rb +47 -0
  39. data/lib/geocoder/lookups/here.rb +62 -0
  40. data/lib/geocoder/lookups/ip_address_labs.rb +43 -0
  41. data/lib/geocoder/lookups/mapquest.rb +60 -0
  42. data/lib/geocoder/lookups/maxmind.rb +90 -0
  43. data/lib/geocoder/lookups/maxmind_local.rb +58 -0
  44. data/lib/geocoder/lookups/nominatim.rb +52 -0
  45. data/lib/geocoder/lookups/okf.rb +43 -0
  46. data/lib/geocoder/lookups/opencagedata.rb +58 -0
  47. data/lib/geocoder/lookups/ovi.rb +62 -0
  48. data/lib/geocoder/lookups/pointpin.rb +68 -0
  49. data/lib/geocoder/lookups/smarty_streets.rb +45 -0
  50. data/lib/geocoder/lookups/telize.rb +40 -0
  51. data/lib/geocoder/lookups/test.rb +44 -0
  52. data/lib/geocoder/lookups/yahoo.rb +88 -0
  53. data/lib/geocoder/lookups/yandex.rb +54 -0
  54. data/lib/geocoder/models/active_record.rb +50 -0
  55. data/lib/geocoder/models/base.rb +39 -0
  56. data/lib/geocoder/models/mongo_base.rb +64 -0
  57. data/lib/geocoder/models/mongo_mapper.rb +26 -0
  58. data/lib/geocoder/models/mongoid.rb +32 -0
  59. data/lib/geocoder/query.rb +111 -0
  60. data/lib/geocoder/railtie.rb +26 -0
  61. data/lib/geocoder/request.rb +25 -0
  62. data/lib/geocoder/results/baidu.rb +79 -0
  63. data/lib/geocoder/results/baidu_ip.rb +62 -0
  64. data/lib/geocoder/results/base.rb +67 -0
  65. data/lib/geocoder/results/bing.rb +48 -0
  66. data/lib/geocoder/results/dstk.rb +6 -0
  67. data/lib/geocoder/results/esri.rb +51 -0
  68. data/lib/geocoder/results/freegeoip.rb +45 -0
  69. data/lib/geocoder/results/geocoder_ca.rb +60 -0
  70. data/lib/geocoder/results/geocoder_us.rb +39 -0
  71. data/lib/geocoder/results/geocodio.rb +66 -0
  72. data/lib/geocoder/results/google.rb +124 -0
  73. data/lib/geocoder/results/google_places_details.rb +35 -0
  74. data/lib/geocoder/results/google_premier.rb +6 -0
  75. data/lib/geocoder/results/here.rb +62 -0
  76. data/lib/geocoder/results/ip_address_labs.rb +78 -0
  77. data/lib/geocoder/results/mapquest.rb +51 -0
  78. data/lib/geocoder/results/maxmind.rb +135 -0
  79. data/lib/geocoder/results/maxmind_local.rb +49 -0
  80. data/lib/geocoder/results/nominatim.rb +94 -0
  81. data/lib/geocoder/results/okf.rb +106 -0
  82. data/lib/geocoder/results/opencagedata.rb +82 -0
  83. data/lib/geocoder/results/ovi.rb +62 -0
  84. data/lib/geocoder/results/pointpin.rb +44 -0
  85. data/lib/geocoder/results/smarty_streets.rb +106 -0
  86. data/lib/geocoder/results/telize.rb +45 -0
  87. data/lib/geocoder/results/test.rb +33 -0
  88. data/lib/geocoder/results/yahoo.rb +55 -0
  89. data/lib/geocoder/results/yandex.rb +84 -0
  90. data/lib/geocoder/sql.rb +107 -0
  91. data/lib/geocoder/stores/active_record.rb +278 -0
  92. data/lib/geocoder/stores/base.rb +127 -0
  93. data/lib/geocoder/stores/mongo_base.rb +89 -0
  94. data/lib/geocoder/stores/mongo_mapper.rb +13 -0
  95. data/lib/geocoder/stores/mongoid.rb +13 -0
  96. data/lib/geocoder/version.rb +3 -0
  97. data/lib/geocoder.rb +47 -0
  98. data/lib/hash_recursive_merge.rb +74 -0
  99. data/lib/maxmind_database.rb +109 -0
  100. data/lib/oauth_util.rb +112 -0
  101. data/lib/tasks/geocoder.rake +29 -0
  102. data/lib/tasks/maxmind.rake +73 -0
  103. data/test/fixtures/baidu_invalid_key +1 -0
  104. data/test/fixtures/baidu_ip_202_198_16_3 +19 -0
  105. data/test/fixtures/baidu_ip_invalid_key +1 -0
  106. data/test/fixtures/baidu_ip_no_results +1 -0
  107. data/test/fixtures/baidu_no_results +1 -0
  108. data/test/fixtures/baidu_reverse +1 -0
  109. data/test/fixtures/baidu_shanghai_pearl_tower +12 -0
  110. data/test/fixtures/bing_invalid_key +1 -0
  111. data/test/fixtures/bing_madison_square_garden +40 -0
  112. data/test/fixtures/bing_no_results +16 -0
  113. data/test/fixtures/bing_reverse +42 -0
  114. data/test/fixtures/cloudmade_invalid_key +1 -0
  115. data/test/fixtures/cloudmade_madison_square_garden +1 -0
  116. data/test/fixtures/cloudmade_no_results +1 -0
  117. data/test/fixtures/esri_madison_square_garden +59 -0
  118. data/test/fixtures/esri_no_results +8 -0
  119. data/test/fixtures/esri_reverse +21 -0
  120. data/test/fixtures/freegeoip_74_200_247_59 +12 -0
  121. data/test/fixtures/freegeoip_no_results +1 -0
  122. data/test/fixtures/geocoder_ca_madison_square_garden +1 -0
  123. data/test/fixtures/geocoder_ca_no_results +1 -0
  124. data/test/fixtures/geocoder_ca_reverse +34 -0
  125. data/test/fixtures/geocoder_us_madison_square_garden +1 -0
  126. data/test/fixtures/geocoder_us_no_results +1 -0
  127. data/test/fixtures/geocodio_1101_pennsylvania_ave +1 -0
  128. data/test/fixtures/geocodio_bad_api_key +3 -0
  129. data/test/fixtures/geocodio_invalid +4 -0
  130. data/test/fixtures/geocodio_no_results +1 -0
  131. data/test/fixtures/geocodio_over_query_limit +4 -0
  132. data/test/fixtures/google_garbage +456 -0
  133. data/test/fixtures/google_madison_square_garden +57 -0
  134. data/test/fixtures/google_no_city_data +44 -0
  135. data/test/fixtures/google_no_locality +51 -0
  136. data/test/fixtures/google_no_results +4 -0
  137. data/test/fixtures/google_over_limit +4 -0
  138. data/test/fixtures/google_places_details_invalid_request +4 -0
  139. data/test/fixtures/google_places_details_madison_square_garden +120 -0
  140. data/test/fixtures/google_places_details_no_results +4 -0
  141. data/test/fixtures/google_places_details_no_reviews +60 -0
  142. data/test/fixtures/google_places_details_no_types +66 -0
  143. data/test/fixtures/here_madison_square_garden +72 -0
  144. data/test/fixtures/here_no_results +8 -0
  145. data/test/fixtures/mapquest_error +16 -0
  146. data/test/fixtures/mapquest_invalid_api_key +16 -0
  147. data/test/fixtures/mapquest_invalid_request +16 -0
  148. data/test/fixtures/mapquest_madison_square_garden +52 -0
  149. data/test/fixtures/mapquest_no_results +16 -0
  150. data/test/fixtures/maxmind_24_24_24_21 +1 -0
  151. data/test/fixtures/maxmind_24_24_24_22 +1 -0
  152. data/test/fixtures/maxmind_24_24_24_23 +1 -0
  153. data/test/fixtures/maxmind_24_24_24_24 +1 -0
  154. data/test/fixtures/maxmind_74_200_247_59 +1 -0
  155. data/test/fixtures/maxmind_invalid_key +1 -0
  156. data/test/fixtures/maxmind_no_results +1 -0
  157. data/test/fixtures/nominatim_madison_square_garden +150 -0
  158. data/test/fixtures/nominatim_no_results +1 -0
  159. data/test/fixtures/nominatim_over_limit +1 -0
  160. data/test/fixtures/okf_kirstinmaki +67 -0
  161. data/test/fixtures/okf_no_results +4 -0
  162. data/test/fixtures/opencagedata_invalid_api_key +25 -0
  163. data/test/fixtures/opencagedata_invalid_request +26 -0
  164. data/test/fixtures/opencagedata_madison_square_garden +73 -0
  165. data/test/fixtures/opencagedata_no_results +29 -0
  166. data/test/fixtures/opencagedata_over_limit +31 -0
  167. data/test/fixtures/ovi_madison_square_garden +72 -0
  168. data/test/fixtures/ovi_no_results +8 -0
  169. data/test/fixtures/pointpin_10_10_10_10 +1 -0
  170. data/test/fixtures/pointpin_555_555_555_555 +1 -0
  171. data/test/fixtures/pointpin_80_111_555_555 +1 -0
  172. data/test/fixtures/pointpin_no_results +1 -0
  173. data/test/fixtures/smarty_streets_11211 +1 -0
  174. data/test/fixtures/smarty_streets_madison_square_garden +47 -0
  175. data/test/fixtures/smarty_streets_no_results +1 -0
  176. data/test/fixtures/telize_10_10_10_10 +1 -0
  177. data/test/fixtures/telize_555_555_555_555 +4 -0
  178. data/test/fixtures/telize_74_200_247_59 +1 -0
  179. data/test/fixtures/telize_no_results +1 -0
  180. data/test/fixtures/yahoo_error +1 -0
  181. data/test/fixtures/yahoo_invalid_key +2 -0
  182. data/test/fixtures/yahoo_madison_square_garden +52 -0
  183. data/test/fixtures/yahoo_no_results +10 -0
  184. data/test/fixtures/yahoo_over_limit +2 -0
  185. data/test/fixtures/yandex_canada_rue_dupuis_14 +446 -0
  186. data/test/fixtures/yandex_invalid_key +1 -0
  187. data/test/fixtures/yandex_kremlin +48 -0
  188. data/test/fixtures/yandex_new_york +1 -0
  189. data/test/fixtures/yandex_no_city_and_town +112 -0
  190. data/test/fixtures/yandex_no_results +16 -0
  191. data/test/integration/http_client_test.rb +31 -0
  192. data/test/mongoid_test_helper.rb +43 -0
  193. data/test/test_helper.rb +386 -0
  194. data/test/unit/active_record_test.rb +16 -0
  195. data/test/unit/cache_test.rb +37 -0
  196. data/test/unit/calculations_test.rb +220 -0
  197. data/test/unit/configuration_test.rb +55 -0
  198. data/test/unit/error_handling_test.rb +56 -0
  199. data/test/unit/geocoder_test.rb +78 -0
  200. data/test/unit/https_test.rb +17 -0
  201. data/test/unit/ip_address_test.rb +27 -0
  202. data/test/unit/lookup_test.rb +153 -0
  203. data/test/unit/lookups/bing_test.rb +68 -0
  204. data/test/unit/lookups/dstk_test.rb +26 -0
  205. data/test/unit/lookups/esri_test.rb +48 -0
  206. data/test/unit/lookups/freegeoip_test.rb +27 -0
  207. data/test/unit/lookups/geocoder_ca_test.rb +17 -0
  208. data/test/unit/lookups/geocodio_test.rb +55 -0
  209. data/test/unit/lookups/google_places_details_test.rb +122 -0
  210. data/test/unit/lookups/google_premier_test.rb +22 -0
  211. data/test/unit/lookups/google_test.rb +84 -0
  212. data/test/unit/lookups/mapquest_test.rb +60 -0
  213. data/test/unit/lookups/maxmind_local_test.rb +28 -0
  214. data/test/unit/lookups/maxmind_test.rb +63 -0
  215. data/test/unit/lookups/nominatim_test.rb +31 -0
  216. data/test/unit/lookups/okf_test.rb +38 -0
  217. data/test/unit/lookups/opencagedata_test.rb +64 -0
  218. data/test/unit/lookups/pointpin_test.rb +30 -0
  219. data/test/unit/lookups/smarty_streets_test.rb +71 -0
  220. data/test/unit/lookups/telize_test.rb +36 -0
  221. data/test/unit/lookups/yahoo_test.rb +35 -0
  222. data/test/unit/method_aliases_test.rb +26 -0
  223. data/test/unit/model_test.rb +38 -0
  224. data/test/unit/mongoid_test.rb +47 -0
  225. data/test/unit/near_test.rb +87 -0
  226. data/test/unit/oauth_util_test.rb +31 -0
  227. data/test/unit/proxy_test.rb +37 -0
  228. data/test/unit/query_test.rb +52 -0
  229. data/test/unit/rake_task_test.rb +21 -0
  230. data/test/unit/request_test.rb +35 -0
  231. data/test/unit/result_test.rb +72 -0
  232. data/test/unit/test_mode_test.rb +70 -0
  233. metadata +281 -0
@@ -0,0 +1,54 @@
1
+ require 'geocoder/lookups/base'
2
+ require "geocoder/results/geocoder_ca"
3
+
4
+ module Geocoder::Lookup
5
+ class GeocoderCa < Base
6
+
7
+ def name
8
+ "Geocoder.ca"
9
+ end
10
+
11
+ def query_url(query)
12
+ "#{protocol}://geocoder.ca/?" + url_query_string(query)
13
+ end
14
+
15
+ private # ---------------------------------------------------------------
16
+
17
+ def results(query)
18
+ return [] unless doc = fetch_data(query)
19
+ if doc['error'].nil?
20
+ return [doc]
21
+ elsif doc['error']['code'] == "005"
22
+ # "Postal Code is not in the proper Format" => no results, just shut up
23
+ else
24
+ warn "Geocoder.ca service error: #{doc['error']['code']} (#{doc['error']['description']})."
25
+ end
26
+ return []
27
+ end
28
+
29
+ def query_url_params(query)
30
+ params = {
31
+ :geoit => "xml",
32
+ :jsonp => 1,
33
+ :callback => "test",
34
+ :auth => configuration.api_key
35
+ }.merge(super)
36
+ if query.reverse_geocode?
37
+ lat,lon = query.coordinates
38
+ params[:latt] = lat
39
+ params[:longt] = lon
40
+ params[:corner] = 1
41
+ params[:reverse] = 1
42
+ else
43
+ params[:locate] = query.sanitized_text
44
+ params[:showpostal] = 1
45
+ end
46
+ params
47
+ end
48
+
49
+ def parse_raw_data(raw_data)
50
+ super raw_data[/^test\((.*)\)\;\s*$/, 1]
51
+ end
52
+ end
53
+ end
54
+
@@ -0,0 +1,39 @@
1
+ require 'geocoder/lookups/base'
2
+ require "geocoder/results/geocoder_us"
3
+
4
+ module Geocoder::Lookup
5
+ class GeocoderUs < Base
6
+
7
+ def name
8
+ "Geocoder.us"
9
+ end
10
+
11
+ def query_url(query)
12
+ if configuration.api_key
13
+ "http://#{configuration.api_key}@geocoder.us/member/service/csv/geocode?" + url_query_string(query)
14
+ else
15
+ "http://geocoder.us/service/csv/geocode?" + url_query_string(query)
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def results(query)
22
+ return [] unless doc = fetch_data(query)
23
+ if doc[0].to_s =~ /^(\d+)\:/
24
+ return []
25
+ else
26
+ return [doc.size == 5 ? ((doc[0..1] << nil) + doc[2..4]) : doc]
27
+ end
28
+ end
29
+
30
+ def query_url_params(query)
31
+ (query.text =~ /^\d{5}(?:-\d{4})?$/ ? {:zip => query} : {:address => query.sanitized_text}).merge(super)
32
+ end
33
+
34
+ def parse_raw_data(raw_data)
35
+ raw_data.chomp.split(',')
36
+ end
37
+ end
38
+ end
39
+
@@ -0,0 +1,42 @@
1
+ require 'geocoder/lookups/base'
2
+ require "geocoder/results/geocodio"
3
+
4
+ module Geocoder::Lookup
5
+ class Geocodio < Base
6
+
7
+ def name
8
+ "Geocodio"
9
+ end
10
+
11
+ def query_url(query)
12
+ path = query.reverse_geocode? ? "reverse" : "geocode"
13
+ "#{protocol}://api.geocod.io/v1/#{path}?#{url_query_string(query)}"
14
+ end
15
+
16
+ def results(query)
17
+ return [] unless doc = fetch_data(query)
18
+ return doc["results"] if doc['error'].nil?
19
+
20
+ if doc['error'] == 'Invalid API key'
21
+ raise_error(Geocoder::InvalidApiKey) ||
22
+ warn("Geocodio service error: invalid API key.")
23
+ elsif doc['error'].match(/You have reached your daily maximum/)
24
+ raise_error(Geocoder::OverQueryLimitError, doc['error']) ||
25
+ warn("Geocodio service error: #{doc['error']}.")
26
+ else
27
+ raise_error(Geocoder::InvalidRequest, doc['error']) ||
28
+ warn("Geocodio service error: #{doc['error']}.")
29
+ end
30
+ []
31
+ end
32
+
33
+ private # ---------------------------------------------------------------
34
+
35
+ def query_url_params(query)
36
+ {
37
+ :api_key => configuration.api_key,
38
+ :q => query.sanitized_text
39
+ }.merge(super)
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,67 @@
1
+ require 'geocoder/lookups/base'
2
+ require "geocoder/results/google"
3
+
4
+ module Geocoder::Lookup
5
+ class Google < Base
6
+
7
+ def name
8
+ "Google"
9
+ end
10
+
11
+ def map_link_url(coordinates)
12
+ "http://maps.google.com/maps?q=#{coordinates.join(',')}"
13
+ end
14
+
15
+ def query_url(query)
16
+ "#{protocol}://maps.googleapis.com/maps/api/geocode/json?" + url_query_string(query)
17
+ end
18
+
19
+ private # ---------------------------------------------------------------
20
+
21
+ def valid_response?(response)
22
+ status = parse_json(response.body)["status"]
23
+ super(response) and ['OK', 'ZERO_RESULTS'].include?(status)
24
+ end
25
+
26
+ def results(query)
27
+ return [] unless doc = fetch_data(query)
28
+ case doc['status']; when "OK" # OK status implies >0 results
29
+ return doc['results']
30
+ when "OVER_QUERY_LIMIT"
31
+ raise_error(Geocoder::OverQueryLimitError) ||
32
+ warn("Google Geocoding API error: over query limit.")
33
+ when "REQUEST_DENIED"
34
+ raise_error(Geocoder::RequestDenied) ||
35
+ warn("Google Geocoding API error: request denied.")
36
+ when "INVALID_REQUEST"
37
+ raise_error(Geocoder::InvalidRequest) ||
38
+ warn("Google Geocoding API error: invalid request.")
39
+ end
40
+ return []
41
+ end
42
+
43
+ def query_url_google_params(query)
44
+ params = {
45
+ (query.reverse_geocode? ? :latlng : :address) => query.sanitized_text,
46
+ :sensor => "false",
47
+ :language => (query.language || configuration.language)
48
+ }
49
+ unless (bounds = query.options[:bounds]).nil?
50
+ params[:bounds] = bounds.map{ |point| "%f,%f" % point }.join('|')
51
+ end
52
+ unless (region = query.options[:region]).nil?
53
+ params[:region] = region
54
+ end
55
+ unless (components = query.options[:components]).nil?
56
+ params[:components] = components.is_a?(Array) ? components.join("|") : components
57
+ end
58
+ params
59
+ end
60
+
61
+ def query_url_params(query)
62
+ query_url_google_params(query).merge(
63
+ :key => configuration.api_key
64
+ ).merge(super)
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,50 @@
1
+ require "geocoder/lookups/google"
2
+ require "geocoder/results/google_places_details"
3
+
4
+ module Geocoder
5
+ module Lookup
6
+ class GooglePlacesDetails < Google
7
+ def name
8
+ "Google Places Details"
9
+ end
10
+
11
+ def required_api_key_parts
12
+ ["key"]
13
+ end
14
+
15
+ def use_ssl?
16
+ true
17
+ end
18
+
19
+ def query_url(query)
20
+ "#{protocol}://maps.googleapis.com/maps/api/place/details/json?#{url_query_string(query)}"
21
+ end
22
+
23
+ private
24
+
25
+ def results(query)
26
+ return [] unless doc = fetch_data(query)
27
+
28
+ case doc["status"]
29
+ when "OK"
30
+ return [doc["result"]]
31
+ when "OVER_QUERY_LIMIT"
32
+ raise_error(Geocoder::OverQueryLimitError) || warn("Google Places Details API error: over query limit.")
33
+ when "REQUEST_DENIED"
34
+ raise_error(Geocoder::RequestDenied) || warn("Google Places Details API error: request denied.")
35
+ when "INVALID_REQUEST"
36
+ raise_error(Geocoder::InvalidRequest) || warn("Google Places Details API error: invalid request.")
37
+ end
38
+
39
+ []
40
+ end
41
+
42
+ def query_url_google_params(query)
43
+ {
44
+ placeid: query.text,
45
+ language: query.language || configuration.language
46
+ }
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,47 @@
1
+ require 'openssl'
2
+ require 'base64'
3
+ require 'geocoder/lookups/google'
4
+ require 'geocoder/results/google_premier'
5
+
6
+ module Geocoder::Lookup
7
+ class GooglePremier < Google
8
+
9
+ def name
10
+ "Google Premier"
11
+ end
12
+
13
+ def required_api_key_parts
14
+ ["private key", "client", "channel"]
15
+ end
16
+
17
+ def query_url(query)
18
+ path = "/maps/api/geocode/json?" + url_query_string(query)
19
+ "#{protocol}://maps.googleapis.com#{path}&signature=#{sign(path)}"
20
+ end
21
+
22
+ private # ---------------------------------------------------------------
23
+
24
+ def query_url_params(query)
25
+ query_url_google_params(query).merge(super).merge(
26
+ :key => nil, # don't use param inherited from Google lookup
27
+ :client => configuration.api_key[1],
28
+ :channel => configuration.api_key[2]
29
+ )
30
+ end
31
+
32
+ def sign(string)
33
+ raw_private_key = url_safe_base64_decode(configuration.api_key[0])
34
+ digest = OpenSSL::Digest.new('sha1')
35
+ raw_signature = OpenSSL::HMAC.digest(digest, raw_private_key, string)
36
+ url_safe_base64_encode(raw_signature)
37
+ end
38
+
39
+ def url_safe_base64_decode(base64_string)
40
+ Base64.decode64(base64_string.tr('-_', '+/'))
41
+ end
42
+
43
+ def url_safe_base64_encode(raw)
44
+ Base64.encode64(raw).tr('+/', '-_').strip
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,62 @@
1
+ require 'geocoder/lookups/base'
2
+ require 'geocoder/results/here'
3
+
4
+ module Geocoder::Lookup
5
+ class Here < Base
6
+
7
+ def name
8
+ "Here"
9
+ end
10
+
11
+ def required_api_key_parts
12
+ []
13
+ end
14
+
15
+ def query_url(query)
16
+ "#{protocol}://#{if query.reverse_geocode? then 'reverse.' end}geocoder.api.here.com/6.2/#{if query.reverse_geocode? then 'reverse' end}geocode.json?" + url_query_string(query)
17
+ end
18
+
19
+ private # ---------------------------------------------------------------
20
+
21
+ def results(query)
22
+ return [] unless doc = fetch_data(query)
23
+ return [] unless doc['Response'] && doc['Response']['View']
24
+ if r=doc['Response']['View']
25
+ return [] if r.nil? || !r.is_a?(Array) || r.empty?
26
+ return r.first['Result']
27
+ end
28
+ []
29
+ end
30
+
31
+ def query_url_params(query)
32
+ options = {
33
+ :gen=>4,
34
+ :app_id=>api_key,
35
+ :app_code=>api_code
36
+ }
37
+
38
+ if query.reverse_geocode?
39
+ super.merge(options).merge(
40
+ :prox=>query.sanitized_text,
41
+ :mode=>:retrieveAddresses
42
+ )
43
+ else
44
+ super.merge(options).merge(
45
+ :searchtext=>query.sanitized_text
46
+ )
47
+ end
48
+ end
49
+
50
+ def api_key
51
+ if a=configuration.api_key
52
+ return a.first if a.is_a?(Array)
53
+ end
54
+ end
55
+
56
+ def api_code
57
+ if a=configuration.api_key
58
+ return a.last if a.is_a?(Array)
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,43 @@
1
+ require 'geocoder/lookups/base'
2
+ require 'geocoder/results/ip_address_labs'
3
+ require 'csv'
4
+
5
+ module Geocoder::Lookup
6
+ class IpAddressLabs < Base
7
+
8
+ def name
9
+ "IpAddressLabs"
10
+ end
11
+
12
+ def query_url(query)
13
+ "#{protocol}://api.ipaddresslabs.com/iplocation/v1.7/locateip?key=#{configuration[:api_key]}&ip=#{query}&format=JSON"
14
+ end
15
+
16
+ def reserved_result
17
+ {
18
+ "city" => "New York",
19
+ "region_code" => "NY",
20
+ "latitude" => 40.7127837,
21
+ "longitude" => -74.0059413
22
+ }
23
+ end
24
+
25
+ private # ---------------------------------------------------------------
26
+
27
+
28
+ def results(query)
29
+ # don't look up a loopback address, just return the stored result
30
+ return [reserved_result] if query.loopback_ip_address?
31
+ doc = fetch_data(query)
32
+ status = doc["query_status"]["query_status_code"]
33
+ message = doc["query_status"]["query_status_description"]
34
+
35
+ if status == "OK"
36
+ return [ doc["geolocation_data"] ]
37
+ else
38
+ warn("IP Address Labs API Error: #{message}")
39
+ []
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,60 @@
1
+ require 'cgi'
2
+ require 'geocoder/lookups/base'
3
+ require "geocoder/results/mapquest"
4
+
5
+ module Geocoder::Lookup
6
+ class Mapquest < Base
7
+
8
+ def name
9
+ "Mapquest"
10
+ end
11
+
12
+ def required_api_key_parts
13
+ ["key"]
14
+ end
15
+
16
+ def query_url(query)
17
+ domain = configuration[:open] ? "open" : "www"
18
+ version = configuration[:version] || 1
19
+ url = "#{protocol}://#{domain}.mapquestapi.com/geocoding/v#{version}/#{search_type(query)}?"
20
+ url + url_query_string(query)
21
+ end
22
+
23
+ private # ---------------------------------------------------------------
24
+
25
+ def search_type(query)
26
+ query.reverse_geocode? ? "reverse" : "address"
27
+ end
28
+
29
+ def query_url_params(query)
30
+ params = { :location => query.sanitized_text }
31
+ if key = configuration.api_key
32
+ params[:key] = CGI.unescape(key)
33
+ end
34
+ params.merge(super)
35
+ end
36
+
37
+ # http://www.mapquestapi.com/geocoding/status_codes.html
38
+ # http://open.mapquestapi.com/geocoding/status_codes.html
39
+ def results(query)
40
+ return [] unless doc = fetch_data(query)
41
+ return doc["results"][0]['locations'] if doc['info']['statuscode'] == 0 # A successful geocode call
42
+
43
+ messages = doc['info']['messages'].join
44
+
45
+ case doc['info']['statuscode']
46
+ when 400 # Error with input
47
+ raise_error(Geocoder::InvalidRequest, messages) ||
48
+ warn("Mapquest Geocoding API error: #{messages}")
49
+ when 403 # Key related error
50
+ raise_error(Geocoder::InvalidApiKey, messages) ||
51
+ warn("Mapquest Geocoding API error: #{messages}")
52
+ when 500 # Unknown error
53
+ raise_error(Geocoder::Error, messages) ||
54
+ warn("Mapquest Geocoding API error: #{messages}")
55
+ end
56
+ []
57
+ end
58
+
59
+ end
60
+ end
@@ -0,0 +1,90 @@
1
+ require 'geocoder/lookups/base'
2
+ require 'geocoder/results/maxmind'
3
+ require 'csv'
4
+
5
+ module Geocoder::Lookup
6
+ class Maxmind < Base
7
+
8
+ def name
9
+ "MaxMind"
10
+ end
11
+
12
+ def query_url(query)
13
+ "#{protocol}://geoip.maxmind.com/#{service_code}?" + url_query_string(query)
14
+ end
15
+
16
+ private # ---------------------------------------------------------------
17
+
18
+ ##
19
+ # Return the name of the configured service, or raise an exception.
20
+ #
21
+ def configured_service!
22
+ if s = configuration[:service] and services.keys.include?(s)
23
+ return s
24
+ else
25
+ raise(
26
+ Geocoder::ConfigurationError,
27
+ "When using MaxMind you MUST specify a service name: " +
28
+ "Geocoder.configure(:maxmind => {:service => ...}), " +
29
+ "where '...' is one of: #{services.keys.inspect}"
30
+ )
31
+ end
32
+ end
33
+
34
+ def service_code
35
+ services[configured_service!]
36
+ end
37
+
38
+ def service_response_fields_count
39
+ Geocoder::Result::Maxmind.field_names[configured_service!].size
40
+ end
41
+
42
+ def data_contains_error?(parsed_data)
43
+ # if all fields given then there is an error
44
+ parsed_data.size == service_response_fields_count and !parsed_data.last.nil?
45
+ end
46
+
47
+ ##
48
+ # Service names mapped to code used in URL.
49
+ #
50
+ def services
51
+ {
52
+ :country => "a",
53
+ :city => "b",
54
+ :city_isp_org => "f",
55
+ :omni => "e"
56
+ }
57
+ end
58
+
59
+ def results(query)
60
+ # don't look up a loopback address, just return the stored result
61
+ return [reserved_result] if query.loopback_ip_address?
62
+ doc = fetch_data(query)
63
+ if doc and doc.is_a?(Array)
64
+ if !data_contains_error?(doc)
65
+ return [doc]
66
+ elsif doc.last == "INVALID_LICENSE_KEY"
67
+ raise_error(Geocoder::InvalidApiKey) || warn("Invalid MaxMind API key.")
68
+ end
69
+ end
70
+ return []
71
+ end
72
+
73
+ def parse_raw_data(raw_data)
74
+ # Maxmind just returns text/plain as csv format but according to documentation,
75
+ # we get ISO-8859-1 encoded string. We need to convert it.
76
+ CSV.parse_line raw_data.force_encoding("ISO-8859-1").encode("UTF-8")
77
+ end
78
+
79
+ def reserved_result
80
+ ",,,,0,0,0,0,,,".split(",")
81
+ end
82
+
83
+ def query_url_params(query)
84
+ {
85
+ :l => configuration.api_key,
86
+ :i => query.sanitized_text
87
+ }.merge(super)
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,58 @@
1
+ require 'ipaddr'
2
+ require 'geocoder/lookups/base'
3
+ require 'geocoder/results/maxmind_local'
4
+
5
+ module Geocoder::Lookup
6
+ class MaxmindLocal < Base
7
+
8
+ def initialize
9
+ if !configuration[:file].nil?
10
+ begin
11
+ gem = RUBY_PLATFORM == 'java' ? 'jgeoip' : 'geoip'
12
+ require gem
13
+ rescue LoadError
14
+ raise "Could not load geoip dependency. To use MaxMind Local lookup you must add the #{gem} gem to your Gemfile or have it installed in your system."
15
+ end
16
+ end
17
+ super
18
+ end
19
+
20
+ def name
21
+ "MaxMind Local"
22
+ end
23
+
24
+ def required_api_key_parts
25
+ []
26
+ end
27
+
28
+ private
29
+
30
+ def results(query)
31
+ if configuration[:file]
32
+ geoip_class = RUBY_PLATFORM == "java" ? JGeoIP : GeoIP
33
+ result = geoip_class.new(configuration[:file]).city(query.to_s)
34
+ result.nil? ? [] : [result.to_hash]
35
+ elsif configuration[:package] == :city
36
+ addr = IPAddr.new(query.text).to_i
37
+ q = "SELECT l.country, l.region, l.city, l.latitude, l.longitude
38
+ FROM maxmind_geolite_city_location l WHERE l.loc_id = (SELECT b.loc_id FROM maxmind_geolite_city_blocks b
39
+ WHERE b.start_ip_num <= #{addr} AND #{addr} <= b.end_ip_num LIMIT 1)"
40
+ format_result(q, [:country_name, :region_name, :city_name, :latitude, :longitude])
41
+ elsif configuration[:package] == :country
42
+ addr = IPAddr.new(query.text).to_i
43
+ q = "SELECT country, country_code FROM maxmind_geolite_country
44
+ WHERE start_ip_num <= #{addr} AND #{addr} <= end_ip_num"
45
+ format_result(q, [:country_name, :country_code2])
46
+ end
47
+ end
48
+
49
+ def format_result(query, attr_names)
50
+ if r = ActiveRecord::Base.connection.execute(query).first
51
+ r = r.values if r.is_a?(Hash) # some db adapters return Hash, some Array
52
+ [Hash[*attr_names.zip(r).flatten]]
53
+ else
54
+ []
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,52 @@
1
+ require 'geocoder/lookups/base'
2
+ require "geocoder/results/nominatim"
3
+
4
+ module Geocoder::Lookup
5
+ class Nominatim < Base
6
+
7
+ def name
8
+ "Nominatim"
9
+ end
10
+
11
+ def map_link_url(coordinates)
12
+ "http://www.openstreetmap.org/?lat=#{coordinates[0]}&lon=#{coordinates[1]}&zoom=15&layers=M"
13
+ end
14
+
15
+ def query_url(query)
16
+ method = query.reverse_geocode? ? "reverse" : "search"
17
+ host = configuration[:host] || "nominatim.openstreetmap.org"
18
+ "#{protocol}://#{host}/#{method}?" + url_query_string(query)
19
+ end
20
+
21
+ private # ---------------------------------------------------------------
22
+
23
+ def results(query)
24
+ return [] unless doc = fetch_data(query)
25
+ doc.is_a?(Array) ? doc : [doc]
26
+ end
27
+
28
+ def parse_raw_data(raw_data)
29
+ if raw_data.include?("Bandwidth limit exceeded")
30
+ raise_error(Geocoder::OverQueryLimitError) || warn("Over API query limit.")
31
+ else
32
+ super(raw_data)
33
+ end
34
+ end
35
+
36
+ def query_url_params(query)
37
+ params = {
38
+ :format => "json",
39
+ :addressdetails => "1",
40
+ :"accept-language" => (query.language || configuration.language)
41
+ }.merge(super)
42
+ if query.reverse_geocode?
43
+ lat,lon = query.coordinates
44
+ params[:lat] = lat
45
+ params[:lon] = lon
46
+ else
47
+ params[:q] = query.sanitized_text
48
+ end
49
+ params
50
+ end
51
+ end
52
+ end