geocoder 1.5.0 → 1.6.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +29 -2
  3. data/LICENSE +1 -1
  4. data/README.md +15 -17
  5. data/bin/console +7 -0
  6. data/examples/autoexpire_cache_redis.rb +2 -0
  7. data/lib/easting_northing.rb +171 -0
  8. data/lib/geocoder/calculations.rb +1 -1
  9. data/lib/geocoder/ip_address.rb +13 -0
  10. data/lib/geocoder/lookup.rb +8 -4
  11. data/lib/geocoder/lookups/baidu_ip.rb +1 -1
  12. data/lib/geocoder/lookups/ban_data_gouv_fr.rb +13 -0
  13. data/lib/geocoder/lookups/base.rb +7 -2
  14. data/lib/geocoder/lookups/bing.rb +2 -1
  15. data/lib/geocoder/lookups/esri.rb +38 -13
  16. data/lib/geocoder/lookups/freegeoip.rb +7 -7
  17. data/lib/geocoder/lookups/here.rb +28 -22
  18. data/lib/geocoder/lookups/ip2location.rb +7 -14
  19. data/lib/geocoder/lookups/ipapi_com.rb +2 -1
  20. data/lib/geocoder/lookups/ipdata_co.rb +5 -4
  21. data/lib/geocoder/lookups/ipgeolocation.rb +51 -0
  22. data/lib/geocoder/lookups/ipinfo_io.rb +2 -11
  23. data/lib/geocoder/lookups/ipregistry.rb +68 -0
  24. data/lib/geocoder/lookups/ipstack.rb +2 -2
  25. data/lib/geocoder/lookups/maxmind.rb +2 -2
  26. data/lib/geocoder/lookups/maxmind_geoip2.rb +4 -7
  27. data/lib/geocoder/lookups/nationaal_georegister_nl.rb +38 -0
  28. data/lib/geocoder/lookups/osmnames.rb +57 -0
  29. data/lib/geocoder/lookups/pelias.rb +2 -3
  30. data/lib/geocoder/lookups/pickpoint.rb +1 -1
  31. data/lib/geocoder/lookups/pointpin.rb +3 -3
  32. data/lib/geocoder/lookups/smarty_streets.rb +13 -3
  33. data/lib/geocoder/lookups/telize.rb +2 -2
  34. data/lib/geocoder/lookups/tencent.rb +59 -0
  35. data/lib/geocoder/lookups/uk_ordnance_survey_names.rb +59 -0
  36. data/lib/geocoder/lookups/yandex.rb +2 -2
  37. data/lib/geocoder/query.rb +14 -0
  38. data/lib/geocoder/railtie.rb +1 -1
  39. data/lib/geocoder/results/baidu.rb +0 -4
  40. data/lib/geocoder/results/ban_data_gouv_fr.rb +1 -1
  41. data/lib/geocoder/results/here.rb +4 -1
  42. data/lib/geocoder/results/ipgeolocation.rb +59 -0
  43. data/lib/geocoder/results/ipregistry.rb +308 -0
  44. data/lib/geocoder/results/nationaal_georegister_nl.rb +62 -0
  45. data/lib/geocoder/results/osmnames.rb +56 -0
  46. data/lib/geocoder/results/smarty_streets.rb +48 -18
  47. data/lib/geocoder/results/tencent.rb +72 -0
  48. data/lib/geocoder/results/uk_ordnance_survey_names.rb +59 -0
  49. data/lib/geocoder/results/yandex.rb +217 -59
  50. data/lib/geocoder/sql.rb +4 -4
  51. data/lib/geocoder/stores/active_record.rb +1 -1
  52. data/lib/geocoder/version.rb +1 -1
  53. data/lib/hash_recursive_merge.rb +1 -2
  54. data/lib/maxmind_database.rb +3 -3
  55. metadata +18 -13
  56. data/lib/geocoder/lookups/geocoder_us.rb +0 -51
  57. data/lib/geocoder/lookups/mapzen.rb +0 -15
  58. data/lib/geocoder/results/geocoder_us.rb +0 -39
  59. data/lib/geocoder/results/mapzen.rb +0 -5
