geocoder 1.5.2 → 1.7.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +48 -0
- data/LICENSE +1 -1
- data/README.md +323 -237
- data/bin/console +13 -0
- data/examples/autoexpire_cache_redis.rb +2 -0
- data/lib/easting_northing.rb +171 -0
- data/lib/geocoder/cache.rb +9 -1
- data/lib/geocoder/configuration.rb +3 -1
- data/lib/geocoder/configuration_hash.rb +4 -4
- data/lib/geocoder/ip_address.rb +8 -1
- data/lib/geocoder/lookup.rb +20 -3
- data/lib/geocoder/lookups/abstract_api.rb +46 -0
- data/lib/geocoder/lookups/amazon_location_service.rb +53 -0
- data/lib/geocoder/lookups/ban_data_gouv_fr.rb +14 -1
- data/lib/geocoder/lookups/bing.rb +1 -1
- data/lib/geocoder/lookups/esri.rb +6 -0
- data/lib/geocoder/lookups/geoapify.rb +72 -0
- data/lib/geocoder/lookups/geocodio.rb +1 -1
- data/lib/geocoder/lookups/geoip2.rb +4 -0
- data/lib/geocoder/lookups/google.rb +7 -2
- data/lib/geocoder/lookups/google_places_details.rb +8 -14
- data/lib/geocoder/lookups/google_places_search.rb +28 -2
- data/lib/geocoder/lookups/google_premier.rb +4 -0
- data/lib/geocoder/lookups/here.rb +7 -16
- data/lib/geocoder/lookups/ip2location.rb +10 -14
- data/lib/geocoder/lookups/ipgeolocation.rb +51 -0
- data/lib/geocoder/lookups/ipqualityscore.rb +50 -0
- data/lib/geocoder/lookups/latlon.rb +1 -2
- data/lib/geocoder/lookups/maxmind_local.rb +7 -1
- data/lib/geocoder/lookups/melissa_street.rb +41 -0
- data/lib/geocoder/lookups/nationaal_georegister_nl.rb +38 -0
- data/lib/geocoder/lookups/osmnames.rb +57 -0
- data/lib/geocoder/lookups/photon.rb +89 -0
- data/lib/geocoder/lookups/pickpoint.rb +1 -1
- data/lib/geocoder/lookups/smarty_streets.rb +6 -1
- data/lib/geocoder/lookups/telize.rb +1 -1
- data/lib/geocoder/lookups/tencent.rb +9 -9
- data/lib/geocoder/lookups/test.rb +4 -0
- data/lib/geocoder/lookups/uk_ordnance_survey_names.rb +59 -0
- data/lib/geocoder/lookups/yandex.rb +1 -2
- data/lib/geocoder/results/abstract_api.rb +146 -0
- data/lib/geocoder/results/amazon_location_service.rb +57 -0
- data/lib/geocoder/results/baidu.rb +0 -4
- data/lib/geocoder/results/ban_data_gouv_fr.rb +27 -2
- data/lib/geocoder/results/db_ip_com.rb +1 -1
- data/lib/geocoder/results/esri.rb +5 -2
- data/lib/geocoder/results/geoapify.rb +179 -0
- data/lib/geocoder/results/ipgeolocation.rb +59 -0
- data/lib/geocoder/results/ipqualityscore.rb +54 -0
- data/lib/geocoder/results/ipregistry.rb +4 -8
- data/lib/geocoder/results/mapbox.rb +10 -4
- data/lib/geocoder/results/melissa_street.rb +46 -0
- data/lib/geocoder/results/nationaal_georegister_nl.rb +62 -0
- data/lib/geocoder/results/nominatim.rb +4 -0
- data/lib/geocoder/results/osmnames.rb +56 -0
- data/lib/geocoder/results/photon.rb +119 -0
- data/lib/geocoder/results/uk_ordnance_survey_names.rb +59 -0
- data/lib/geocoder/results/yandex.rb +217 -59
- data/lib/geocoder/sql.rb +4 -4
- data/lib/geocoder/util.rb +29 -0
- data/lib/geocoder/version.rb +1 -1
- data/lib/maxmind_database.rb +3 -3
- metadata +29 -11
- data/lib/geocoder/lookups/geocoder_us.rb +0 -51
- data/lib/geocoder/results/geocoder_us.rb +0 -39
- data/lib/hash_recursive_merge.rb +0 -74
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'geocoder/results/base'
|
2
|
+
|
3
|
+
module Geocoder::Result
|
4
|
+
class NationaalGeoregisterNl < Base
|
5
|
+
|
6
|
+
def response_attributes
|
7
|
+
@data
|
8
|
+
end
|
9
|
+
|
10
|
+
def coordinates
|
11
|
+
@data['centroide_ll'][6..-2].split(' ').map(&:to_f).reverse
|
12
|
+
end
|
13
|
+
|
14
|
+
def formatted_address
|
15
|
+
@data['weergavenaam']
|
16
|
+
end
|
17
|
+
|
18
|
+
alias_method :address, :formatted_address
|
19
|
+
|
20
|
+
def province
|
21
|
+
@data['provincienaam']
|
22
|
+
end
|
23
|
+
|
24
|
+
alias_method :state, :province
|
25
|
+
|
26
|
+
def city
|
27
|
+
@data['woonplaatsnaam']
|
28
|
+
end
|
29
|
+
|
30
|
+
def district
|
31
|
+
@data['gemeentenaam']
|
32
|
+
end
|
33
|
+
|
34
|
+
def street
|
35
|
+
@data['straatnaam']
|
36
|
+
end
|
37
|
+
|
38
|
+
def street_number
|
39
|
+
@data['huis_nlt']
|
40
|
+
end
|
41
|
+
|
42
|
+
def address_components
|
43
|
+
@data
|
44
|
+
end
|
45
|
+
|
46
|
+
def state_code
|
47
|
+
@data['provinciecode']
|
48
|
+
end
|
49
|
+
|
50
|
+
def postal_code
|
51
|
+
@data['postcode']
|
52
|
+
end
|
53
|
+
|
54
|
+
def country
|
55
|
+
"Netherlands"
|
56
|
+
end
|
57
|
+
|
58
|
+
def country_code
|
59
|
+
"NL"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'geocoder/results/base'
|
2
|
+
|
3
|
+
module Geocoder::Result
|
4
|
+
class Osmnames < Base
|
5
|
+
def address
|
6
|
+
@data['display_name']
|
7
|
+
end
|
8
|
+
|
9
|
+
def coordinates
|
10
|
+
[@data['lat'].to_f, @data['lon'].to_f]
|
11
|
+
end
|
12
|
+
|
13
|
+
def viewport
|
14
|
+
west, south, east, north = @data['boundingbox'].map(&:to_f)
|
15
|
+
[south, west, north, east]
|
16
|
+
end
|
17
|
+
|
18
|
+
def state
|
19
|
+
@data['state']
|
20
|
+
end
|
21
|
+
alias_method :state_code, :state
|
22
|
+
|
23
|
+
def place_class
|
24
|
+
@data['class']
|
25
|
+
end
|
26
|
+
|
27
|
+
def place_type
|
28
|
+
@data['type']
|
29
|
+
end
|
30
|
+
|
31
|
+
def postal_code
|
32
|
+
''
|
33
|
+
end
|
34
|
+
|
35
|
+
def country_code
|
36
|
+
@data['country_code']
|
37
|
+
end
|
38
|
+
|
39
|
+
def country
|
40
|
+
@data['country']
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.response_attributes
|
44
|
+
%w[house_number street city name osm_id osm_type boundingbox place_rank
|
45
|
+
importance county rank name_suffix]
|
46
|
+
end
|
47
|
+
|
48
|
+
response_attributes.each do |a|
|
49
|
+
unless method_defined?(a)
|
50
|
+
define_method a do
|
51
|
+
@data[a]
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
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,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(
|
169
|
+
def address(_format = :full)
|
11
170
|
@data['GeoObject']['metaDataProperty']['GeocoderMetaData']['text']
|
12
171
|
end
|
13
172
|
|
14
173
|
def city
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
53
|
-
|
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
|
64
|
-
""
|
207
|
+
def street
|
208
|
+
thoroughfare_data.is_a?(Hash) ? thoroughfare_data['ThoroughfareName'] : ""
|
65
209
|
end
|
66
210
|
|
67
|
-
def
|
68
|
-
|
211
|
+
def street_number
|
212
|
+
premise.is_a?(Hash) ? premise.fetch('PremiseNumber', "") : ""
|
69
213
|
end
|
70
214
|
|
71
|
-
def
|
72
|
-
|
215
|
+
def premise_name
|
216
|
+
premise.is_a?(Hash) ? premise.fetch('PremiseName', "") : ""
|
73
217
|
end
|
74
218
|
|
75
|
-
def
|
76
|
-
|
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
|
97
|
-
|
241
|
+
def top_level_locality
|
242
|
+
find_in_hash(@data, *ADDRESS_DETAILS, 'Locality')
|
98
243
|
end
|
99
244
|
|
100
|
-
def
|
101
|
-
|
245
|
+
def country_level_locality
|
246
|
+
find_in_hash(@data, *COUNTRY_LEVEL, 'Locality')
|
102
247
|
end
|
103
248
|
|
104
249
|
def admin_locality
|
105
|
-
|
106
|
-
address_details['AdministrativeArea']['Locality']
|
250
|
+
find_in_hash(@data, *ADMIN_LEVEL, 'Locality')
|
107
251
|
end
|
108
252
|
|
109
253
|
def subadmin_locality
|
110
|
-
|
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
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
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
|
123
|
-
|
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
|
127
|
-
|
128
|
-
|
129
|
-
|
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
|
data/lib/geocoder/sql.rb
CHANGED
@@ -44,13 +44,13 @@ module Geocoder
|
|
44
44
|
end
|
45
45
|
|
46
46
|
def within_bounding_box(sw_lat, sw_lng, ne_lat, ne_lng, lat_attr, lon_attr)
|
47
|
-
spans = "#{lat_attr} BETWEEN #{sw_lat} AND #{ne_lat} AND "
|
47
|
+
spans = "#{lat_attr} BETWEEN #{sw_lat.to_f} AND #{ne_lat.to_f} AND "
|
48
48
|
# handle box that spans 180 longitude
|
49
49
|
if sw_lng.to_f > ne_lng.to_f
|
50
|
-
spans + "(#{lon_attr} BETWEEN #{sw_lng} AND 180 OR " +
|
51
|
-
"#{lon_attr} BETWEEN -180 AND #{ne_lng})"
|
50
|
+
spans + "(#{lon_attr} BETWEEN #{sw_lng.to_f} AND 180 OR " +
|
51
|
+
"#{lon_attr} BETWEEN -180 AND #{ne_lng.to_f})"
|
52
52
|
else
|
53
|
-
spans + "#{lon_attr} BETWEEN #{sw_lng} AND #{ne_lng}"
|
53
|
+
spans + "#{lon_attr} BETWEEN #{sw_lng.to_f} AND #{ne_lng.to_f}"
|
54
54
|
end
|
55
55
|
end
|
56
56
|
|
@@ -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
|