geocoder 1.4.3 → 1.6.1

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 (109) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +64 -0
  3. data/LICENSE +1 -1
  4. data/README.md +365 -883
  5. data/examples/autoexpire_cache_redis.rb +5 -3
  6. data/lib/generators/geocoder/config/templates/initializer.rb +3 -2
  7. data/lib/generators/geocoder/maxmind/geolite_city_generator.rb +2 -0
  8. data/lib/generators/geocoder/maxmind/geolite_country_generator.rb +2 -0
  9. data/lib/generators/geocoder/maxmind/templates/migration/geolite_city.rb +1 -1
  10. data/lib/generators/geocoder/maxmind/templates/migration/geolite_country.rb +1 -1
  11. data/lib/generators/geocoder/migration_version.rb +15 -0
  12. data/lib/geocoder/cache.rb +6 -2
  13. data/lib/geocoder/calculations.rb +1 -1
  14. data/lib/geocoder/cli.rb +2 -2
  15. data/lib/geocoder/configuration.rb +2 -2
  16. data/lib/geocoder/exceptions.rb +1 -1
  17. data/lib/geocoder/ip_address.rb +14 -1
  18. data/lib/geocoder/lookup.rb +13 -6
  19. data/lib/geocoder/lookups/amap.rb +63 -0
  20. data/lib/geocoder/lookups/baidu.rb +14 -10
  21. data/lib/geocoder/lookups/baidu_ip.rb +7 -36
  22. data/lib/geocoder/lookups/ban_data_gouv_fr.rb +17 -4
  23. data/lib/geocoder/lookups/base.rb +28 -5
  24. data/lib/geocoder/lookups/bing.rb +15 -13
  25. data/lib/geocoder/lookups/db_ip_com.rb +52 -0
  26. data/lib/geocoder/lookups/dstk.rb +4 -2
  27. data/lib/geocoder/lookups/esri.rb +39 -29
  28. data/lib/geocoder/lookups/freegeoip.rb +16 -7
  29. data/lib/geocoder/lookups/geocoder_ca.rb +4 -4
  30. data/lib/geocoder/lookups/geocodio.rb +5 -5
  31. data/lib/geocoder/lookups/geoportail_lu.rb +7 -7
  32. data/lib/geocoder/lookups/google.rb +13 -9
  33. data/lib/geocoder/lookups/google_places_details.rb +4 -4
  34. data/lib/geocoder/lookups/google_places_search.rb +4 -4
  35. data/lib/geocoder/lookups/google_premier.rb +11 -1
  36. data/lib/geocoder/lookups/here.rb +29 -23
  37. data/lib/geocoder/lookups/ip2location.rb +67 -0
  38. data/lib/geocoder/lookups/ipapi_com.rb +9 -13
  39. data/lib/geocoder/lookups/ipdata_co.rb +62 -0
  40. data/lib/geocoder/lookups/ipgeolocation.rb +51 -0
  41. data/lib/geocoder/lookups/ipinfo_io.rb +11 -29
  42. data/lib/geocoder/lookups/ipregistry.rb +68 -0
  43. data/lib/geocoder/lookups/ipstack.rb +63 -0
  44. data/lib/geocoder/lookups/latlon.rb +4 -4
  45. data/lib/geocoder/lookups/location_iq.rb +26 -8
  46. data/lib/geocoder/lookups/mapbox.rb +12 -6
  47. data/lib/geocoder/lookups/mapquest.rb +4 -5
  48. data/lib/geocoder/lookups/maxmind.rb +6 -6
  49. data/lib/geocoder/lookups/maxmind_geoip2.rb +8 -7
  50. data/lib/geocoder/lookups/nominatim.rb +17 -5
  51. data/lib/geocoder/lookups/opencagedata.rb +7 -6
  52. data/lib/geocoder/lookups/osmnames.rb +57 -0
  53. data/lib/geocoder/lookups/pelias.rb +8 -9
  54. data/lib/geocoder/lookups/pickpoint.rb +41 -0
  55. data/lib/geocoder/lookups/pointpin.rb +10 -9
  56. data/lib/geocoder/lookups/postcode_anywhere_uk.rb +4 -5
  57. data/lib/geocoder/lookups/postcodes_io.rb +31 -0
  58. data/lib/geocoder/lookups/smarty_streets.rb +20 -10
  59. data/lib/geocoder/lookups/telize.rb +26 -6
  60. data/lib/geocoder/lookups/tencent.rb +59 -0
  61. data/lib/geocoder/lookups/yandex.rb +12 -8
  62. data/lib/geocoder/models/active_record.rb +4 -3
  63. data/lib/geocoder/query.rb +14 -0
  64. data/lib/geocoder/railtie.rb +1 -1
  65. data/lib/geocoder/request.rb +32 -0
  66. data/lib/geocoder/results/amap.rb +87 -0
  67. data/lib/geocoder/results/baidu.rb +10 -14
  68. data/lib/geocoder/results/ban_data_gouv_fr.rb +1 -1
  69. data/lib/geocoder/results/base.rb +13 -1
  70. data/lib/geocoder/results/bing.rb +1 -1
  71. data/lib/geocoder/results/db_ip_com.rb +58 -0
  72. data/lib/geocoder/results/freegeoip.rb +0 -5
  73. data/lib/geocoder/results/geocoder_ca.rb +3 -3
  74. data/lib/geocoder/results/geoip2.rb +24 -10
  75. data/lib/geocoder/results/geoportail_lu.rb +5 -3
  76. data/lib/geocoder/results/google.rb +16 -5
  77. data/lib/geocoder/results/here.rb +12 -1
  78. data/lib/geocoder/results/ip2location.rb +22 -0
  79. data/lib/geocoder/results/ipdata_co.rb +40 -0
  80. data/lib/geocoder/results/ipgeolocation.rb +59 -0
  81. data/lib/geocoder/results/ipregistry.rb +308 -0
  82. data/lib/geocoder/results/ipstack.rb +60 -0
  83. data/lib/geocoder/results/maxmind.rb +0 -5
  84. data/lib/geocoder/results/maxmind_local.rb +0 -5
  85. data/lib/geocoder/results/nominatim.rb +12 -0
  86. data/lib/geocoder/results/opencagedata.rb +12 -2
  87. data/lib/geocoder/results/osmnames.rb +56 -0
  88. data/lib/geocoder/results/pickpoint.rb +6 -0
  89. data/lib/geocoder/results/postcodes_io.rb +40 -0
  90. data/lib/geocoder/results/smarty_streets.rb +55 -19
  91. data/lib/geocoder/results/telize.rb +0 -5
  92. data/lib/geocoder/results/tencent.rb +72 -0
  93. data/lib/geocoder/results/test.rb +1 -1
  94. data/lib/geocoder/sql.rb +4 -4
  95. data/lib/geocoder/stores/active_record.rb +16 -5
  96. data/lib/geocoder/stores/base.rb +1 -2
  97. data/lib/geocoder/version.rb +1 -1
  98. data/lib/hash_recursive_merge.rb +1 -2
  99. data/lib/maxmind_database.rb +3 -3
  100. data/lib/tasks/geocoder.rake +11 -3
  101. metadata +30 -14
  102. data/lib/geocoder/lookups/geocoder_us.rb +0 -43
  103. data/lib/geocoder/lookups/mapzen.rb +0 -15
  104. data/lib/geocoder/lookups/okf.rb +0 -44
  105. data/lib/geocoder/lookups/ovi.rb +0 -62
  106. data/lib/geocoder/results/geocoder_us.rb +0 -39
  107. data/lib/geocoder/results/mapzen.rb +0 -5
  108. data/lib/geocoder/results/okf.rb +0 -106
  109. data/lib/geocoder/results/ovi.rb +0 -71
