geocoder 1.6.2 → 1.7.3

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 (58) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +44 -0
  3. data/LICENSE +1 -1
  4. data/README.md +328 -231
  5. data/bin/console +6 -0
  6. data/lib/generators/geocoder/config/templates/initializer.rb +7 -1
  7. data/lib/geocoder/cache.rb +16 -33
  8. data/lib/geocoder/cache_stores/base.rb +40 -0
  9. data/lib/geocoder/cache_stores/generic.rb +35 -0
  10. data/lib/geocoder/cache_stores/redis.rb +34 -0
  11. data/lib/geocoder/configuration.rb +11 -4
  12. data/lib/geocoder/configuration_hash.rb +4 -4
  13. data/lib/geocoder/ip_address.rb +8 -1
  14. data/lib/geocoder/lookup.rb +16 -2
  15. data/lib/geocoder/lookups/abstract_api.rb +46 -0
  16. data/lib/geocoder/lookups/amazon_location_service.rb +54 -0
  17. data/lib/geocoder/lookups/ban_data_gouv_fr.rb +1 -1
  18. data/lib/geocoder/lookups/base.rb +8 -2
  19. data/lib/geocoder/lookups/bing.rb +1 -1
  20. data/lib/geocoder/lookups/esri.rb +6 -0
  21. data/lib/geocoder/lookups/geoapify.rb +72 -0
  22. data/lib/geocoder/lookups/geocodio.rb +1 -1
  23. data/lib/geocoder/lookups/geoip2.rb +4 -0
  24. data/lib/geocoder/lookups/google.rb +7 -2
  25. data/lib/geocoder/lookups/google_places_details.rb +8 -14
  26. data/lib/geocoder/lookups/google_places_search.rb +28 -2
  27. data/lib/geocoder/lookups/google_premier.rb +4 -0
  28. data/lib/geocoder/lookups/ip2location.rb +10 -6
  29. data/lib/geocoder/lookups/ipdata_co.rb +1 -1
  30. data/lib/geocoder/lookups/ipqualityscore.rb +50 -0
  31. data/lib/geocoder/lookups/latlon.rb +1 -2
  32. data/lib/geocoder/lookups/maxmind_local.rb +7 -1
  33. data/lib/geocoder/lookups/melissa_street.rb +41 -0
  34. data/lib/geocoder/lookups/photon.rb +89 -0
  35. data/lib/geocoder/lookups/smarty_streets.rb +6 -1
  36. data/lib/geocoder/lookups/telize.rb +1 -1
  37. data/lib/geocoder/lookups/test.rb +4 -0
  38. data/lib/geocoder/lookups/uk_ordnance_survey_names.rb +1 -1
  39. data/lib/geocoder/lookups/yandex.rb +1 -2
  40. data/lib/geocoder/results/abstract_api.rb +146 -0
  41. data/lib/geocoder/results/amazon_location_service.rb +57 -0
  42. data/lib/geocoder/results/ban_data_gouv_fr.rb +26 -1
  43. data/lib/geocoder/results/db_ip_com.rb +1 -1
  44. data/lib/geocoder/results/esri.rb +5 -2
  45. data/lib/geocoder/results/geoapify.rb +179 -0
  46. data/lib/geocoder/results/ipqualityscore.rb +54 -0
  47. data/lib/geocoder/results/ipregistry.rb +4 -8
  48. data/lib/geocoder/results/mapbox.rb +10 -4
  49. data/lib/geocoder/results/melissa_street.rb +46 -0
  50. data/lib/geocoder/results/nationaal_georegister_nl.rb +1 -1
  51. data/lib/geocoder/results/nominatim.rb +27 -15
  52. data/lib/geocoder/results/photon.rb +119 -0
  53. data/lib/geocoder/util.rb +29 -0
  54. data/lib/geocoder/version.rb +1 -1
  55. metadata +22 -10
  56. data/examples/autoexpire_cache_dalli.rb +0 -62
  57. data/examples/autoexpire_cache_redis.rb +0 -30
  58. data/lib/hash_recursive_merge.rb +0 -73