@@ -0,0 +1,72 @@
1
+ require 'geocoder/results/base'
2
+
3
+ module Geocoder::Result
4
+ class Tencent < Base
5
+
6
+ def coordinates
7
+ ['lat', 'lng'].map{ |i| @data['location'][i] }
8
+ end
9
+
10
+ def address
11
+ "#{province}#{city}#{district}#{street}#{street_number}"
12
+
13
+ #@data['title'] or @data['address']
14
+ end
15
+
16
+ # NOTE: The Tencent reverse geocoding API has the field named
17
+ # 'address_component' compared to 'address_components' in the
18
+ # regular geocoding API.
19
+ def province
20
+ @data['address_components'] and (@data['address_components']['province']) or
21
+ (@data['address_component'] and @data['address_component']['province']) or
22
+ ""
23
+ end
24
+
25
+ alias_method :state, :province
26
+
27
+ def city
28
+ @data['address_components'] and (@data['address_components']['city']) or
29
+ (@data['address_component'] and @data['address_component']['city']) or
30
+ ""
31
+ end
32
+
33
+ def district
34
+ @data['address_components'] and (@data['address_components']['district']) or
35
+ (@data['address_component'] and @data['address_component']['district']) or
36
+ ""
37
+ end
38
+
39
+ def street
40
+ @data['address_components'] and (@data['address_components']['street']) or
41
+ (@data['address_component'] and @data['address_component']['street']) or
42
+ ""
43
+ end
44
+
45
+ def street_number
46
+ @data['address_components'] and (@data['address_components']['street_number']) or
47
+ (@data['address_component'] and @data['address_component']['street_number']) or
48
+ ""
49
+ end
50
+
51
+ def address_components
52
+ @data['address_components'] or @data['address_component']
53
+ end
54
+
55
+ def state_code
56
+ ""
57
+ end
58
+
59
+ def postal_code
60
+ ""
61
+ end
62
+
63
+ def country
64
+ "China"
65
+ end
66
+
67
+ def country_code
68
+ "CN"
69
+ end
70
+
71
+ end
72
+ end
@@ -0,0 +1,59 @@
1
+ require 'geocoder/results/base'
2
+ require 'easting_northing'
3
+
4
+ module Geocoder::Result
5
+ class UkOrdnanceSurveyNames < Base
6
+
7
+ def coordinates
8
+ @coordinates ||= Geocoder::EastingNorthing.new(
9
+ easting: data['GEOMETRY_X'],
10
+ northing: data['GEOMETRY_Y'],
11
+ ).lat_lng
12
+ end
13
+
14
+ def city
15
+ is_postcode? ? data['DISTRICT_BOROUGH'] : data['NAME1']
16
+ end
17
+
18
+ def county
19
+ data['COUNTY_UNITARY']
20
+ end
21
+ alias state county
22
+
23
+ def county_code
24
+ code_from_uri data['COUNTY_UNITARY_URI']
25
+ end
26
+ alias state_code county_code
27
+
28
+ def province
29
+ data['REGION']
30
+ end
31
+
32
+ def province_code
33
+ code_from_uri data['REGION_URI']
34
+ end
35
+
36
+ def postal_code
37
+ is_postcode? ? data['NAME1'] : ''
38
+ end
39
+
40
+ def country
41
+ 'United Kingdom'
42
+ end
43
+
44
+ def country_code
45
+ 'UK'
46
+ end
47
+
48
+ private
49
+
50
+ def is_postcode?
51
+ data['LOCAL_TYPE'] == 'Postcode'
52
+ end
53
+
54
+ def code_from_uri(uri)
55
+ return '' if uri.nil?
56
+ uri.split('/').last
57
+ end
58
+ end
59
+ end
@@ -2,78 +2,223 @@ require 'geocoder/results/base'
2
2
 
3
3
  module Geocoder::Result
4
4
  class Yandex < Base
