geocoder 1.6.6 → 1.8.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +68 -2
  3. data/README.md +355 -211
  4. data/examples/app_defined_lookup_services.rb +22 -0
  5. data/lib/generators/geocoder/config/templates/initializer.rb +6 -1
  6. data/lib/geocoder/cache.rb +14 -35
  7. data/lib/geocoder/cache_stores/base.rb +40 -0
  8. data/lib/geocoder/cache_stores/generic.rb +35 -0
  9. data/lib/geocoder/cache_stores/redis.rb +34 -0
  10. data/lib/geocoder/configuration.rb +17 -4
  11. data/lib/geocoder/ip_address.rb +9 -0
  12. data/lib/geocoder/lookup.rb +37 -5
  13. data/lib/geocoder/lookups/abstract_api.rb +46 -0
  14. data/lib/geocoder/lookups/amap.rb +2 -2
  15. data/lib/geocoder/lookups/amazon_location_service.rb +58 -0
  16. data/lib/geocoder/lookups/azure.rb +56 -0
  17. data/lib/geocoder/lookups/ban_data_gouv_fr.rb +1 -1
  18. data/lib/geocoder/lookups/base.rb +2 -1
  19. data/lib/geocoder/lookups/bing.rb +2 -2
  20. data/lib/geocoder/lookups/esri.rb +18 -5
  21. data/lib/geocoder/lookups/freegeoip.rb +8 -6
  22. data/lib/geocoder/lookups/geoapify.rb +78 -0
  23. data/lib/geocoder/lookups/geoip2.rb +4 -0
  24. data/lib/geocoder/lookups/geoportail_lu.rb +1 -1
  25. data/lib/geocoder/lookups/google_places_details.rb +20 -0
  26. data/lib/geocoder/lookups/google_places_search.rb +21 -5
  27. data/lib/geocoder/lookups/here.rb +25 -20
  28. data/lib/geocoder/lookups/ip2location.rb +10 -6
  29. data/lib/geocoder/lookups/ip2location_io.rb +62 -0
  30. data/lib/geocoder/lookups/ip2location_lite.rb +40 -0
  31. data/lib/geocoder/lookups/ipbase.rb +49 -0
  32. data/lib/geocoder/lookups/ipdata_co.rb +1 -1
  33. data/lib/geocoder/lookups/ipqualityscore.rb +50 -0
  34. data/lib/geocoder/lookups/location_iq.rb +5 -1
  35. data/lib/geocoder/lookups/mapbox.rb +3 -3
  36. data/lib/geocoder/lookups/melissa_street.rb +41 -0
  37. data/lib/geocoder/lookups/pc_miler.rb +85 -0
  38. data/lib/geocoder/lookups/pdok_nl.rb +43 -0
  39. data/lib/geocoder/lookups/photon.rb +89 -0
  40. data/lib/geocoder/lookups/test.rb +1 -0
  41. data/lib/geocoder/lookups/twogis.rb +58 -0
  42. data/lib/geocoder/lookups/uk_ordnance_survey_names.rb +1 -1
  43. data/lib/geocoder/lookups/yandex.rb +3 -3
  44. data/lib/geocoder/query.rb +1 -1
  45. data/lib/geocoder/results/abstract_api.rb +146 -0
  46. data/lib/geocoder/results/amazon_location_service.rb +62 -0
  47. data/lib/geocoder/results/azure.rb +65 -0
  48. data/lib/geocoder/results/ban_data_gouv_fr.rb +1 -1
  49. data/lib/geocoder/results/esri.rb +5 -2
  50. data/lib/geocoder/results/geoapify.rb +179 -0
  51. data/lib/geocoder/results/here.rb +20 -25
  52. data/lib/geocoder/results/ip2location_io.rb +21 -0
  53. data/lib/geocoder/results/ip2location_lite.rb +47 -0
  54. data/lib/geocoder/results/ipbase.rb +40 -0
  55. data/lib/geocoder/results/ipqualityscore.rb +54 -0
  56. data/lib/geocoder/results/mapbox.rb +34 -10
  57. data/lib/geocoder/results/melissa_street.rb +46 -0
  58. data/lib/geocoder/results/nominatim.rb +24 -16
  59. data/lib/geocoder/results/pc_miler.rb +98 -0
  60. data/lib/geocoder/results/pdok_nl.rb +62 -0
  61. data/lib/geocoder/results/photon.rb +119 -0
  62. data/lib/geocoder/results/twogis.rb +76 -0
  63. data/lib/geocoder/version.rb +1 -1
  64. data/lib/maxmind_database.rb +12 -12
  65. data/lib/tasks/maxmind.rake +1 -1
  66. metadata +65 -11
  67. data/examples/autoexpire_cache_dalli.rb +0 -62
  68. data/examples/autoexpire_cache_redis.rb +0 -30
  69. data/lib/geocoder/lookups/dstk.rb +0 -22
  70. data/lib/geocoder/results/dstk.rb +0 -6