@@ -0,0 +1,179 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'geocoder/results/base'
4
+
5
+ module Geocoder
6
+ module Result
7
+ # https://apidocs.geoapify.com/docs/geocoding/api
8
+ class Geoapify < Base
9
+ def address(_format = :full)
10
+ properties['formatted']
11
+ end
12
+
13
+ def address_line1
14
+ properties['address_line1']
15
+ end
16
+
17
+ def address_line2
18
+ properties['address_line2']
19
+ end
20
+
21
+ def house_number
22
+ properties['housenumber']
23
+ end
24
+
25
+ def street
26
+ properties['street']
27
+ end
28
+
29
+ def postal_code
30
+ properties['postcode']
31
+ end
32
+
33
+ def district
34
+ properties['district']
35
+ end
36
+
37
+ def suburb
38
+ properties['suburb']
39
+ end
40
+
41
+ def city
42
+ properties['city']
43
+ end
44
+
45
+ def county
46
+ properties['county']
47
+ end
48
+
49
+ def state
50
+ properties['state']
51
+ end
52
+
53
+ # Not currently available in the API
54
+ def state_code
55
+ ''
56
+ end
57
+
58
+ def country
59
+ properties['country']
60
+ end
61
+
62
+ def country_code
63
+ return unless properties['country_code']
64
+
65
+ properties['country_code'].upcase
66
+ end
67
+
68
+ def coordinates
69
+ return unless properties['lat']
70
+ return unless properties['lon']
71
+
72
+ [properties['lat'], properties['lon']]
73
+ end
74
+
75
+ # See: https://tools.ietf.org/html/rfc7946#section-3.1
76
+ #
77
+ # Each feature has a "Point" type in the Geoapify API.
78
+ def geometry
79
+ return unless data['geometry']
80
+
81
+ symbol_hash data['geometry']
82
+ end
83
+
84
+ # See: https://tools.ietf.org/html/rfc7946#section-5
85
+ def bounds
86
+ data['bbox']
87
+ end
88
+
89
+ # Type of the result, one of:
90
+ #
91
+ # * :unknown
92
+ # * :amenity
93
+ # * :building
94
+ # * :street
95
+ # * :suburb
96
+ # * :district
97
+ # * :postcode
98
+ # * :city
99
+ # * :county
100
+ # * :state
101
+ # * :country
102
+ #
103
+ def type
104
+ return :unknown unless properties['result_type']
105
+
106
+ properties['result_type'].to_sym
107
+ end
108
+
109
+ # Distance in meters to given bias:proximity or to given coordinates for
110
+ # reverse geocoding
111
+ def distance
112
+ properties['distance']
113
+ end
114
+
115
+ # Calculated rank for the result, containing the following keys:
116
+ #
117
+ # * `popularity` - The popularity score of the result
118
+ # * `confidence` - The confidence value of the result (0-1)
119
+ # * `match_type` - The result's match type, one of following:
120
+ # * full_match
121
+ # * inner_part
122
+ # * match_by_building
123
+ # * match_by_street
124
+ # * match_by_postcode
125
+ # * match_by_city_or_disrict
126
+ # * match_by_country_or_state
127
+ #
128
+ # Example:
129
+ # {
130
+ # popularity: 8.615793062435909,
131
+ # confidence: 0.88,
132
+ # match_type: :full_match
133
+ # }
134
+ def rank
135
+ return unless properties['rank']
136
+
137
+ r = symbol_hash(properties['rank'])
138
+ r[:match_type] = r[:match_type].to_sym if r[:match_type]
139
+ r
140
+ end
141
+
142
+ # Examples:
143
+ #
144
+ # Open
145
+ # {
146
+ # sourcename: 'openstreetmap',
147
+ # wheelchair: 'limited',
148
+ # wikidata: 'Q186125',
149
+ # wikipedia: 'en:Madison Square Garden',
150
+ # website: 'http://www.thegarden.com/',
151
+ # phone: '12124656741',
152
+ # osm_type: 'W',
153
+ # osm_id: 138141251,
154
+ # continent: 'North America',
155
+ # }
156
+ def datasource
157
+ return unless properties['datasource']
158
+
159
+ symbol_hash properties['datasource']
160
+ end
161
+
162
+ private
163
+
164
+ def properties
165
+ @properties ||= data['properties'] || {}
166
+ end
167
+
168
+ def symbol_hash(orig_hash)
169
+ {}.tap do |result|
170
+ orig_hash.each_key do |key|
171
+ next unless orig_hash[key]
172
+
173
+ result[key.to_sym] = orig_hash[key]
174
+ end
175
+ end
176
+ end
177
+ end
178
+ end
179
+ end
@@ -0,0 +1,54 @@
1
+ require 'geocoder/results/base'
2
+
3
+ module Geocoder
4
+ module Result
5
+ class Ipqualityscore < Base
6
+
7
+ def self.key_method_mappings
8
+ {
9
+ 'request_id' => :request_id,
10
+ 'success' => :success?,
11
+ 'message' => :message,
12
+ 'city' => :city,
13
+ 'region' => :state,
14
+ 'country_code' => :country_code,
15
+ 'mobile' => :mobile?,
16
+ 'fraud_score' => :fraud_score,
17
+ 'ISP' => :isp,
18
+ 'ASN' => :asn,
19
+ 'organization' => :organization,
20
+ 'is_crawler' => :crawler?,
21
+ 'host' => :host,
22
+ 'proxy' => :proxy?,
23
+ 'vpn' => :vpn?,
24
+ 'tor' => :tor?,
25
+ 'active_vpn' => :active_vpn?,
26
+ 'active_tor' => :active_tor?,
27
+ 'recent_abuse' => :recent_abuse?,
28
+ 'bot_status' => :bot?,
29
+ 'connection_type' => :connection_type,
30
+ 'abuse_velocity' => :abuse_velocity,
31
+ 'timezone' => :timezone,
32
+ }
33
+ end
34
+
35
+ key_method_mappings.each_pair do |key, meth|
36
+ define_method meth do
37
+ @data[key]
38
+ end
39
+ end
40
+
41
+ alias_method :state_code, :state
42
+ alias_method :country, :country_code
43
+
44
+ def postal_code
45
+ '' # No suitable fallback
46
+ end
47
+
48
+ def address
49
+ [city, state, country_code].compact.reject(&:empty?).join(', ')
50
+ end
51
+
52
+ end
53
+ end
54
+ end
@@ -9,6 +9,10 @@ module Geocoder::Result
9
9
  @data = flatten_hash(data)
