geocoder2 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (146) hide show
  1. data/.gitignore +5 -0
  2. data/.travis.yml +27 -0
  3. data/CHANGELOG.md +329 -0
  4. data/LICENSE +20 -0
  5. data/README.md +796 -0
  6. data/Rakefile +25 -0
  7. data/bin/geocode2 +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 +15 -0
  12. data/json?address=26+leonard+street%2C+Belmont&key=AIzaSyDoltU6YL8XeIQrSLFGk6ZfpKaWkPukwYQ&language=en +68 -0
  13. data/lib/generators/geocoder2/config/config_generator.rb +14 -0
  14. data/lib/generators/geocoder2/config/templates/initializer.rb +21 -0
  15. data/lib/geocoder2/cache.rb +89 -0
  16. data/lib/geocoder2/calculations.rb +389 -0
  17. data/lib/geocoder2/cli.rb +121 -0
  18. data/lib/geocoder2/configuration.rb +130 -0
  19. data/lib/geocoder2/configuration_hash.rb +11 -0
  20. data/lib/geocoder2/exceptions.rb +21 -0
  21. data/lib/geocoder2/lookup.rb +86 -0
  22. data/lib/geocoder2/lookups/baidu.rb +54 -0
  23. data/lib/geocoder2/lookups/base.rb +266 -0
  24. data/lib/geocoder2/lookups/bing.rb +47 -0
  25. data/lib/geocoder2/lookups/dstk.rb +20 -0
  26. data/lib/geocoder2/lookups/esri.rb +48 -0
  27. data/lib/geocoder2/lookups/freegeoip.rb +43 -0
  28. data/lib/geocoder2/lookups/geocoder_ca.rb +54 -0
  29. data/lib/geocoder2/lookups/geocoder_us.rb +39 -0
  30. data/lib/geocoder2/lookups/google.rb +69 -0
  31. data/lib/geocoder2/lookups/google_premier.rb +47 -0
  32. data/lib/geocoder2/lookups/mapquest.rb +59 -0
  33. data/lib/geocoder2/lookups/maxmind.rb +88 -0
  34. data/lib/geocoder2/lookups/nominatim.rb +44 -0
  35. data/lib/geocoder2/lookups/ovi.rb +62 -0
  36. data/lib/geocoder2/lookups/test.rb +44 -0
  37. data/lib/geocoder2/lookups/yahoo.rb +86 -0
  38. data/lib/geocoder2/lookups/yandex.rb +54 -0
  39. data/lib/geocoder2/models/active_record.rb +46 -0
  40. data/lib/geocoder2/models/base.rb +42 -0
  41. data/lib/geocoder2/models/mongo_base.rb +60 -0
  42. data/lib/geocoder2/models/mongo_mapper.rb +26 -0
  43. data/lib/geocoder2/models/mongoid.rb +32 -0
  44. data/lib/geocoder2/query.rb +107 -0
  45. data/lib/geocoder2/railtie.rb +26 -0
  46. data/lib/geocoder2/request.rb +23 -0
  47. data/lib/geocoder2/results/baidu.rb +79 -0
  48. data/lib/geocoder2/results/base.rb +67 -0
  49. data/lib/geocoder2/results/bing.rb +48 -0
  50. data/lib/geocoder2/results/dstk.rb +6 -0
  51. data/lib/geocoder2/results/esri.rb +51 -0
  52. data/lib/geocoder2/results/freegeoip.rb +45 -0
  53. data/lib/geocoder2/results/geocoder_ca.rb +60 -0
  54. data/lib/geocoder2/results/geocoder_us.rb +39 -0
  55. data/lib/geocoder2/results/google.rb +124 -0
  56. data/lib/geocoder2/results/google_premier.rb +6 -0
  57. data/lib/geocoder2/results/mapquest.rb +51 -0
  58. data/lib/geocoder2/results/maxmind.rb +135 -0
  59. data/lib/geocoder2/results/nominatim.rb +94 -0
  60. data/lib/geocoder2/results/ovi.rb +62 -0
  61. data/lib/geocoder2/results/test.rb +16 -0
  62. data/lib/geocoder2/results/yahoo.rb +55 -0
  63. data/lib/geocoder2/results/yandex.rb +80 -0
  64. data/lib/geocoder2/sql.rb +106 -0
  65. data/lib/geocoder2/stores/active_record.rb +272 -0
  66. data/lib/geocoder2/stores/base.rb +120 -0
  67. data/lib/geocoder2/stores/mongo_base.rb +89 -0
  68. data/lib/geocoder2/stores/mongo_mapper.rb +13 -0
  69. data/lib/geocoder2/stores/mongoid.rb +13 -0
  70. data/lib/geocoder2/version.rb +3 -0
  71. data/lib/geocoder2.rb +55 -0
  72. data/lib/hash_recursive_merge.rb +74 -0
  73. data/lib/oauth_util.rb +112 -0
  74. data/lib/tasks/geocoder2.rake +27 -0
  75. data/test/active_record_test.rb +15 -0
  76. data/test/cache_test.rb +35 -0
  77. data/test/calculations_test.rb +211 -0
  78. data/test/configuration_test.rb +78 -0
  79. data/test/custom_block_test.rb +32 -0
  80. data/test/error_handling_test.rb +43 -0
  81. data/test/fixtures/baidu_invalid_key +1 -0
  82. data/test/fixtures/baidu_no_results +1 -0
  83. data/test/fixtures/baidu_reverse +1 -0
  84. data/test/fixtures/baidu_shanghai_pearl_tower +12 -0
  85. data/test/fixtures/bing_invalid_key +1 -0
  86. data/test/fixtures/bing_madison_square_garden +40 -0
  87. data/test/fixtures/bing_no_results +16 -0
  88. data/test/fixtures/bing_reverse +42 -0
  89. data/test/fixtures/esri_madison_square_garden +59 -0
  90. data/test/fixtures/esri_no_results +8 -0
  91. data/test/fixtures/esri_reverse +21 -0
  92. data/test/fixtures/freegeoip_74_200_247_59 +12 -0
  93. data/test/fixtures/freegeoip_no_results +1 -0
  94. data/test/fixtures/geocoder_ca_madison_square_garden +1 -0
  95. data/test/fixtures/geocoder_ca_no_results +1 -0
  96. data/test/fixtures/geocoder_ca_reverse +34 -0
  97. data/test/fixtures/geocoder_us_madison_square_garden +1 -0
  98. data/test/fixtures/geocoder_us_no_results +1 -0
  99. data/test/fixtures/google_garbage +456 -0
  100. data/test/fixtures/google_madison_square_garden +57 -0
  101. data/test/fixtures/google_no_city_data +44 -0
  102. data/test/fixtures/google_no_locality +51 -0
  103. data/test/fixtures/google_no_results +4 -0
  104. data/test/fixtures/google_over_limit +4 -0
  105. data/test/fixtures/mapquest_error +16 -0
  106. data/test/fixtures/mapquest_invalid_api_key +16 -0
  107. data/test/fixtures/mapquest_invalid_request +16 -0
  108. data/test/fixtures/mapquest_madison_square_garden +52 -0
  109. data/test/fixtures/mapquest_no_results +16 -0
  110. data/test/fixtures/maxmind_24_24_24_21 +1 -0
  111. data/test/fixtures/maxmind_24_24_24_22 +1 -0
  112. data/test/fixtures/maxmind_24_24_24_23 +1 -0
  113. data/test/fixtures/maxmind_24_24_24_24 +1 -0
  114. data/test/fixtures/maxmind_74_200_247_59 +1 -0
  115. data/test/fixtures/maxmind_invalid_key +1 -0
  116. data/test/fixtures/maxmind_no_results +1 -0
  117. data/test/fixtures/nominatim_madison_square_garden +150 -0
  118. data/test/fixtures/nominatim_no_results +1 -0
  119. data/test/fixtures/ovi_madison_square_garden +72 -0
  120. data/test/fixtures/ovi_no_results +8 -0
  121. data/test/fixtures/yahoo_error +1 -0
  122. data/test/fixtures/yahoo_invalid_key +2 -0
  123. data/test/fixtures/yahoo_madison_square_garden +52 -0
  124. data/test/fixtures/yahoo_no_results +10 -0
  125. data/test/fixtures/yahoo_over_limit +2 -0
  126. data/test/fixtures/yandex_invalid_key +1 -0
  127. data/test/fixtures/yandex_kremlin +48 -0
  128. data/test/fixtures/yandex_no_city_and_town +112 -0
  129. data/test/fixtures/yandex_no_results +16 -0
  130. data/test/geocoder_test.rb +59 -0
  131. data/test/https_test.rb +16 -0
  132. data/test/integration/smoke_test.rb +26 -0
  133. data/test/lookup_test.rb +117 -0
  134. data/test/method_aliases_test.rb +25 -0
  135. data/test/mongoid_test.rb +46 -0
  136. data/test/mongoid_test_helper.rb +43 -0
  137. data/test/near_test.rb +61 -0
  138. data/test/oauth_util_test.rb +30 -0
  139. data/test/proxy_test.rb +36 -0
  140. data/test/query_test.rb +52 -0
  141. data/test/request_test.rb +29 -0
  142. data/test/result_test.rb +42 -0
  143. data/test/services_test.rb +393 -0
  144. data/test/test_helper.rb +289 -0
  145. data/test/test_mode_test.rb +59 -0
  146. metadata +213 -0
