geocoder2 0.1.0

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 (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
+