10
10
  end
11
11
 
12
+ def coordinates
13
+ [@data['location_latitude'], @data['location_longitude']]
14
+ end
15
+
12
16
  def flatten_hash(hash)
13
17
  hash.each_with_object({}) do |(k, v), h|
14
18
  if v.is_a? Hash
@@ -35,14 +39,6 @@ module Geocoder::Result
35
39
  @data['location_country_code']
36
40
  end
37
41
 
38
- def latitude
39
- @data['location_latitude']
40
- end
41
-
42
- def longitude
43
- @data['location_longitude']
44
- end
45
-
46
42
  def postal_code
47
43
  @data['location_postal']
48
44
  end
@@ -23,7 +23,10 @@ module Geocoder::Result
23
23
  context_part('region')
24
24
  end
25
25
 
26
- alias_method :state_code, :state
26
+ def state_code
27
+ value = context_part('region', 'short_code')
28
+ value.split('-').last unless value.nil?
29
+ end
27
30
 
28
31
  def postal_code
29
32
  context_part('postcode')
@@ -33,7 +36,10 @@ module Geocoder::Result
33
36
  context_part('country')
34
37
  end
35
38
 
36
- alias_method :country_code, :country
39
+ def country_code
40
+ value = context_part('country', 'short_code')
41
+ value.upcase unless value.nil?
42
+ end
37
43
 
38
44
  def neighborhood
39
45
  context_part('neighborhood')
@@ -45,8 +51,8 @@ module Geocoder::Result
45
51
 
46
52
  private
47
53
 
48
- def context_part(name)
49
- context.map { |c| c['text'] if c['id'] =~ Regexp.new(name) }.compact.first
54
+ def context_part(name, key = 'text')
55
+ (context.detect { |c| c['id'] =~ Regexp.new(name) } || {})[key]
50
56
  end
51
57
 
52
58
  def context