@@ -0,0 +1,86 @@
1
+ module Geocoder2
2
+ module Lookup
3
+ extend self
4
+
5
+ ##
6
+ # Array of valid Lookup service names.
7
+ #
8
+ def all_services
9
+ street_services + ip_services
10
+ end
11
+
12
+ ##
13
+ # Array of valid Lookup service names, excluding :test.
14
+ #
15
+ def all_services_except_test
16
+ all_services - [:test]
17
+ end
18
+
19
+ ##
20
+ # All street address lookup services, default first.
21
+ #
22
+ def street_services
23
+ [
24
+ :dstk,
25
+ :esri,
26
+ :google,
27
+ :google_premier,
28
+ :yahoo,
29
+ :bing,
30
+ :geocoder_ca,
31
+ :geocoder_us,
32
+ :yandex,
33
+ :nominatim,
34
+ :mapquest,
35
+ :ovi,
36
+ :baidu,
37
+ :test
38
+ ]
39
+ end
40
+
41
+ ##
42
+ # All IP address lookup services, default first.
43
+ #
44
+ def ip_services
45
+ [:freegeoip, :maxmind]
46
+ end
47
+
48
+ ##
49
+ # Retrieve a Lookup object from the store.
50
+ # Use this instead of Geocoder2::Lookup::X.new to get an
51
+ # already-configured Lookup object.
52
+ #
53
+ def get(name)
54
+ @services = {} unless defined?(@services)
55
+ @services[name] = spawn(name) unless @services.include?(name)
56
+ @services[name]
57
+ end
58
+
59
+
60
+ private # -----------------------------------------------------------------
61
+
62
+ ##
63
+ # Spawn a Lookup of the given name.
64
+ #
65
+ def spawn(name)
66
+ if all_services.include?(name)
67
+ Geocoder2::Lookup.const_get(classify_name(name)).new
68
+ else
69
+ valids = all_services.map(&:inspect).join(", ")
70
+ raise ConfigurationError, "Please specify a valid lookup for Geocoder2 " +
71
+ "(#{name.inspect} is not one of: #{valids})."
72
+ end
73
+ end
74
+
75
+ ##
76
+ # Convert an "underscore" version of a name into a "class" version.
77
+ #
78
+ def classify_name(filename)
79
+ filename.to_s.split("_").map{ |i| i[0...1].upcase + i[1..-1] }.join
80
+ end
81
+ end
82
+ end
83
+
84
+ Geocoder2::Lookup.all_services.each do |name|
85
+ require "geocoder2/lookups/#{name}"
86
+ end
@@ -0,0 +1,54 @@
1
+ require 'geocoder2/lookups/base'
2
+ require "geocoder2/results/baidu"
3
+
4
+ module Geocoder2::Lookup
5
+ class Baidu < Base
6
+
7
+ def name
8
+ "Baidu"
9
+ end
10
+
11
+ def required_api_key_parts
12
+ ["key"]
13
+ end
14
+
15
+ def query_url(query)
16
+ "http://api.map.baidu.com/geocoder2/v2/?" + url_query_string(query)
17
+ end
18
+
19
+ private # ---------------------------------------------------------------
20
+
21
+ def results(query, reverse = false)
22
+ return [] unless doc = fetch_data(query)
23
+ case doc['status']; when 0
24
+ return [doc['result']] unless doc['result'].blank?
25
+ when 1, 3, 4
26
+ raise_error(Geocoder2::Error, messages) ||
27
+ warn("Baidu Geocoding API error: server error.")
28
+ when 2
29
+ raise_error(Geocoder2::InvalidRequest, messages) ||
30
+ warn("Baidu Geocoding API error: invalid request.")
31
+ when 5
32
+ raise_error(Geocoder2::InvalidApiKey, messages) ||
33
+ warn("Baidu Geocoding API error: invalid api key.")
34
+ when 101, 102, 200..299
35
+ raise_error(Geocoder2::RequestDenied) ||
36
+ warn("Baidu Geocoding API error: request denied.")
37
+ when 300..399
38
+ raise_error(Geocoder2::OverQueryLimitError) ||
39
+ warn("Baidu Geocoding API error: over query limit.")
40
+ end
41
+ return []
42
+ end
43
+
44
+ def query_url_params(query)
45
+ {
46
+ (query.reverse_geocode? ? :location : :address) => query.sanitized_text,
47
+ :ak => configuration.api_key,
48
+ :output => "json"
49
+ }.merge(super)
50
+ end
51
+
52
+ end
53
+ end
54
+
@@ -0,0 +1,266 @@
1
+ require 'net/http'
2
+ require 'net/https'
3
+ require 'uri'
4
+
5
+ unless defined?(ActiveSupport::JSON)
6
+ begin
7
+ require 'rubygems' # for Ruby 1.8
8
+ require 'json'
9
+ rescue LoadError
10
+ raise LoadError, "Please install the 'json' or 'json_pure' gem to parse geocoder2 results."
11
+ end
12
+ end
13
+
14
+ module Geocoder2
15
+ module Lookup
16
+
17
+ class Base
18
+
19
+ ##
20
+ # Human-readable name of the geocoding API.
21
+ #
22
+ def name
23
+ fail
24
+ end
25
+
26
+ ##
27
+ # Symbol which is used in configuration to refer to this Lookup.
28
+ #
29
+ def handle
30
+ str = self.class.to_s
31
+ str[str.rindex(':')+1..-1].gsub(/([a-z\d]+)([A-Z])/,'\1_\2').downcase.to_sym
32
+ end
33
+
34
+ ##
35
+ # Query the geocoding API and return a Geocoder2::Result object.
36
+ # Returns +nil+ on timeout or error.
37
+ #
38
+ # Takes a search string (eg: "Mississippi Coast Coliseumf, Biloxi, MS",
39
+ # "205.128.54.202") for geocoding, or coordinates (latitude, longitude)
40
+ # for reverse geocoding. Returns an array of <tt>Geocoder2::Result</tt>s.
41
+ #
42
+ def search(query, options = {})
43
+ query = Geocoder2::Query.new(query, options) unless query.is_a?(Geocoder2::Query)
44
+ results(query).map{ |r|
45
+ result = result_class.new(r)
46
+ result.cache_hit = @cache_hit if cache
47
+ result
48
+ }
49
+ end
50
+
51
+ ##
52
+ # Return the URL for a map of the given coordinates.
53
+ #
54
+ # Not necessarily implemented by all subclasses as only some lookups
55
+ # also provide maps.
56
+ #
57
+ def map_link_url(coordinates)
58
+ nil
59
+ end
60
+
61
+ ##
62
+ # Array containing string descriptions of keys required by the API.
63
+ # Empty array if keys are optional or not required.
64
+ #
65
+ def required_api_key_parts
66
+ []
67
+ end
68
+
69
+ ##
70
+ # URL to use for querying the geocoding engine.
71
+ #
72
+ def query_url(query)
73
+ fail
74
+ end
75
+
76
+ ##
77
+ # The working Cache object.
78
+ #
79
+ def cache
80
+ if @cache.nil? and store = configuration.cache
81
+ @cache = Cache.new(store, configuration.cache_prefix)
82
+ end
83
+ @cache
84
+ end
85
+
86
+ private # -------------------------------------------------------------
87
+
88
+ ##
89
+ # An object with configuration data for this particular lookup.
90
+ #
91
+ def configuration
92
+ Geocoder2.config_for_lookup(handle)
93
+ end
94
+
95
+ ##
96
+ # Object used to make HTTP requests.
97
+ #
98
+ def http_client
99
+ protocol = "http#{'s' if configuration.use_https}"
100
+ proxy_name = "#{protocol}_proxy"
101
+ if proxy = configuration.send(proxy_name)
102
+ proxy_url = !!(proxy =~ /^#{protocol}/) ? proxy : protocol + '://' + proxy
103
+ begin
104
+ uri = URI.parse(proxy_url)
105
+ rescue URI::InvalidURIError
106
+ raise ConfigurationError,
107
+ "Error parsing #{protocol.upcase} proxy URL: '#{proxy_url}'"
108
+ end
109
+ Net::HTTP::Proxy(uri.host, uri.port, uri.user, uri.password)
110
+ else
111
+ Net::HTTP
112
+ end
113
+ end
114
+
115
+ ##
116
+ # Geocoder2::Result object or nil on timeout or other error.
117
+ #
118
+ def results(query)
119
+ fail
120
+ end
121
+
122
+ def query_url_params(query)
123
+ query.options[:params] || {}
124
+ end
125
+
126
+ def url_query_string(query)
127
+ hash_to_query(
128
+ query_url_params(query).reject{ |key,value| value.nil? }
129
+ )
130
+ end
131
+
132
+ ##
133
+ # Key to use for caching a geocoding result. Usually this will be the
134
+ # request URL, but in cases where OAuth is used and the nonce,
135
+ # timestamp, etc varies from one request to another, we need to use
136
+ # something else (like the URL before OAuth encoding).
137
+ #
138
+ def cache_key(query)
139
+ query_url(query)
140
+ end
141
+
142
+ ##
143
+ # Class of the result objects
144
+ #
145
+ def result_class
146
+ Geocoder2::Result.const_get(self.class.to_s.split(":").last)
147
+ end
148
+
149
+ ##
150
+ # Raise exception if configuration specifies it should be raised.
151
+ # Return false if exception not raised.
152
+ #
153
+ def raise_error(error, message = nil)
154
+ exceptions = configuration.always_raise
155
+ if exceptions == :all or exceptions.include?( error.is_a?(Class) ? error : error.class )
156
+ raise error, message
157
+ else
158
+ false
159
+ end
160
+ end
161
+
162
+ ##
163
+ # Returns a parsed search result (Ruby hash).
164
+ #
165
+ def fetch_data(query)
166
+ parse_raw_data fetch_raw_data(query)
167
+ rescue SocketError => err
168
+ raise_error(err) or warn "Geocoding API connection cannot be established."
169
+ rescue TimeoutError => err
170
+ raise_error(err) or warn "Geocoding API not responding fast enough " +
171
+ "(use Geocoder2.configure(:timeout => ...) to set limit)."
172
+ end
173
+
174
+ def parse_json(data)
175
+ if defined?(ActiveSupport::JSON)
176
+ ActiveSupport::JSON.decode(data)
177
+ else
178
+ JSON.parse(data)
179
+ end
180
+ end
181
+
182
+ ##
183
+ # Parses a raw search result (returns hash or array).
184
+ #
185
+ def parse_raw_data(raw_data)
186
+ parse_json(raw_data)
187
+ rescue
188
+ warn "Geocoding API's response was not valid JSON."
189
+ end
190
+
191
+ ##
192
+ # Protocol to use for communication with geocoding services.
193
+ # Set in configuration but not available for every service.
194
+ #
195
+ def protocol
196
+ "http" + (configuration.use_https ? "s" : "")
197
+ end
198
+
199
+ def valid_response?(response)
200
+ (200..399).include?(response.code.to_i)
201
+ end
202
+
203
+ ##
204
+ # Fetch a raw geocoding result (JSON string).
205
+ # The result might or might not be cached.
206
+ #
207
+ def fetch_raw_data(query)
208
+ key = cache_key(query)
209
+ if cache and body = cache[key]
210
+ @cache_hit = true
211
+ else
212
+ check_api_key_configuration!(query)
213
+ response = make_api_request(query)
214
+ body = response.body
215
+ if cache and valid_response?(response)
216
+ cache[key] = body
217
+ end
218
+ @cache_hit = false
219
+ end
220
+ body
221
+ end
222
+
223
+ ##
224
+ # Make an HTTP(S) request to a geocoding API and
225
+ # return the response object.
226
+ #
227
+ def make_api_request(query)
228
+ timeout(configuration.timeout) do
229
+ uri = URI.parse(query_url(query))
230
+ puts "DEBUG: #{uri.inspect}"
231
+ client = http_client.new(uri.host, uri.port)
232
+ client.use_ssl = true if configuration.use_https
233
+ client.get(uri.request_uri, configuration.http_headers)
234
+
235
+ #http_client.start(uri.host, uri.port) do |client|
236
+ # client.use_ssl = true if configuration.use_https
237
+ # req = Net::HTTP::Get.new(uri.request_uri, configuration.http_headers)
238
+ # req.basic_auth(uri.user, uri.password) if uri.user and uri.password
239
+ # client.request(req)
240
+ #end
241
+ end
242
+ end
243
+
244
+ def check_api_key_configuration!(query)
245
+ key_parts = query.lookup.required_api_key_parts
246
+ if key_parts.size > Array(configuration.api_key).size
247
+ parts_string = key_parts.size == 1 ? key_parts.first : key_parts
248
+ raise Geocoder2::ConfigurationError,
249
+ "The #{query.lookup.name} API requires a key to be configured: " +
250
+ parts_string.inspect
251
+ end
252
+ end
253
+
254
+ ##
255
+ # Simulate ActiveSupport's Object#to_query.
256
+ # Removes any keys with nil value.
257
+ #
258
+ def hash_to_query(hash)
259
+ require 'cgi' unless defined?(CGI) && defined?(CGI.escape)
260
+ hash.collect{ |p|
261
+ p[1].nil? ? nil : p.map{ |i| CGI.escape i.to_s } * '='
262
+ }.compact.sort * '&'
263
+ end
264
+ end
265
+ end
266
+ end
@@ -0,0 +1,47 @@
1
+ require 'geocoder2/lookups/base'
2
+ require "geocoder2/results/bing"
3
+
4
+ module Geocoder2::Lookup
5
+ class Bing < Base
6
+
7
+ def name
8
+ "Bing"
9
+ end
10
+
11
+ def map_link_url(coordinates)
12
+ "http://www.bing.com/maps/default.aspx?cp=#{coordinates.join('~')}"
13
+ end
14
+
15
+ def required_api_key_parts
16
+ ["key"]
17
+ end
18
+
19
+ def query_url(query)
20
+ "#{protocol}://dev.virtualearth.net/REST/v1/Locations" +
21
+ (query.reverse_geocode? ? "/#{query.sanitized_text}?" : "?") +
22
+ url_query_string(query)
23
+ end
24
+
25
+ private # ---------------------------------------------------------------
26
+
27
+ def results(query)
28
+ return [] unless doc = fetch_data(query)
29
+
30
+ if doc['statusCode'] == 200
31
+ return doc['resourceSets'].first['estimatedTotal'] > 0 ? doc['resourceSets'].first['resources'] : []
32
+ elsif doc['statusCode'] == 401 and doc["authenticationResultCode"] == "InvalidCredentials"
33
+ raise_error(Geocoder2::InvalidApiKey) || warn("Invalid Bing API key.")
34
+ else
35
+ warn "Bing Geocoding API error: #{doc['statusCode']} (#{doc['statusDescription']})."
36
+ end
37
+ return []
38
+ end
39
+
40
+ def query_url_params(query)
41
+ {
42
+ :key => configuration.api_key,
43
+ :query => query.reverse_geocode? ? nil : query.sanitized_text
44
+ }.merge(super)
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,20 @@
1
+ # More information about the Data Science Toolkit can be found at:
2
+ # http://www.datasciencetoolkit.org/. The provided APIs mimic the
3
+ # Google geocoding api.
4
+
5
+ require 'geocoder2/lookups/google'
6
+ require 'geocoder2/results/dstk'
7
+
8
+ module Geocoder2::Lookup
9
+ class Dstk < Google
10
+
11
+ def name
12
+ "Data Science Toolkit"
13
+ end
14
+
15
+ def query_url(query)
16
+ host = configuration[:host] || "www.datasciencetoolkit.org"
17
+ "#{protocol}://#{host}/maps/api/geocode/json?" + url_query_string(query)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,48 @@
1
+ require 'geocoder2/lookups/base'
2
+ require "geocoder2/results/esri"
3
+
4
+ module Geocoder2::Lookup
5
+ class Esri < Base
6
+
7
+ def name
8
+ "Esri"
9
+ end
10
+
11
+ def query_url(query)
12
+ search_keyword = query.reverse_geocode? ? "reverseGeocode" : "find"
13
+
14
+ "#{protocol}://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/#{search_keyword}?" +
15
+ url_query_string(query)
16
+ end
17
+
18
+ private # ---------------------------------------------------------------
19
+
20
+ def results(query)
21
+ return [] unless doc = fetch_data(query)
22
+
23
+ if (!query.reverse_geocode?)
24
+ return [] if doc['locations'].empty?
25
+ end
26
+
27
+ if (doc['error'].nil?)
28
+ return [ doc ]
29
+ else
30
+ return []
31
+ end
32
+ end
33
+
34
+ def query_url_params(query)
35
+ params = {
36
+ :f => "pjson",
37
+ :outFields => "*"
38
+ }
39
+ if query.reverse_geocode?
40
+ params[:location] = query.coordinates.reverse.join(',')
41
+ else
42
+ params[:text] = query.sanitized_text
43
+ end
44
+ params.merge(super)
45
+ end
46
+
47
+ end
48
+ end
@@ -0,0 +1,43 @@
1
+ require 'geocoder2/lookups/base'
2
+ require 'geocoder2/results/freegeoip'
3
+
4
+ module Geocoder2::Lookup
5
+ class Freegeoip < Base
6
+
7
+ def name
8
+ "FreeGeoIP"
9
+ end
10
+
11
+ def query_url(query)
12
+ "#{protocol}://freegeoip.net/json/#{query.sanitized_text}"
13
+ end
14
+
15
+ private # ---------------------------------------------------------------
16
+
17
+ def parse_raw_data(raw_data)
18
+ raw_data.match(/^<html><title>404/) ? nil : super(raw_data)
19
+ end
20
+
21
+ def results(query)
22
+ # don't look up a loopback address, just return the stored result
23
+ return [reserved_result(query.text)] if query.loopback_ip_address?
24
+ # note: Freegeoip.net returns plain text "Not Found" on bad request
25
+ (doc = fetch_data(query)) ? [doc] : []
26
+ end
27
+
28
+ def reserved_result(ip)
29
+ {
30
+ "ip" => ip,
31
+ "city" => "",
32
+ "region_code" => "",
33
+ "region_name" => "",
34
+ "metrocode" => "",
35
+ "zipcode" => "",
36
+ "latitude" => "0",
37
+ "longitude" => "0",
38
+ "country_name" => "Reserved",
39
+ "country_code" => "RD"
40
+ }
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,54 @@
1
+ require 'geocoder2/lookups/base'
2
+ require "geocoder2/results/geocoder_ca"
3
+
4
+ module Geocoder2::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 "Geocoder2.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 'geocoder2/lookups/base'
2
+ require "geocoder2/results/geocoder_us"
3
+
4
+ module Geocoder2::Lookup
5
+ class GeocoderUs < Base
6
+
7
+ def name
8
+ "Geocoder2.us"
9
+ end
10
+
11
+ def query_url(query)
12
+ if configuration.api_key
13
+ "http://#{configuration.api_key}@geocoder2.us/member/service/csv/geocode?" + url_query_string(query)
14
+ else
15
+ "http://geocoder2.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
+