really-broken-geocoder 1.5.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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +557 -0
- data/LICENSE +20 -0
- data/README.md +3 -0
- data/bin/geocode +5 -0
- data/examples/autoexpire_cache_dalli.rb +62 -0
- data/examples/autoexpire_cache_redis.rb +28 -0
- data/examples/cache_bypass.rb +48 -0
- data/examples/reverse_geocode_job.rb +40 -0
- data/lib/generators/geocoder/config/config_generator.rb +14 -0
- data/lib/generators/geocoder/config/templates/initializer.rb +22 -0
- data/lib/generators/geocoder/maxmind/geolite_city_generator.rb +30 -0
- data/lib/generators/geocoder/maxmind/geolite_country_generator.rb +30 -0
- data/lib/generators/geocoder/maxmind/templates/migration/geolite_city.rb +30 -0
- data/lib/generators/geocoder/maxmind/templates/migration/geolite_country.rb +17 -0
- data/lib/generators/geocoder/migration_version.rb +15 -0
- data/lib/geocoder.rb +48 -0
- data/lib/geocoder/cache.rb +94 -0
- data/lib/geocoder/calculations.rb +420 -0
- data/lib/geocoder/cli.rb +121 -0
- data/lib/geocoder/configuration.rb +137 -0
- data/lib/geocoder/configuration_hash.rb +11 -0
- data/lib/geocoder/esri_token.rb +38 -0
- data/lib/geocoder/exceptions.rb +40 -0
- data/lib/geocoder/ip_address.rb +26 -0
- data/lib/geocoder/kernel_logger.rb +25 -0
- data/lib/geocoder/logger.rb +47 -0
- data/lib/geocoder/lookup.rb +118 -0
- data/lib/geocoder/lookups/amap.rb +63 -0
- data/lib/geocoder/lookups/baidu.rb +63 -0
- data/lib/geocoder/lookups/baidu_ip.rb +30 -0
- data/lib/geocoder/lookups/ban_data_gouv_fr.rb +130 -0
- data/lib/geocoder/lookups/base.rb +348 -0
- data/lib/geocoder/lookups/bing.rb +82 -0
- data/lib/geocoder/lookups/db_ip_com.rb +52 -0
- data/lib/geocoder/lookups/dstk.rb +22 -0
- data/lib/geocoder/lookups/esri.rb +95 -0
- data/lib/geocoder/lookups/freegeoip.rb +60 -0
- data/lib/geocoder/lookups/geocoder_ca.rb +53 -0
- data/lib/geocoder/lookups/geocoder_us.rb +51 -0
- data/lib/geocoder/lookups/geocodio.rb +42 -0
- data/lib/geocoder/lookups/geoip2.rb +45 -0
- data/lib/geocoder/lookups/geoportail_lu.rb +65 -0
- data/lib/geocoder/lookups/google.rb +95 -0
- data/lib/geocoder/lookups/google_places_details.rb +50 -0
- data/lib/geocoder/lookups/google_places_search.rb +33 -0
- data/lib/geocoder/lookups/google_premier.rb +57 -0
- data/lib/geocoder/lookups/here.rb +77 -0
- data/lib/geocoder/lookups/ip2location.rb +75 -0
- data/lib/geocoder/lookups/ipapi_com.rb +82 -0
- data/lib/geocoder/lookups/ipdata_co.rb +62 -0
- data/lib/geocoder/lookups/ipinfo_io.rb +44 -0
- data/lib/geocoder/lookups/ipstack.rb +63 -0
- data/lib/geocoder/lookups/latlon.rb +59 -0
- data/lib/geocoder/lookups/location_iq.rb +50 -0
- data/lib/geocoder/lookups/mapbox.rb +59 -0
- data/lib/geocoder/lookups/mapquest.rb +58 -0
- data/lib/geocoder/lookups/maxmind.rb +90 -0
- data/lib/geocoder/lookups/maxmind_geoip2.rb +70 -0
- data/lib/geocoder/lookups/maxmind_local.rb +65 -0
- data/lib/geocoder/lookups/nominatim.rb +64 -0
- data/lib/geocoder/lookups/opencagedata.rb +65 -0
- data/lib/geocoder/lookups/pelias.rb +63 -0
- data/lib/geocoder/lookups/pickpoint.rb +41 -0
- data/lib/geocoder/lookups/pointpin.rb +69 -0
- data/lib/geocoder/lookups/postcode_anywhere_uk.rb +50 -0
- data/lib/geocoder/lookups/postcodes_io.rb +31 -0
- data/lib/geocoder/lookups/smarty_streets.rb +63 -0
- data/lib/geocoder/lookups/telize.rb +75 -0
- data/lib/geocoder/lookups/tencent.rb +59 -0
- data/lib/geocoder/lookups/test.rb +44 -0
- data/lib/geocoder/lookups/yandex.rb +62 -0
- data/lib/geocoder/models/active_record.rb +51 -0
- data/lib/geocoder/models/base.rb +39 -0
- data/lib/geocoder/models/mongo_base.rb +62 -0
- data/lib/geocoder/models/mongo_mapper.rb +26 -0
- data/lib/geocoder/models/mongoid.rb +32 -0
- data/lib/geocoder/query.rb +125 -0
- data/lib/geocoder/railtie.rb +26 -0
- data/lib/geocoder/request.rb +114 -0
- data/lib/geocoder/results/amap.rb +87 -0
- data/lib/geocoder/results/baidu.rb +79 -0
- data/lib/geocoder/results/baidu_ip.rb +62 -0
- data/lib/geocoder/results/ban_data_gouv_fr.rb +257 -0
- data/lib/geocoder/results/base.rb +79 -0
- data/lib/geocoder/results/bing.rb +52 -0
- data/lib/geocoder/results/db_ip_com.rb +58 -0
- data/lib/geocoder/results/dstk.rb +6 -0
- data/lib/geocoder/results/esri.rb +75 -0
- data/lib/geocoder/results/freegeoip.rb +40 -0
- data/lib/geocoder/results/geocoder_ca.rb +60 -0
- data/lib/geocoder/results/geocoder_us.rb +39 -0
- data/lib/geocoder/results/geocodio.rb +78 -0
- data/lib/geocoder/results/geoip2.rb +76 -0
- data/lib/geocoder/results/geoportail_lu.rb +71 -0
- data/lib/geocoder/results/google.rb +150 -0
- data/lib/geocoder/results/google_places_details.rb +39 -0
- data/lib/geocoder/results/google_places_search.rb +52 -0
- data/lib/geocoder/results/google_premier.rb +6 -0
- data/lib/geocoder/results/here.rb +79 -0
- data/lib/geocoder/results/ip2location.rb +22 -0
- data/lib/geocoder/results/ipapi_com.rb +45 -0
- data/lib/geocoder/results/ipdata_co.rb +40 -0
- data/lib/geocoder/results/ipinfo_io.rb +48 -0
- data/lib/geocoder/results/ipstack.rb +60 -0
- data/lib/geocoder/results/latlon.rb +71 -0
- data/lib/geocoder/results/location_iq.rb +6 -0
- data/lib/geocoder/results/mapbox.rb +57 -0
- data/lib/geocoder/results/mapquest.rb +48 -0
- data/lib/geocoder/results/maxmind.rb +130 -0
- data/lib/geocoder/results/maxmind_geoip2.rb +9 -0
- data/lib/geocoder/results/maxmind_local.rb +44 -0
- data/lib/geocoder/results/nominatim.rb +109 -0
- data/lib/geocoder/results/opencagedata.rb +100 -0
- data/lib/geocoder/results/pelias.rb +58 -0
- data/lib/geocoder/results/pickpoint.rb +6 -0
- data/lib/geocoder/results/pointpin.rb +40 -0
- data/lib/geocoder/results/postcode_anywhere_uk.rb +42 -0
- data/lib/geocoder/results/postcodes_io.rb +40 -0
- data/lib/geocoder/results/smarty_streets.rb +142 -0
- data/lib/geocoder/results/telize.rb +40 -0
- data/lib/geocoder/results/tencent.rb +72 -0
- data/lib/geocoder/results/test.rb +33 -0
- data/lib/geocoder/results/yandex.rb +134 -0
- data/lib/geocoder/sql.rb +110 -0
- data/lib/geocoder/stores/active_record.rb +328 -0
- data/lib/geocoder/stores/base.rb +115 -0
- data/lib/geocoder/stores/mongo_base.rb +58 -0
- data/lib/geocoder/stores/mongo_mapper.rb +13 -0
- data/lib/geocoder/stores/mongoid.rb +13 -0
- data/lib/geocoder/version.rb +3 -0
- data/lib/hash_recursive_merge.rb +74 -0
- data/lib/maxmind_database.rb +109 -0
- data/lib/tasks/geocoder.rake +54 -0
- data/lib/tasks/maxmind.rake +73 -0
- metadata +186 -0
@@ -0,0 +1,142 @@
|
|
1
|
+
require 'geocoder/lookups/base'
|
2
|
+
|
3
|
+
module Geocoder::Result
|
4
|
+
class SmartyStreets < Base
|
5
|
+
def coordinates
|
6
|
+
result = %w(latitude longitude).map do |i|
|
7
|
+
zipcode_endpoint? ? zipcodes.first[i] : metadata[i]
|
8
|
+
end
|
9
|
+
|
10
|
+
if result.compact.empty?
|
11
|
+
nil
|
12
|
+
else
|
13
|
+
result
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def address
|
18
|
+
parts =
|
19
|
+
if international_endpoint?
|
20
|
+
(1..12).map { |i| @data["address#{i}"] }
|
21
|
+
else
|
22
|
+
[
|
23
|
+
delivery_line_1,
|
24
|
+
delivery_line_2,
|
25
|
+
last_line
|
26
|
+
]
|
27
|
+
end
|
28
|
+
parts.select{ |i| i.to_s != "" }.join(" ")
|
29
|
+
end
|
30
|
+
|
31
|
+
def state
|
32
|
+
if international_endpoint?
|
33
|
+
components['administrative_area']
|
34
|
+
elsif zipcode_endpoint?
|
35
|
+
city_states.first['state']
|
36
|
+
else
|
37
|
+
components['state_abbreviation']
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def state_code
|
42
|
+
if international_endpoint?
|
43
|
+
components['administrative_area']
|
44
|
+
elsif zipcode_endpoint?
|
45
|
+
city_states.first['state_abbreviation']
|
46
|
+
else
|
47
|
+
components['state_abbreviation']
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def country
|
52
|
+
international_endpoint? ?
|
53
|
+
components['country_iso_3'] :
|
54
|
+
"United States"
|
55
|
+
end
|
56
|
+
|
57
|
+
def country_code
|
58
|
+
international_endpoint? ?
|
59
|
+
components['country_iso_3'] :
|
60
|
+
"US"
|
61
|
+
end
|
62
|
+
|
63
|
+
## Extra methods not in base.rb ------------------------
|
64
|
+
|
65
|
+
def street
|
66
|
+
international_endpoint? ?
|
67
|
+
components['thoroughfare_name'] :
|
68
|
+
components['street_name']
|
69
|
+
end
|
70
|
+
|
71
|
+
def city
|
72
|
+
if international_endpoint?
|
73
|
+
components['locality']
|
74
|
+
elsif zipcode_endpoint?
|
75
|
+
city_states.first['city']
|
76
|
+
else
|
77
|
+
components['city_name']
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def zipcode
|
82
|
+
if international_endpoint?
|
83
|
+
components['postal_code']
|
84
|
+
elsif zipcode_endpoint?
|
85
|
+
zipcodes.first['zipcode']
|
86
|
+
else
|
87
|
+
components['zipcode']
|
88
|
+
end
|
89
|
+
end
|
90
|
+
alias_method :postal_code, :zipcode
|
91
|
+
|
92
|
+
def zip4
|
93
|
+
components['plus4_code']
|
94
|
+
end
|
95
|
+
alias_method :postal_code_extended, :zip4
|
96
|
+
|
97
|
+
def fips
|
98
|
+
zipcode_endpoint? ?
|
99
|
+
zipcodes.first['county_fips'] :
|
100
|
+
metadata['county_fips']
|
101
|
+
end
|
102
|
+
|
103
|
+
def zipcode_endpoint?
|
104
|
+
zipcodes.any?
|
105
|
+
end
|
106
|
+
|
107
|
+
def international_endpoint?
|
108
|
+
!@data['address1'].nil?
|
109
|
+
end
|
110
|
+
|
111
|
+
[
|
112
|
+
:delivery_line_1,
|
113
|
+
:delivery_line_2,
|
114
|
+
:last_line,
|
115
|
+
:delivery_point_barcode,
|
116
|
+
:addressee
|
117
|
+
].each do |m|
|
118
|
+
define_method(m) do
|
119
|
+
@data[m.to_s] || ''
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
[
|
124
|
+
:components,
|
125
|
+
:metadata,
|
126
|
+
:analysis
|
127
|
+
].each do |m|
|
128
|
+
define_method(m) do
|
129
|
+
@data[m.to_s] || {}
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
[
|
134
|
+
:city_states,
|
135
|
+
:zipcodes
|
136
|
+
].each do |m|
|
137
|
+
define_method(m) do
|
138
|
+
@data[m.to_s] || []
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'geocoder/results/base'
|
2
|
+
|
3
|
+
module Geocoder::Result
|
4
|
+
class Telize < Base
|
5
|
+
|
6
|
+
def city
|
7
|
+
@data['city']
|
8
|
+
end
|
9
|
+
|
10
|
+
def state
|
11
|
+
@data['region']
|
12
|
+
end
|
13
|
+
|
14
|
+
def state_code
|
15
|
+
@data['region_code']
|
16
|
+
end
|
17
|
+
|
18
|
+
def country
|
19
|
+
@data['country']
|
20
|
+
end
|
21
|
+
|
22
|
+
def country_code
|
23
|
+
@data['country_code']
|
24
|
+
end
|
25
|
+
|
26
|
+
def postal_code
|
27
|
+
@data['postal_code']
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.response_attributes
|
31
|
+
%w[timezone isp dma_code area_code ip asn continent_code country_code3]
|
32
|
+
end
|
33
|
+
|
34
|
+
response_attributes.each do |a|
|
35
|
+
define_method a do
|
36
|
+
@data[a]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -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,33 @@
|
|
1
|
+
require 'geocoder/results/base'
|
2
|
+
|
3
|
+
module Geocoder
|
4
|
+
module Result
|
5
|
+
class Test < Base
|
6
|
+
|
7
|
+
def self.add_result_attribute(attr)
|
8
|
+
begin
|
9
|
+
remove_method(attr) if method_defined?(attr)
|
10
|
+
rescue NameError # method defined on superclass
|
11
|
+
end
|
12
|
+
|
13
|
+
define_method(attr) do
|
14
|
+
@data[attr.to_s] || @data[attr.to_sym]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
%w[coordinates neighborhood city state state_code sub_state
|
19
|
+
sub_state_code province province_code postal_code country
|
20
|
+
country_code address street_address street_number route geometry].each do |attr|
|
21
|
+
add_result_attribute(attr)
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize(data)
|
25
|
+
data.each_key do |attr|
|
26
|
+
Test.add_result_attribute(attr)
|
27
|
+
end
|
28
|
+
|
29
|
+
super
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
require 'geocoder/results/base'
|
2
|
+
|
3
|
+
module Geocoder::Result
|
4
|
+
class Yandex < Base
|
5
|
+
|
6
|
+
def coordinates
|
7
|
+
@data['GeoObject']['Point']['pos'].split(' ').reverse.map(&:to_f)
|
8
|
+
end
|
9
|
+
|
10
|
+
def address(format = :full)
|
11
|
+
@data['GeoObject']['metaDataProperty']['GeocoderMetaData']['text']
|
12
|
+
end
|
13
|
+
|
14
|
+
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
|
25
|
+
end
|
26
|
+
|
27
|
+
def country
|
28
|
+
if address_details
|
29
|
+
address_details['CountryName']
|
30
|
+
else
|
31
|
+
""
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def country_code
|
36
|
+
if address_details
|
37
|
+
address_details['CountryNameCode']
|
38
|
+
else
|
39
|
+
""
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def state
|
44
|
+
if address_details and address_details['AdministrativeArea']
|
45
|
+
address_details['AdministrativeArea']['AdministrativeAreaName']
|
46
|
+
else
|
47
|
+
""
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
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
|
57
|
+
end
|
58
|
+
|
59
|
+
def state_code
|
60
|
+
""
|
61
|
+
end
|
62
|
+
|
63
|
+
def postal_code
|
64
|
+
""
|
65
|
+
end
|
66
|
+
|
67
|
+
def premise_name
|
68
|
+
address_details['Locality']['Premise']['PremiseName']
|
69
|
+
end
|
70
|
+
|
71
|
+
def street
|
72
|
+
thoroughfare_data && thoroughfare_data['ThoroughfareName']
|
73
|
+
end
|
74
|
+
|
75
|
+
def street_number
|
76
|
+
thoroughfare_data && thoroughfare_data['Premise'] && thoroughfare_data['Premise']['PremiseNumber']
|
77
|
+
end
|
78
|
+
|
79
|
+
def kind
|
80
|
+
@data['GeoObject']['metaDataProperty']['GeocoderMetaData']['kind']
|
81
|
+
end
|
82
|
+
|
83
|
+
def precision
|
84
|
+
@data['GeoObject']['metaDataProperty']['GeocoderMetaData']['precision']
|
85
|
+
end
|
86
|
+
|
87
|
+
def viewport
|
88
|
+
envelope = @data['GeoObject']['boundedBy']['Envelope'] || fail
|
89
|
+
east, north = envelope['upperCorner'].split(' ').map(&:to_f)
|
90
|
+
west, south = envelope['lowerCorner'].split(' ').map(&:to_f)
|
91
|
+
[south, west, north, east]
|
92
|
+
end
|
93
|
+
|
94
|
+
private # ----------------------------------------------------------------
|
95
|
+
|
96
|
+
def thoroughfare_data
|
97
|
+
locality_data && locality_data['Thoroughfare']
|
98
|
+
end
|
99
|
+
|
100
|
+
def locality_data
|
101
|
+
dependent_locality && subadmin_locality && admin_locality
|
102
|
+
end
|
103
|
+
|
104
|
+
def admin_locality
|
105
|
+
address_details && address_details['AdministrativeArea'] &&
|
106
|
+
address_details['AdministrativeArea']['Locality']
|
107
|
+
end
|
108
|
+
|
109
|
+
def subadmin_locality
|
110
|
+
address_details && address_details['AdministrativeArea'] &&
|
111
|
+
address_details['AdministrativeArea']['SubAdministrativeArea'] &&
|
112
|
+
address_details['AdministrativeArea']['SubAdministrativeArea']['Locality']
|
113
|
+
end
|
114
|
+
|
115
|
+
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']
|
120
|
+
end
|
121
|
+
|
122
|
+
def address_details
|
123
|
+
@data['GeoObject']['metaDataProperty']['GeocoderMetaData']['AddressDetails']['Country']
|
124
|
+
end
|
125
|
+
|
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
|
+
""
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
data/lib/geocoder/sql.rb
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
module Geocoder
|
2
|
+
module Sql
|
3
|
+
extend self
|
4
|
+
|
5
|
+
##
|
6
|
+
# Distance calculation for use with a database that supports POWER(),
|
7
|
+
# SQRT(), PI(), and trigonometric functions SIN(), COS(), ASIN(),
|
8
|
+
# ATAN2().
|
9
|
+
#
|
10
|
+
# Based on the excellent tutorial at:
|
11
|
+
# http://www.scribd.com/doc/2569355/Geo-Distance-Search-with-MySQL
|
12
|
+
#
|
13
|
+
def full_distance(latitude, longitude, lat_attr, lon_attr, options = {})
|
14
|
+
units = options[:units] || Geocoder.config.units
|
15
|
+
earth = Geocoder::Calculations.earth_radius(units)
|
16
|
+
|
17
|
+
"#{earth} * 2 * ASIN(SQRT(" +
|
18
|
+
"POWER(SIN((#{latitude.to_f} - #{lat_attr}) * PI() / 180 / 2), 2) + " +
|
19
|
+
"COS(#{latitude.to_f} * PI() / 180) * COS(#{lat_attr} * PI() / 180) * " +
|
20
|
+
"POWER(SIN((#{longitude.to_f} - #{lon_attr}) * PI() / 180 / 2), 2)" +
|
21
|
+
"))"
|
22
|
+
end
|
23
|
+
|
24
|
+
##
|
25
|
+
# Distance calculation for use with a database without trigonometric
|
26
|
+
# functions, like SQLite. Approach is to find objects within a square
|
27
|
+
# rather than a circle, so results are very approximate (will include
|
28
|
+
# objects outside the given radius).
|
29
|
+
#
|
30
|
+
# Distance and bearing calculations are *extremely inaccurate*. To be
|
31
|
+
# clear: this only exists to provide interface consistency. Results
|
32
|
+
# are not intended for use in production!
|
33
|
+
#
|
34
|
+
def approx_distance(latitude, longitude, lat_attr, lon_attr, options = {})
|
35
|
+
units = options[:units] || Geocoder.config.units
|
36
|
+
dx = Geocoder::Calculations.longitude_degree_distance(30, units)
|
37
|
+
dy = Geocoder::Calculations.latitude_degree_distance(units)
|
38
|
+
|
39
|
+
# sin of 45 degrees = average x or y component of vector
|
40
|
+
factor = Math.sin(Math::PI / 4)
|
41
|
+
|
42
|
+
"(#{dy} * ABS(#{lat_attr} - #{latitude.to_f}) * #{factor}) + " +
|
43
|
+
"(#{dx} * ABS(#{lon_attr} - #{longitude.to_f}) * #{factor})"
|
44
|
+
end
|
45
|
+
|
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 "
|
48
|
+
# handle box that spans 180 longitude
|
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})"
|
52
|
+
else
|
53
|
+
spans + "#{lon_attr} BETWEEN #{sw_lng} AND #{ne_lng}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
##
|
58
|
+
# Fairly accurate bearing calculation. Takes a latitude, longitude,
|
59
|
+
# and an options hash which must include a :bearing value
|
60
|
+
# (:linear or :spherical).
|
61
|
+
#
|
62
|
+
# For use with a database that supports MOD() and trigonometric functions
|
63
|
+
# SIN(), COS(), ASIN(), ATAN2().
|
64
|
+
#
|
65
|
+
# Based on:
|
66
|
+
# http://www.beginningspatial.com/calculating_bearing_one_point_another
|
67
|
+
#
|
68
|
+
def full_bearing(latitude, longitude, lat_attr, lon_attr, options = {})
|
69
|
+
degrees_per_radian = Geocoder::Calculations::DEGREES_PER_RADIAN
|
70
|
+
case options[:bearing] || Geocoder.config.distances
|
71
|
+
when :linear
|
72
|
+
"MOD(CAST(" +
|
73
|
+
"(ATAN2( " +
|
74
|
+
"((#{lon_attr} - #{longitude.to_f}) / #{degrees_per_radian}), " +
|
75
|
+
"((#{lat_attr} - #{latitude.to_f}) / #{degrees_per_radian})" +
|
76
|
+
") * #{degrees_per_radian}) + 360 " +
|
77
|
+
"AS decimal), 360)"
|
78
|
+
when :spherical
|
79
|
+
"MOD(CAST(" +
|
80
|
+
"(ATAN2( " +
|
81
|
+
"SIN( (#{lon_attr} - #{longitude.to_f}) / #{degrees_per_radian} ) * " +
|
82
|
+
"COS( (#{lat_attr}) / #{degrees_per_radian} ), (" +
|
83
|
+
"COS( (#{latitude.to_f}) / #{degrees_per_radian} ) * SIN( (#{lat_attr}) / #{degrees_per_radian})" +
|
84
|
+
") - (" +
|
85
|
+
"SIN( (#{latitude.to_f}) / #{degrees_per_radian}) * COS((#{lat_attr}) / #{degrees_per_radian}) * " +
|
86
|
+
"COS( (#{lon_attr} - #{longitude.to_f}) / #{degrees_per_radian})" +
|
87
|
+
")" +
|
88
|
+
") * #{degrees_per_radian}) + 360 " +
|
89
|
+
"AS decimal), 360)"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
##
|
94
|
+
# Totally lame bearing calculation. Basically useless except that it
|
95
|
+
# returns *something* in databases without trig functions.
|
96
|
+
#
|
97
|
+
def approx_bearing(latitude, longitude, lat_attr, lon_attr, options = {})
|
98
|
+
"CASE " +
|
99
|
+
"WHEN (#{lat_attr} >= #{latitude.to_f} AND " +
|
100
|
+
"#{lon_attr} >= #{longitude.to_f}) THEN 45.0 " +
|
101
|
+
"WHEN (#{lat_attr} < #{latitude.to_f} AND " +
|
102
|
+
"#{lon_attr} >= #{longitude.to_f}) THEN 135.0 " +
|
103
|
+
"WHEN (#{lat_attr} < #{latitude.to_f} AND " +
|
104
|
+
"#{lon_attr} < #{longitude.to_f}) THEN 225.0 " +
|
105
|
+
"WHEN (#{lat_attr} >= #{latitude.to_f} AND " +
|
106
|
+
"#{lon_attr} < #{longitude.to_f}) THEN 315.0 " +
|
107
|
+
"END"
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|