@@ -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
@@ -7,76 +7,71 @@ module Geocoder::Result
7
7
  # A string in the given format.
8
8
  #
9
9
  def address(format = :full)
10
- address_data['Label']
10
+ address_data["label"]
11
11
  end
12
12
 
13
13
  ##
14
14
  # A two-element array: [lat, lon].
15
15
  #
16
16
  def coordinates
17
- fail unless d = @data['Location']['DisplayPosition']
18
- [d['Latitude'].to_f, d['Longitude'].to_f]
17
+ fail unless d = @data["position"]
18
+ [d["lat"].to_f, d["lng"].to_f]
19
19
  end
20
20
 
21
21
  def route
22
- address_data['Street']
22
+ address_data["street"]
23
23
  end
24
24
 
25
25
  def street_number
26
- address_data['HouseNumber']
26
+ address_data["houseNumber"]
27
27
  end
28
28
 
29
29
  def state
30
- fail unless d = address_data['AdditionalData']
31
- if v = d.find{|ad| ad['key']=='StateName'}
32
- return v['value']
33
- end
30
+ address_data["state"]
34
31
  end
35
32
 
36
33
  def province
37
- address_data['County']
34
+ address_data["county"]
38
35
  end
39
36
 
40
37
  def postal_code
41
- address_data['PostalCode']
38
+ address_data["postalCode"]
42
39
  end
43
40
 
44
41
  def city
45
- address_data['City']
42
+ address_data["city"]
46
43
  end
47
44
 
48
45
  def state_code
49
- address_data['State']
46
+ address_data["stateCode"]
50
47
  end
51
48
 
52
49
  def province_code
53
- address_data['State']
50
+ address_data["state"]
54
51
  end
55
52
 
56
53
  def country
57
- fail unless d = address_data['AdditionalData']
58
- if v = d.find{|ad| ad['key']=='CountryName'}
59
- return v['value']
60
- end
54
+ address_data["countryName"]
61
55
  end
62
56
 
63
57
  def country_code
64
- address_data['Country']
58
+ address_data["countryCode"]
65
59
  end
66
60
 
67
61
  def viewport
68
- map_view = data['Location']['MapView'] || fail
69
- south = map_view['BottomRight']['Latitude']
70
- west = map_view['TopLeft']['Longitude']
71
- north = map_view['TopLeft']['Latitude']
72
- east = map_view['BottomRight']['Longitude']
62
+ return [] if data["resultType"] == "place"
63
+ map_view = data["mapView"]
64
+ south = map_view["south"]
65
+ west = map_view["west"]
66
+ north = map_view["north"]
67
+ east = map_view["east"]
73
68
  [south, west, north, east]
74
69
  end
75
70
 
76
71
  private # ----------------------------------------------------------------
77
72
 
78
73
  def address_data
79
- @data['Location']['Address'] || fail
74
+ @data["address"] || fail
80
75
  end
81
76
  end
82
77
  end