5
+ # Yandex result has difficult tree structure,
6
+ # and presence of some nodes depends on exact search case.
7
+
8
+ # Also Yandex lacks documentation about it.
9
+ # See https://tech.yandex.com/maps/doc/geocoder/desc/concepts/response_structure-docpage/
10
+
11
+ # Ultimatly, we need to find Locality and/or Thoroughfare data.
12
+
13
+ # It may resides on the top (ADDRESS_DETAILS) level.
14
+ # example: 'Baltic Sea'
15
+ # "AddressDetails": {
16
+ # "Locality": {
17
+ # "Premise": {
18
+ # "PremiseName": "Baltic Sea"
19
+ # }
20
+ # }
21
+ # }
22
+
23
+ ADDRESS_DETAILS = %w[
24
+ GeoObject metaDataProperty GeocoderMetaData
25
+ AddressDetails
26
+ ].freeze
27
+
28
+ # On COUNTRY_LEVEL.
29
+ # example: 'Potomak'
30
+ # "AddressDetails": {
31
+ # "Country": {
32
+ # "AddressLine": "reka Potomak",
33
+ # "CountryNameCode": "US",
34
+ # "CountryName": "United States of America",
35
+ # "Locality": {
36
+ # "Premise": {
37
+ # "PremiseName": "reka Potomak"
38
+ # }
39
+ # }
40
+ # }
41
+ # }
42
+
43
+ COUNTRY_LEVEL = %w[
44
+ GeoObject metaDataProperty GeocoderMetaData
45
+ AddressDetails Country
46
+ ].freeze
47
+
48
+ # On ADMIN_LEVEL (usually state or city)
49
+ # example: 'Moscow, Tverskaya'
50
+ # "AddressDetails": {
51
+ # "Country": {
52
+ # "AddressLine": "Moscow, Tverskaya Street",
53
+ # "CountryNameCode": "RU",
54
+ # "CountryName": "Russia",
55
+ # "AdministrativeArea": {
56
+ # "AdministrativeAreaName": "Moscow",
57
+ # "Locality": {
58
+ # "LocalityName": "Moscow",
59
+ # "Thoroughfare": {
60
+ # "ThoroughfareName": "Tverskaya Street"
61
+ # }
62
+ # }
63
+ # }
64
+ # }
65
+ # }
66
+
67
+ ADMIN_LEVEL = %w[
68
+ GeoObject metaDataProperty GeocoderMetaData
69
+ AddressDetails Country
70
+ AdministrativeArea
71
+ ].freeze
72
+
73
+ # On SUBADMIN_LEVEL (may refer to urban district)
74
+ # example: 'Moscow Region, Krasnogorsk'
75
+ # "AddressDetails": {
76
+ # "Country": {
77
+ # "AddressLine": "Moscow Region, Krasnogorsk",
78
+ # "CountryNameCode": "RU",
79
+ # "CountryName": "Russia",
80
+ # "AdministrativeArea": {
81
+ # "AdministrativeAreaName": "Moscow Region",
82
+ # "SubAdministrativeArea": {
83
+ # "SubAdministrativeAreaName": "gorodskoy okrug Krasnogorsk",
84
+ # "Locality": {
85
+ # "LocalityName": "Krasnogorsk"
86
+ # }
87
+ # }
88
+ # }
89
+ # }
90
+ # }
91
+
92
+ SUBADMIN_LEVEL = %w[
93
+ GeoObject metaDataProperty GeocoderMetaData
94
+ AddressDetails Country
95
+ AdministrativeArea
96
+ SubAdministrativeArea
97
+ ].freeze
98
+
99
+ # On DEPENDENT_LOCALITY_1 (may refer to district of city)
100
+ # example: 'Paris, Etienne Marcel'
101
+ # "AddressDetails": {
102
+ # "Country": {
103
+ # "AddressLine": "Île-de-France, Paris, 1er Arrondissement, Rue Étienne Marcel",
104
+ # "CountryNameCode": "FR",
105
+ # "CountryName": "France",
106
+ # "AdministrativeArea": {
107
+ # "AdministrativeAreaName": "Île-de-France",
108
+ # "Locality": {
109
+ # "LocalityName": "Paris",
110
+ # "DependentLocality": {
111
+ # "DependentLocalityName": "1er Arrondissement",
112
+ # "Thoroughfare": {
113
+ # "ThoroughfareName": "Rue Étienne Marcel"
114
+ # }
115
+ # }
116
+ # }
117
+ # }
118
+ # }
119
+ # }
120
+
121
+ DEPENDENT_LOCALITY_1 = %w[
122
+ GeoObject metaDataProperty GeocoderMetaData
123
+ AddressDetails Country
124
+ AdministrativeArea Locality
125
+ DependentLocality
126
+ ].freeze
127
+
128
+ # On DEPENDENT_LOCALITY_2 (for special cases like turkish "mahalle")
129
+ # https://en.wikipedia.org/wiki/Mahalle
130
+ # example: 'Istanbul Mabeyinci Yokuşu 17'
131
+
132
+ # "AddressDetails": {
133
+ # "Country": {
134
+ # "AddressLine": "İstanbul, Fatih, Saraç İshak Mah., Mabeyinci Yokuşu, 17",
135
+ # "CountryNameCode": "TR",
136
+ # "CountryName": "Turkey",
137
+ # "AdministrativeArea": {
138
+ # "AdministrativeAreaName": "İstanbul",
139
+ # "SubAdministrativeArea": {
140
+ # "SubAdministrativeAreaName": "Fatih",
141
+ # "Locality": {
142
+ # "DependentLocality": {
143
+ # "DependentLocalityName": "Saraç İshak Mah.",
144
+ # "Thoroughfare": {
145
+ # "ThoroughfareName": "Mabeyinci Yokuşu",
146
+ # "Premise": {
147
+ # "PremiseNumber": "17"
148
+ # }
149
+ # }
150
+ # }
151
+ # }
152
+ # }
153
+ # }
154
+ # }
155
+ # }
156
+
157
+ DEPENDENT_LOCALITY_2 = %w[
158
+ GeoObject metaDataProperty GeocoderMetaData
159
+ AddressDetails Country
160
+ AdministrativeArea
161
+ SubAdministrativeArea Locality
162
+ DependentLocality
163
+ ].freeze
5
164
 