@@ -0,0 +1,46 @@
1
+ require 'geocoder/results/base'
2
+
3
+ module Geocoder::Result
4
+ class MelissaStreet < Base
5
+ def address(format = :full)
6
+ @data['FormattedAddress']
7
+ end
8
+
9
+ def street_address
10
+ @data['AddressLine1']
11
+ end
12
+
13
+ def suffix
14
+ @data['ThoroughfareTrailingType']
15
+ end
16
+
17
+ def number
18
+ @data['PremisesNumber']
19
+ end
20
+
21
+ def city
22
+ @data['Locality']
23
+ end
24
+
25
+ def state_code
26
+ @data['AdministrativeArea']
27
+ end
28
+ alias_method :state, :state_code
29
+
30
+ def country
31
+ @data['CountryName']
32
+ end
33
+
34
+ def country_code
35
+ @data['CountryISO3166_1_Alpha2']
36
+ end
37
+
38
+ def postal_code
39
+ @data['PostalCode']
40
+ end
41
+
42
+ def coordinates
43
+ [@data['Latitude'].to_f, @data['Longitude'].to_f]
44
+ end
45
+ end
46
+ end
@@ -8,7 +8,7 @@ module Geocoder::Result
8
8
  end
9
9
 
10
10
  def coordinates
11
- @data['centroide_ll'][6..-2].split(' ').map(&:to_f)
11
+ @data['centroide_ll'][6..-2].split(' ').map(&:to_f).reverse
12
12
  end
13
13
 
14
14
  def formatted_address
@@ -4,12 +4,12 @@ module Geocoder::Result
4
4
  class Nominatim < Base
5
5
 
6
6
  def poi
7
- return @data['address'][place_type] if @data['address'].key?(place_type)
7
+ return address_data[place_type] if address_data.key?(place_type)
8
8
  return nil
9
9
  end
10
10
 
11
11
  def house_number
12
- @data['address']['house_number']
12
+ address_data['house_number']
13
13
  end
14
14
 
15
15
  def address
@@ -18,65 +18,71 @@ module Geocoder::Result
18
18
 
19
19
  def street
20
20
  %w[road pedestrian highway].each do |key|
21
- return @data['address'][key] if @data['address'].key?(key)
21
+ return address_data[key] if address_data.key?(key)
22
22
  end
23
23
  return nil
24
24
  end
25
25
 
26
26
  def city
27
27
  %w[city town village hamlet].each do |key|
28
- return @data['address'][key] if @data['address'].key?(key)
28
+ return address_data[key] if address_data.key?(key)
29
29
  end
30
30
  return nil
31
31
  end
32
32
 
33
33
  def village
34
- @data['address']['village']
34
+ address_data['village']
35
35
  end
36
36
 
37
37
  def town
38
- @data['address']['town']
38
+ address_data['town']
39
39
  end
40
40
 
41
41
  def state
42
- @data['address']['state']
42
+ address_data['state']
43
43
  end
44
44
 
45
45
  alias_method :state_code, :state
46
46
 
47
47
  def postal_code
48
- @data['address']['postcode']
48
+ address_data['postcode']
49
49
  end
50
50
 
51
51
  def county
52
- @data['address']['county']
52
+ address_data['county']
53
53
  end
54
54
 
55
55
  def country
56
- @data['address']['country']
56
+ address_data['country']
57
57
  end
58
58
 
59
59
  def country_code
60
- @data['address']['country_code']
60
+ address_data['country_code']
61
61
  end
62
62
 
63
63
  def suburb
64
- @data['address']['suburb']
64
+ address_data['suburb']
65
65
  end
66
66
 
67
67
  def city_district
68
- @data['address']['city_district']
68
+ address_data['city_district']
69
69
  end
70
70
 
71
71
  def state_district
72
- @data['address']['state_district']
72
+ address_data['state_district']
73
73
  end
74
74
 
75
75
  def neighbourhood
76
- @data['address']['neighbourhood']
76
+ address_data['neighbourhood']
77
+ end
78
+
79
+ def municipality
80
+ address_data['municipality']
77
81
  end
78
82
 
79
83
  def coordinates
84
+ return [] unless @data['lat'] && @data['lon']
85
+
80
86
  [@data['lat'].to_f, @data['lon'].to_f]
81
87
  end
82
88
 
@@ -105,5 +111,11 @@ module Geocoder::Result
105
111
  end
106
112
  end
107
113
  end
114
+
115
+ private
116
+
117
+ def address_data
118
+ @data['address'] || {}
119
+ end
108
120
  end
109
121
  end