@@ -0,0 +1,21 @@
1
+ require 'geocoder/results/base'
2
+
3
+ module Geocoder::Result
4
+ class Ip2locationIo < Base
5
+
6
+ def address(format = :full)
7
+ "#{city_name} #{zip_code}, #{country_name}".sub(/^[ ,]*/, '')
8
+ end
9
+
10
+ def self.response_attributes
11
+ %w[ip country_code country_name region_name city_name latitude longitude
12
+ zip_code time_zone asn as is_proxy]
13
+ end
14
+
15
+ response_attributes.each do |attr|
16
+ define_method attr do
17
+ @data[attr] || ""
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,47 @@
1
+ require 'geocoder/results/base'
2
+
3
+ module Geocoder::Result
4
+ class Ip2locationLite < Base
5
+
6
+ def coordinates
7
+ [@data[:latitude], @data[:longitude]]
8
+ end
9
+
10
+ def city
11
+ @data[:city]
12
+ end
13
+
14
+ def state
15
+ @data[:region]
16
+ end
17
+
18
+ def state_code
19
+ "" # Not available in Maxmind's database
20
+ end
21
+
22
+ def country
23
+ @data[:country_long]
24
+ end
25
+
26
+ def country_code
27
+ @data[:country_short]
28
+ end
29
+
30
+ def postal_code
31
+ @data[:zipcode]
32
+ end
33
+
34
+ def self.response_attributes
35
+ %w[country_short country_long region latitude longitude isp
36
+ domain netspeed areacode iddcode timezone zipcode weatherstationname
37
+ weatherstationcode mcc mnc mobilebrand elevation usagetype addresstype
38
+ category district asn as]
39
+ end
40
+
41
+ response_attributes.each do |a|
42
+ define_method a do
43
+ @data[a] || ""
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,40 @@
1
+ require 'geocoder/results/base'
2
+
3
+ module Geocoder::Result
4
+ class Ipbase < Base
5
+ def ip
6
+ @data["data"]['ip']
7
+ end
8
+
9
+ def country_code
10
+ @data["data"]["location"]["country"]["alpha2"]
11
+ end
12
+
13
+ def country
14
+ @data["data"]["location"]["country"]["name"]
15
+ end
16
+
17
+ def state_code
18
+ @data["data"]["location"]["region"]["alpha2"]
19
+ end
20
+
21
+ def state
22
+ @data["data"]["location"]["region"]["name"]
23
+ end
24
+
25
+ def city
26
+ @data["data"]["location"]["city"]["name"]
27
+ end
28
+
29
+ def postal_code
30
+ @data["data"]["location"]["zip"]
31
+ end
32
+
33
+ def coordinates
34
+ [
35
+ @data["data"]["location"]["latitude"].to_f,
36
+ @data["data"]["location"]["longitude"].to_f
37
+ ]
38
+ end
39
+ end
40
+ 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
@@ -16,37 +16,61 @@ module Geocoder::Result
16
16
  end
17
17
 
18
18
  def city
19
- context_part('place')
19
+ data_part('place') || context_part('place')
20
20
  end
21
21
 
22
22
  def state
23
- context_part('region')
23
+ data_part('region') || context_part('region')
24
24
  end
25
25
 
26
- alias_method :state_code, :state
26
+ def state_code
27
+ if id_matches_name?(data['id'], 'region')
28
+ value = data['properties']['short_code']
29
+ else
30
+ value = context_part('region', 'short_code')
31
+ end
32
+
33
+ value.split('-').last unless value.nil?
34
+ end
27
35
 
28
36
  def postal_code
29
- context_part('postcode')
37
+ data_part('postcode') || context_part('postcode')
30
38
  end
31
39
 
32
40
  def country
33
- context_part('country')
41
+ data_part('country') || context_part('country')
34
42
  end
35
43
 
36
- alias_method :country_code, :country
44
+ def country_code
45
+ if id_matches_name?(data['id'], 'country')
46
+ value = data['properties']['short_code']
47
+ else
48
+ value = context_part('country', 'short_code')
49
+ end
50
+
51
+ value.upcase unless value.nil?
52
+ end
37
53
 
38
54
  def neighborhood
39
- context_part('neighborhood')
55
+ data_part('neighborhood') || context_part('neighborhood')
40
56
  end
41
57
 
42
58
  def address
43
- [place_name, street, city, state, postal_code, country].compact.join(', ')
59
+ data['place_name']
44
60
  end
45
61
 
46
62
  private
47
63
 
48
- def context_part(name)
49
- context.map { |c| c['text'] if c['id'] =~ Regexp.new(name) }.compact.first
64
+ def id_matches_name?(id, name)
65
+ id =~ Regexp.new(name)
66
+ end
67
+
68
+ def data_part(name)
69
+ data['text'] if id_matches_name?(data['id'], name)
70
+ end
71
+
72
+ def context_part(name, key = 'text')
73
+ (context.detect { |c| id_matches_name?(c['id'], name) } || {})[key]
50
74
  end
51
75
 
52
76
  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
@@ -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,69 +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
77
  end
78
78
 
79
79
  def municipality
80
- @data['address']['municipality']
80
+ address_data['municipality']
81
81
  end
82
82
 
83
83
  def coordinates
84
+ return [] unless @data['lat'] && @data['lon']
85
+
84
86
  [@data['lat'].to_f, @data['lon'].to_f]
85
87
  end
86
88
 
@@ -109,5 +111,11 @@ module Geocoder::Result
109
111
  end
110
112
  end
111
113
  end
114
+
115
+ private
116
+
117
+ def address_data
118
+ @data['address'] || {}
119
+ end
112
120
  end
113
121
  end