6
165
  def coordinates
7
166
  @data['GeoObject']['Point']['pos'].split(' ').reverse.map(&:to_f)
8
167
  end
9
168
 
10
- def address(format = :full)
169
+ def address(_format = :full)
11
170
  @data['GeoObject']['metaDataProperty']['GeocoderMetaData']['text']
12
171
  end
13
172
 
14
173
  def city
15
- if state.empty? and address_details and address_details.has_key? 'Locality'
16
- address_details['Locality']['LocalityName']
17
- elsif sub_state.empty? and address_details and address_details.has_key? 'AdministrativeArea' and
18
- address_details['AdministrativeArea'].has_key? 'Locality'
19
- address_details['AdministrativeArea']['Locality']['LocalityName']
20
- elsif not sub_state_city.empty?
21
- sub_state_city
22
- else
23
- ""
24
- end
174
+ result =
175
+ if state.empty?
176
+ find_in_hash(@data, *COUNTRY_LEVEL, 'Locality', 'LocalityName')
177
+ elsif sub_state.empty?
178
+ find_in_hash(@data, *ADMIN_LEVEL, 'Locality', 'LocalityName')
179
+ else
180
+ find_in_hash(@data, *SUBADMIN_LEVEL, 'Locality', 'LocalityName')
181
+ end
182
+
183
+ result || ""
25
184
  end
26
185
 
27
186
  def country
28
- if address_details
29
- address_details['CountryName']
30
- else
31
- ""
32
- end
187
+ find_in_hash(@data, *COUNTRY_LEVEL, 'CountryName') || ""
33
188
  end
34
189
 
35
190
  def country_code
36
- if address_details
37
- address_details['CountryNameCode']
38
- else
39
- ""
40
- end
191
+ find_in_hash(@data, *COUNTRY_LEVEL, 'CountryNameCode') || ""
41
192
  end
42
193
 
43
194
  def state
44
- if address_details and address_details['AdministrativeArea']
45
- address_details['AdministrativeArea']['AdministrativeAreaName']
46
- else
47
- ""
48
- end
195
+ find_in_hash(@data, *ADMIN_LEVEL, 'AdministrativeAreaName') || ""
49
196
  end
50
197
 
51
198
  def sub_state
52
- if !state.empty? and address_details and address_details['AdministrativeArea']['SubAdministrativeArea']
53
- address_details['AdministrativeArea']['SubAdministrativeArea']['SubAdministrativeAreaName']
54
- else
55
- ""
56
- end
199
+ return "" if state.empty?
200
+ find_in_hash(@data, *SUBADMIN_LEVEL, 'SubAdministrativeAreaName') || ""
57
201
  end
58
202
 
59
203
  def state_code
60
204
  ""
61
205
  end
62
206
 