@@ -0,0 +1,119 @@
1
+ require 'geocoder/results/base'
2
+
3
+ module Geocoder::Result
4
+ class Photon < Base
5
+ def name
6
+ properties['name']
7
+ end
8
+
9
+ def address(_format = :full)
10
+ parts = []
11
+ parts << name if name
12
+ parts << street_address if street_address
13
+ parts << city
14
+ parts << state if state
15
+ parts << postal_code
16
+ parts << country
17
+
18
+ parts.join(', ')
19
+ end
20
+
21
+ def street_address
22
+ return unless street
23
+ return street unless house_number
24
+
25
+ "#{house_number} #{street}"
26
+ end
27
+
28
+ def house_number
29
+ properties['housenumber']
30
+ end
31
+
32
+ def street
33
+ properties['street']
34
+ end
35
+
36
+ def postal_code
37
+ properties['postcode']
38
+ end
39
+
40
+ def city
41
+ properties['city']
42
+ end
43
+
44
+ def state
45
+ properties['state']
46
+ end
47
+
48
+ def state_code
49
+ ''
50
+ end
51
+
52
+ def country
53
+ properties['country']
54
+ end
55
+
56
+ def country_code
57
+ ''
58
+ end
59
+
60
+ def coordinates
61
+ return unless geometry
62
+ return unless geometry[:coordinates]
63
+
64
+ geometry[:coordinates].reverse
65
+ end
66
+
67
+ def geometry
68
+ return unless data['geometry']
69
+
70
+ symbol_hash data['geometry']
71
+ end
72
+
73
+ def bounds
74
+ properties['extent']
75
+ end
76
+
77
+ # Type of the result (OSM object type), one of:
78
+ #
79
+ # :node
80
+ # :way
81
+ # :relation
82
+ #
83
+ def type
84
+ {
85
+ 'N' => :node,
86
+ 'W' => :way,
87
+ 'R' => :relation
88
+ }[properties['osm_type']]
89
+ end
90
+
91
+ def osm_id
92
+ properties['osm_id']
93
+ end
94
+
95
+ # See: https://wiki.openstreetmap.org/wiki/Tags
96
+ def osm_tag
97
+ return unless properties['osm_key']
98
+ return properties['osm_key'] unless properties['osm_value']
99
+
100
+ "#{properties['osm_key']}=#{properties['osm_value']}"
101
+ end
102
+
103
+ private
104
+
105
+ def properties
106
+ @properties ||= data['properties'] || {}
107
+ end
108
+
109
+ def symbol_hash(orig_hash)
110
+ {}.tap do |result|
111
+ orig_hash.each_key do |key|
112
+ next unless orig_hash[key]
113
+
114
+ result[key.to_sym] = orig_hash[key]
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Geocoder
4
+ module Util
5
+ #
6
+ # Recursive version of Hash#merge!
7
+ #
8
+ # Adds the contents of +h2+ to +h1+,
9
+ # merging entries in +h1+ with duplicate keys with those from +h2+.
10
+ #
11
+ # Compared with Hash#merge!, this method supports nested hashes.
12
+ # When both +h1+ and +h2+ contains an entry with the same key,
13
+ # it merges and returns the values from both hashes.
14
+ #
15
+ # h1 = {"a" => 100, "b" => 200, "c" => {"c1" => 12, "c2" => 14}}
16
+ # h2 = {"b" => 254, "c" => {"c1" => 16, "c3" => 94}}
17
+ # recursive_hash_merge(h1, h2) #=> {"a" => 100, "b" => 254, "c" => {"c1" => 16, "c2" => 14, "c3" => 94}}
18
+ #
19
+ # Simply using Hash#merge! would return
20
+ #
21
+ # h1.merge!(h2) #=> {"a" => 100, "b" = >254, "c" => {"c1" => 16, "c3" => 94}}
22
+ #
23
+ def self.recursive_hash_merge(h1, h2)
24
+ h1.merge!(h2) do |_key, oldval, newval|
25
+ oldval.class == h1.class ? self.recursive_hash_merge(oldval, newval) : newval
26
+ end
27
+ end
28
+ end
29
+ end
@@ -1,3 +1,3 @@
1
1
  module Geocoder
2
- VERSION = "1.6.2"
2
+ VERSION = "1.7.3"
3
3
  end