@@ -1,6 +1,8 @@
1
1
  # This class implements a cache with simple delegation to the Redis store, but
2
2
  # when it creates a key/value pair, it also sends an EXPIRE command with a TTL.
3
3
  # It should be fairly simple to do the same thing with Memcached.
4
+ # Alternatively, this class could inherit from Redis, which would make most
5
+ # of the below methods unnecessary.
4
6
  class AutoexpireCacheRedis
5
7
  def initialize(store, ttl = 86400)
6
8
  @store = store
@@ -8,11 +10,11 @@ class AutoexpireCacheRedis
8
10
  end
9
11
 
10
12
  def [](url)
11
- @store.[](url)
13
+ @store.get(url)
12
14
  end
13
15
 
14
16
  def []=(url, value)
15
- @store.[]=(url, value)
17
+ @store.set(url, value)
16
18
  @store.expire(url, @ttl)
17
19
  end
18
20
 
@@ -25,4 +27,4 @@ class AutoexpireCacheRedis
25
27
  end
26
28
  end
27
29
 
28
- Geocoder.configure(:cache => AutoexpireCacheRedis.new(Redis.new))
30
+ Geocoder.configure(:cache => AutoexpireCacheRedis.new(Redis.new))
@@ -1,13 +1,14 @@
1
1
  Geocoder.configure(
2
2
  # Geocoding options
3
3
  # timeout: 3, # geocoding service timeout (secs)
4
- # lookup: :google, # name of geocoding service (symbol)
4
+ # lookup: :nominatim, # name of geocoding service (symbol)
5
+ # ip_lookup: :ipinfo_io, # name of IP address geocoding service (symbol)
5
6
  # language: :en, # ISO-639 language code
6
7
  # use_https: false, # use HTTPS for lookup requests? (if supported)
7
8
  # http_proxy: nil, # HTTP proxy server (user:pass@host:port)
8
9
  # https_proxy: nil, # HTTPS proxy server (user:pass@host:port)
9
10
  # api_key: nil, # API key for geocoding service
10
- # cache: nil, # cache object (must respond to #[], #[]=, and #keys)
11
+ # cache: nil, # cache object (must respond to #[], #[]=, and #del)
11
12
  # cache_prefix: 'geocoder:', # prefix (string) to use for all cache keys
12
13
 
13
14
  # Exceptions that should not be rescued by default
@@ -1,10 +1,12 @@
1
1
  require 'rails/generators/migration'
2
+ require 'generators/geocoder/migration_version'
2
3
 
3
4
  module Geocoder
4
5
  module Generators
5
6
  module Maxmind
6
7
  class GeoliteCityGenerator < Rails::Generators::Base
7
8
  include Rails::Generators::Migration
9
+ include Generators::MigrationVersion
8
10
 
9
11
  source_root File.expand_path('../templates', __FILE__)
10
12
 
@@ -1,10 +1,12 @@
1
1
  require 'rails/generators/migration'
2
+ require 'generators/geocoder/migration_version'
2
3
 
3
4
  module Geocoder
4
5
  module Generators
5
6
  module Maxmind
6
7
  class GeoliteCountryGenerator < Rails::Generators::Base
7
8
  include Rails::Generators::Migration
9
+ include Generators::MigrationVersion
8
10
 
9
11
  source_root File.expand_path('../templates', __FILE__)
10
12
 
@@ -1,4 +1,4 @@
1
- class GeocoderMaxmindGeoliteCity < ActiveRecord::Migration
1
+ class GeocoderMaxmindGeoliteCity < ActiveRecord::Migration<%= migration_version %>
2
2
  def self.up
3
3
  create_table :maxmind_geolite_city_blocks, id: false do |t|
4
4
  t.column :start_ip_num, :bigint, null: false
@@ -1,4 +1,4 @@
1
- class GeocoderMaxmindGeoliteCountry < ActiveRecord::Migration
1
+ class GeocoderMaxmindGeoliteCountry < ActiveRecord::Migration<%= migration_version %>
2
2
  def self.up
3
3
  create_table :maxmind_geolite_country, id: false do |t|
4
4
  t.column :start_ip, :string
@@ -0,0 +1,15 @@
1
+ module Geocoder
2
+ module Generators
3
+ module MigrationVersion
4
+ def rails_5?
5
+ Rails::VERSION::MAJOR == 5
6
+ end
7
+
8
+ def migration_version
9
+ if rails_5?
10
+ "[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]"
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -40,7 +40,11 @@ module Geocoder
40
40
  #
41
41
  def expire(url)
42
42
  if url == :all
43
- urls.each{ |u| expire(u) }
43
+ if store.respond_to?(:keys)
44
+ urls.each{ |u| expire(u) }
45
+ else
46
+ raise(NoMethodError, "The Geocoder cache store must implement `#keys` for `expire(:all)` to work")
47
+ end
44
48
  else
45
49
  expire_single_url(url)
46
50
  end
@@ -64,7 +68,7 @@ module Geocoder
64
68
  # that have non-nil values.
65
69
  #
66
70
  def keys
67
- store.keys.select{ |k| k.match(/^#{prefix}/) and interpret(store[k]) }
71
+ store.keys.select{ |k| k.match(/^#{prefix}/) and self[k] }
68
72
  end
69
73
 
70
74
  ##
@@ -154,7 +154,7 @@ module Geocoder
154
154
  # Translate a bearing (float) into a compass direction (string, eg "North").
155
155
  #
156
156
  def compass_point(bearing, points = COMPASS_POINTS)
157
- seg_size = 360 / points.size
157
+ seg_size = 360.0 / points.size
158
158
  points[((bearing + (seg_size / 2)) % 360) / seg_size]
159
159
  end
160
160
 
@@ -97,7 +97,7 @@ module Geocoder
97
97
  end
98
98
 
99
99
  if (result = Geocoder.search(query).first)
100
- google = Geocoder::Lookup.get(:google)
100
+ nominatim = Geocoder::Lookup.get(:nominatim)
101
101
  lines = [
102
102
  ["Latitude", result.latitude],
103
103
  ["Longitude", result.longitude],
@@ -106,7 +106,7 @@ module Geocoder
106
106
  ["State/province", result.state],
107
107
  ["Postal code", result.postal_code],
108
108
  ["Country", result.country],
109
- ["Google map", google.map_link_url(result.coordinates)],
109
+ ["Map", nominatim.map_link_url(result.coordinates)],
110
110
  ]
111
111
  lines.each do |line|
112
112
  out << (line[0] + ": ").ljust(18) + line[1].to_s + "\n"
@@ -97,8 +97,8 @@ module Geocoder
97
97
 
98
98
  # geocoding options
99
99
  @data[:timeout] = 3 # geocoding service timeout (secs)
100
- @data[:lookup] = :google # name of street address geocoding service (symbol)
101
- @data[:ip_lookup] = :freegeoip # name of IP address geocoding service (symbol)
100
+ @data[:lookup] = :nominatim # name of street address geocoding service (symbol)
101
+ @data[:ip_lookup] = :ipinfo_io # name of IP address geocoding service (symbol)
102
102
  @data[:language] = :en # ISO-639 language code
103
103
  @data[:http_headers] = {} # HTTP headers for lookup
104
104
  @data[:use_https] = false # use HTTPS for lookup requests? (if supported)
@@ -1,4 +1,4 @@
1
- require 'timeout' # required for Ruby 1.9.3
1
+ require 'timeout'
2
2
 
3
3
  module Geocoder
4
4
 
@@ -1,9 +1,22 @@
1
1
  require 'resolv'
2
2
  module Geocoder
3
3
  class IpAddress < String
4
+ PRIVATE_IPS = [
5
+ '10.0.0.0/8',
6
+ '172.16.0.0/12',
7
+ '192.168.0.0/16',
8
+ ].map { |ip| IPAddr.new(ip) }.freeze
9
+
10
+ def internal?
11
+ loopback? || private?
12
+ end
4
13
 
5
14
  def loopback?
6
- valid? and (self == "0.0.0.0" or self.match(/\A127\./) or self == "::1")
15
+ valid? and !!(self == "0.0.0.0" or self.match(/\A127\./) or self == "::1")
16
+ end
17
+
18
+ def private?
19
+ valid? && PRIVATE_IPS.any? { |ip| ip.include?(self) }
7
20
  end
8
21
 
9
22
  def valid?
@@ -32,25 +32,26 @@ module Geocoder
32
32
  :google_places_search,
33
33
  :bing,
34
34
  :geocoder_ca,
35
- :geocoder_us,
36
35
  :yandex,
37
36
  :nominatim,
38
37
  :mapbox,
39
38
  :mapquest,
40
- :mapzen,
41
39
  :opencagedata,
42
- :ovi,
43
40
  :pelias,
41
+ :pickpoint,
44
42
  :here,
45
43
  :baidu,
44
+ :tencent,
46
45
  :geocodio,
47
46
  :smarty_streets,
48
- :okf,
49
47
  :postcode_anywhere_uk,
48
+ :postcodes_io,
50
49
  :geoportail_lu,
51
50
  :ban_data_gouv_fr,
52
51
  :test,
53
- :latlon
52
+ :latlon,
53
+ :amap,
54
+ :osmnames
54
55
  ]
55
56
  end
56
57
 
@@ -68,7 +69,13 @@ module Geocoder
68
69
  :pointpin,
69
70
  :maxmind_geoip2,
70
71
  :ipinfo_io,
71
- :ipapi_com
72
+ :ipregistry,
73
+ :ipapi_com,
74
+ :ipdata_co,
75
+ :db_ip_com,
76
+ :ipstack,
77
+ :ip2location,
78
+ :ipgeolocation
72
79
  ]
73
80
  end
74
81
 
@@ -0,0 +1,63 @@
1
+ require 'geocoder/lookups/base'
2
+ require "geocoder/results/amap"
3
+
4
+ module Geocoder::Lookup
5
+ class Amap < Base
6
+
7
+ def name
8
+ "AMap"
9
+ end
10
+
11
+ def required_api_key_parts
12
+ ["key"]
13
+ end
14
+
15
+ def supported_protocols
16
+ [:http]
17
+ end
18
+
19
+ private # ---------------------------------------------------------------
20
+
21
+ def base_query_url(query)
22
+ path = query.reverse_geocode? ? 'regeo' : 'geo'
23
+ "http://restapi.amap.com/v3/geocode/#{path}?"
24
+ end
25
+
26
+ def results(query, reverse = false)
27
+ return [] unless doc = fetch_data(query)
28
+ case [doc['status'], doc['info']]
29
+ when ['1', 'OK']
30
+ return doc['regeocodes'] unless doc['regeocodes'].blank?
31
+ return [doc['regeocode']] unless doc['regeocode'].blank?
32
+ return doc['geocodes'] unless doc['geocodes'].blank?
33
+ when ['0', 'INVALID_USER_KEY']
34
+ raise_error(Geocoder::InvalidApiKey, "invalid api key") ||
35
+ warn("#{self.name} Geocoding API error: invalid api key.")
36
+ else
37
+ raise_error(Geocoder::Error, "server error.") ||
38
+ warn("#{self.name} Geocoding API error: server error - [#{doc['info']}]")
39
+ end
40
+ return []
41
+ end
42
+
43
+ def query_url_params(query)
44
+ params = {
45
+ :key => configuration.api_key,
46
+ :output => "json"
47
+ }
48
+ if query.reverse_geocode?
49
+ params[:location] = revert_coordinates(query.text)
50
+ params[:extensions] = "all"
51
+ params[:coordsys] = "gps"
52
+ else
53
+ params[:address] = query.sanitized_text
54
+ end
55
+ params.merge(super)
56
+ end
57
+
58
+ def revert_coordinates(text)
59
+ [text[1],text[0]].join(",")
60
+ end
61
+
62
+ end
63
+ end
@@ -12,10 +12,6 @@ module Geocoder::Lookup
12
12
  ["key"]
13
13
  end
14
14
 
15
- def query_url(query)
16
- "#{protocol}://api.map.baidu.com/geocoder/v2/?" + url_query_string(query)
17
- end
18
-
19
15
  # HTTP only
20
16
  def supported_protocols
21
17
  [:http]
@@ -23,26 +19,34 @@ module Geocoder::Lookup
23
19
 
24
20
  private # ---------------------------------------------------------------
25
21
 
22
+ def base_query_url(query)
23
+ "#{protocol}://api.map.baidu.com/geocoder/v2/?"
24
+ end
25
+
26
+ def content_key
27
+ 'result'
28
+ end
29
+
26
30
  def results(query, reverse = false)
27
31
  return [] unless doc = fetch_data(query)
28
32
  case doc['status']
29
33
  when 0
30
- return [doc['result']] unless doc['result'].blank?
34
+ return [doc[content_key]] unless doc[content_key].blank?
31
35
  when 1, 3, 4
32
36
  raise_error(Geocoder::Error, "server error.") ||
33
- Geocoder.log(:warn, "Baidu Geocoding API error: server error.")
37
+ Geocoder.log(:warn, "#{name} Geocoding API error: server error.")
34
38
  when 2
35
39
  raise_error(Geocoder::InvalidRequest, "invalid request.") ||
36
- Geocoder.log(:warn, "Baidu Geocoding API error: invalid request.")
40
+ Geocoder.log(:warn, "#{name} Geocoding API error: invalid request.")
37
41
  when 5
38
42
  raise_error(Geocoder::InvalidApiKey, "invalid api key") ||
39
- Geocoder.log(:warn, "Baidu Geocoding API error: invalid api key.")
43
+ Geocoder.log(:warn, "#{name} Geocoding API error: invalid api key.")
40
44
  when 101, 102, 200..299
41
45
  raise_error(Geocoder::RequestDenied, "request denied") ||
42
- Geocoder.log(:warn, "Baidu Geocoding API error: request denied.")
46
+ Geocoder.log(:warn, "#{name} Geocoding API error: request denied.")
43
47
  when 300..399
44
48
  raise_error(Geocoder::OverQueryLimitError, "over query limit.") ||
45
- Geocoder.log(:warn, "Baidu Geocoding API error: over query limit.")
49
+ Geocoder.log(:warn, "#{name} Geocoding API error: over query limit.")
46
50
  end
47
51
  return []
48
52
  end
@@ -1,50 +1,21 @@
1
- require 'geocoder/lookups/base'
1
+ require 'geocoder/lookups/baidu'
2
2
  require 'geocoder/results/baidu_ip'
3
3
 
4
4
  module Geocoder::Lookup
5
- class BaiduIp < Base
5
+ class BaiduIp < Baidu
6
6
 
7
7
  def name
8
8
  "Baidu IP"
9
9
  end
10
10
 
11
- def required_api_key_parts
12
- ["key"]
13
- end
14
-
15
- def query_url(query)
16
- "#{protocol}://api.map.baidu.com/location/ip?" + url_query_string(query)
17
- end
11
+ private # ---------------------------------------------------------------
18
12
 
19
- # HTTP only
20
- def supported_protocols
21
- [:http]
13
+ def base_query_url(query)
14
+ "#{protocol}://api.map.baidu.com/location/ip?"
22
15
  end
23
16
 
24
- private # ---------------------------------------------------------------
25
-
26
- def results(query, reverse = false)
27
- return [] unless doc = fetch_data(query)
28
- case doc['status']
29
- when 0
30
- return [doc['content']] unless doc['content'].blank?
31
- when 1, 3, 4
32
- raise_error(Geocoder::Error, "server error.") ||
33
- Geocoder.log(:warn, "Baidu IP Geocoding API error: server error.")
34
- when 2
35
- raise_error(Geocoder::InvalidRequest, "invalid request.") ||
36
- Geocoder.log(:warn, "Baidu IP Geocoding API error: invalid request.")
37
- when 5
38
- raise_error(Geocoder::InvalidApiKey, "invalid api key.") ||
39
- Geocoder.log(:warn, "Baidu IP Geocoding API error: invalid api key.")
40
- when 101, 102, 200..299
41
- raise_error(Geocoder::RequestDenied, "request denied.") ||
42
- Geocoder.log(:warn, "Baidu IP Geocoding API error: request denied.")
43
- when 300..399
44
- raise_error(Geocoder::OverQueryLimitError, "over query limit") ||
45
- Geocoder.log(:warn, "Baidu IP Geocoding API error: over query limit.")
46
- end
47
- return []
17
+ def content_key
18
+ 'content'
48
19
  end
49
20
 
50
21
  def query_url_params(query)
@@ -14,13 +14,13 @@ module Geocoder::Lookup
14
14
  "https://www.openstreetmap.org/#map=19/#{coordinates.join('/')}"
15
15
  end
16
16
 
17
- def query_url(query)
17
+ private # ---------------------------------------------------------------
18
+
19
+ def base_query_url(query)
18
20
  method = query.reverse_geocode? ? "reverse" : "search"
19
- "#{protocol}://api-adresse.data.gouv.fr/#{method}/?" + url_query_string(query)
21
+ "#{protocol}://api-adresse.data.gouv.fr/#{method}/?"
20
22
  end
21
23
 
22
- private # ---------------------------------------------------------------
23
-
24
24
  def any_result?(doc)
25
25
  doc['features'].any?
26
26
  end
@@ -86,6 +86,12 @@ module Geocoder::Lookup
86
86
  unless (citycode = query.options[:citycode]).nil? || !code_param_is_valid?(citycode)
87
87
  params[:citycode] = citycode.to_s
88
88
  end
89
+ unless (lat = query.options[:lat]).nil? || !latitude_is_valid?(lat)
90
+ params[:lat] = lat
91
+ end
92
+ unless (lon = query.options[:lon]).nil? || !longitude_is_valid?(lon)
93
+ params[:lon] = lon
94
+ end
89
95
  params
90
96
  end
91
97
 
@@ -126,5 +132,12 @@ module Geocoder::Lookup
126
132
  (1..99999).include?(param.to_i)
127
133
  end
128
134
 
135
+ def latitude_is_valid?(param)
136
+ param.to_f <= 90 && param.to_f >= -90
137
+ end
138
+
139
+ def longitude_is_valid?(param)
140
+ param.to_f <= 180 && param.to_f >= -180
141
+ end
129
142
  end
130
143
  end