63
- def postal_code
64
- ""
207
+ def street
208
+ thoroughfare_data.is_a?(Hash) ? thoroughfare_data['ThoroughfareName'] : ""
65
209
  end
66
210
 
67
- def premise_name
68
- address_details['Locality']['Premise']['PremiseName']
211
+ def street_number
212
+ premise.is_a?(Hash) ? premise.fetch('PremiseNumber', "") : ""
69
213
  end
70
214
 
71
- def street
72
- thoroughfare_data && thoroughfare_data['ThoroughfareName']
215
+ def premise_name
216
+ premise.is_a?(Hash) ? premise.fetch('PremiseName', "") : ""
73
217
  end
74
218
 
75
- def street_number
76
- thoroughfare_data && thoroughfare_data['Premise'] && thoroughfare_data['Premise']['PremiseNumber']
219
+ def postal_code
220
+ return "" unless premise.is_a?(Hash)
221
+ find_in_hash(premise, 'PostalCode', 'PostalCodeNumber') || ""
77
222
  end
78
223
 
79
224
  def kind
@@ -93,42 +238,55 @@ module Geocoder::Result
93
238
 
94
239
  private # ----------------------------------------------------------------
95
240
 
96
- def thoroughfare_data
97
- locality_data && locality_data['Thoroughfare']
241
+ def top_level_locality
242
+ find_in_hash(@data, *ADDRESS_DETAILS, 'Locality')
98
243
  end
99
244
 
100
- def locality_data
101
- dependent_locality && subadmin_locality && admin_locality
245
+ def country_level_locality
246
+ find_in_hash(@data, *COUNTRY_LEVEL, 'Locality')
102
247
  end
103
248
 
104
249
  def admin_locality
105
- address_details && address_details['AdministrativeArea'] &&
106
- address_details['AdministrativeArea']['Locality']
250
+ find_in_hash(@data, *ADMIN_LEVEL, 'Locality')
107
251
  end
108
252
 
109
253
  def subadmin_locality
110
- address_details && address_details['AdministrativeArea'] &&
111
- address_details['AdministrativeArea']['SubAdministrativeArea'] &&
112
- address_details['AdministrativeArea']['SubAdministrativeArea']['Locality']
254
+ find_in_hash(@data, *SUBADMIN_LEVEL, 'Locality')
113
255
  end
114
256
 
115
257
  def dependent_locality
116
- address_details && address_details['AdministrativeArea'] &&
117
- address_details['AdministrativeArea']['SubAdministrativeArea'] &&
118
- address_details['AdministrativeArea']['SubAdministrativeArea']['Locality'] &&
119
- address_details['AdministrativeArea']['SubAdministrativeArea']['Locality']['DependentLocality']
258
+ find_in_hash(@data, *DEPENDENT_LOCALITY_1) ||
259
+ find_in_hash(@data, *DEPENDENT_LOCALITY_2)
260
+ end
261
+
262
+ def locality_data
263
+ dependent_locality || subadmin_locality || admin_locality ||
264
+ country_level_locality || top_level_locality
265
+ end
266
+
267
+ def thoroughfare_data
268
+ locality_data['Thoroughfare'] if locality_data.is_a?(Hash)
120
269
  end
121
270
 
122
- def address_details
123
- @data['GeoObject']['metaDataProperty']['GeocoderMetaData']['AddressDetails']['Country']
271
+ def premise
272
+ if thoroughfare_data.is_a?(Hash)
273
+ thoroughfare_data['Premise']
274
+ elsif locality_data.is_a?(Hash)
275
+ locality_data['Premise']
276
+ end
124
277
  end
125
278
 
126
- def sub_state_city
127
- if !sub_state.empty? and address_details and address_details['AdministrativeArea']['SubAdministrativeArea'].has_key? 'Locality'
128
- address_details['AdministrativeArea']['SubAdministrativeArea']['Locality']['LocalityName'] || ""
129
- else
130
- ""
279
+ def find_in_hash(source, *keys)
280
+ key = keys.shift
281
+ result = source[key]
282
+
283
+ if keys.empty?
284
+ return result
285
+ elsif !result.is_a?(Hash)
286
+ return nil
131
287
  end
288
+
289
+ find_in_hash(result, *keys)
132
290
  end
133
291
  end
134
292
  end