geocoder 1.1.3 → 1.1.4
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of geocoder might be problematic. Click here for more details.
- data/CHANGELOG.rdoc +12 -0
- data/README.md +580 -0
- data/examples/autoexpire_cache.rb +30 -0
- data/lib/geocoder.rb +9 -85
- data/lib/geocoder/calculations.rb +1 -1
- data/lib/geocoder/cli.rb +9 -10
- data/lib/geocoder/configuration.rb +3 -1
- data/lib/geocoder/lookup.rb +81 -0
- data/lib/geocoder/lookups/base.rb +20 -32
- data/lib/geocoder/lookups/bing.rb +12 -8
- data/lib/geocoder/lookups/freegeoip.rb +5 -5
- data/lib/geocoder/lookups/geocoder_ca.rb +13 -9
- data/lib/geocoder/lookups/google.rb +19 -7
- data/lib/geocoder/lookups/google_premier.rb +8 -7
- data/lib/geocoder/lookups/mapquest.rb +5 -23
- data/lib/geocoder/lookups/nominatim.rb +16 -13
- data/lib/geocoder/lookups/test.rb +8 -6
- data/lib/geocoder/lookups/yahoo.rb +49 -10
- data/lib/geocoder/lookups/yandex.rb +15 -8
- data/lib/geocoder/models/mongoid.rb +0 -1
- data/lib/geocoder/query.rb +88 -0
- data/lib/geocoder/results/mapquest.rb +2 -61
- data/lib/geocoder/results/nominatim.rb +24 -3
- data/lib/geocoder/sql.rb +104 -0
- data/lib/geocoder/stores/active_record.rb +68 -140
- data/lib/geocoder/stores/mongo_base.rb +2 -2
- data/lib/geocoder/version.rb +1 -1
- data/test/active_record_test.rb +15 -0
- data/test/calculations_test.rb +7 -0
- data/test/error_handling_test.rb +7 -7
- data/test/fixtures/yahoo_madison_square_garden.json +49 -43
- data/test/fixtures/yahoo_v1_madison_square_garden.json +46 -0
- data/test/fixtures/yahoo_v1_no_results.json +10 -0
- data/test/https_test.rb +2 -2
- data/test/integration/smoke_test.rb +6 -4
- data/test/lookup_test.rb +13 -6
- data/test/query_test.rb +34 -0
- data/test/result_test.rb +1 -1
- data/test/services_test.rb +48 -7
- data/test/test_helper.rb +64 -49
- data/test/test_mode_test.rb +0 -1
- metadata +13 -7
- data/README.rdoc +0 -552
- data/test/fixtures/yahoo_garbage.json +0 -50
- data/test/input_handling_test.rb +0 -43
@@ -1,66 +1,7 @@
|
|
1
1
|
require 'geocoder/results/base'
|
2
|
+
require 'geocoder/results/nominatim'
|
2
3
|
|
3
4
|
module Geocoder::Result
|
4
|
-
class Mapquest <
|
5
|
-
|
6
|
-
def house_number
|
7
|
-
@data['address']['house_number']
|
8
|
-
end
|
9
|
-
|
10
|
-
def address
|
11
|
-
@data['display_name']
|
12
|
-
end
|
13
|
-
|
14
|
-
def street
|
15
|
-
@data['address']['road']
|
16
|
-
end
|
17
|
-
|
18
|
-
def city
|
19
|
-
@data['address']['city']
|
20
|
-
end
|
21
|
-
|
22
|
-
def village
|
23
|
-
@data['address']['villiage']
|
24
|
-
end
|
25
|
-
|
26
|
-
def town
|
27
|
-
@data['address']['town']
|
28
|
-
end
|
29
|
-
|
30
|
-
def state
|
31
|
-
@data['address']['state']
|
32
|
-
end
|
33
|
-
|
34
|
-
alias_method :state_code, :state
|
35
|
-
|
36
|
-
def postal_code
|
37
|
-
@data['address']['postcode']
|
38
|
-
end
|
39
|
-
|
40
|
-
def county
|
41
|
-
@data['address']['county']
|
42
|
-
end
|
43
|
-
|
44
|
-
def country
|
45
|
-
@data['address']['country']
|
46
|
-
end
|
47
|
-
|
48
|
-
def country_code
|
49
|
-
@data['address']['country_code']
|
50
|
-
end
|
51
|
-
|
52
|
-
def coordinates
|
53
|
-
[@data['lat'].to_f, @data['lon'].to_f]
|
54
|
-
end
|
55
|
-
|
56
|
-
def self.response_attributes
|
57
|
-
%w[place_id boundingbox license polygonpoints display_name class type stadium suburb]
|
58
|
-
end
|
59
|
-
|
60
|
-
response_attributes.each do |a|
|
61
|
-
define_method a do
|
62
|
-
@data[a]
|
63
|
-
end
|
64
|
-
end
|
5
|
+
class Mapquest < Nominatim
|
65
6
|
end
|
66
7
|
end
|
@@ -5,8 +5,9 @@ module Geocoder::Result
|
|
5
5
|
|
6
6
|
def poi
|
7
7
|
%w[stadium bus_stop tram_stop].each do |key|
|
8
|
-
@data['address'][key] if @data['address'].key?(key)
|
8
|
+
return @data['address'][key] if @data['address'].key?(key)
|
9
9
|
end
|
10
|
+
return nil
|
10
11
|
end
|
11
12
|
|
12
13
|
def house_number
|
@@ -69,14 +70,34 @@ module Geocoder::Result
|
|
69
70
|
[@data['lat'].to_f, @data['lon'].to_f]
|
70
71
|
end
|
71
72
|
|
73
|
+
def place_class
|
74
|
+
@data['class']
|
75
|
+
end
|
76
|
+
|
77
|
+
def place_type
|
78
|
+
@data['type']
|
79
|
+
end
|
80
|
+
|
72
81
|
def self.response_attributes
|
73
82
|
%w[place_id osm_type osm_id boundingbox license
|
74
83
|
polygonpoints display_name class type stadium]
|
75
84
|
end
|
76
85
|
|
86
|
+
def class
|
87
|
+
warn "DEPRECATION WARNING: The 'class' method of Geocoder::Result::Nominatim objects is deprecated and will be removed in Geocoder version 1.2.0. Please use 'place_class' instead."
|
88
|
+
@data['class']
|
89
|
+
end
|
90
|
+
|
91
|
+
def type
|
92
|
+
warn "DEPRECATION WARNING: The 'type' method of Geocoder::Result::Nominatim objects is deprecated and will be removed in Geocoder version 1.2.0. Please use 'place_type' instead."
|
93
|
+
@data['type']
|
94
|
+
end
|
95
|
+
|
77
96
|
response_attributes.each do |a|
|
78
|
-
|
79
|
-
|
97
|
+
unless method_defined?(a)
|
98
|
+
define_method a do
|
99
|
+
@data[a]
|
100
|
+
end
|
80
101
|
end
|
81
102
|
end
|
82
103
|
end
|
data/lib/geocoder/sql.rb
ADDED
@@ -0,0 +1,104 @@
|
|
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(), DEGREES(), and RADIANS().
|
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
|
+
earth = Geocoder::Calculations.earth_radius(options[:units] || :mi)
|
15
|
+
|
16
|
+
"#{earth} * 2 * ASIN(SQRT(" +
|
17
|
+
"POWER(SIN((#{latitude.to_f} - #{lat_attr}) * PI() / 180 / 2), 2) + " +
|
18
|
+
"COS(#{latitude.to_f} * PI() / 180) * COS(#{lat_attr} * PI() / 180) * " +
|
19
|
+
"POWER(SIN((#{longitude.to_f} - #{lon_attr}) * PI() / 180 / 2), 2)" +
|
20
|
+
"))"
|
21
|
+
end
|
22
|
+
|
23
|
+
##
|
24
|
+
# Distance calculation for use with a database without trigonometric
|
25
|
+
# functions, like SQLite. Approach is to find objects within a square
|
26
|
+
# rather than a circle, so results are very approximate (will include
|
27
|
+
# objects outside the given radius).
|
28
|
+
#
|
29
|
+
# Distance and bearing calculations are *extremely inaccurate*. To be
|
30
|
+
# clear: this only exists to provide interface consistency. Results
|
31
|
+
# are not intended for use in production!
|
32
|
+
#
|
33
|
+
def approx_distance(latitude, longitude, lat_attr, lon_attr, options = {})
|
34
|
+
dx = Geocoder::Calculations.longitude_degree_distance(30, options[:units] || :mi)
|
35
|
+
dy = Geocoder::Calculations.latitude_degree_distance(options[:units] || :mi)
|
36
|
+
|
37
|
+
# sin of 45 degrees = average x or y component of vector
|
38
|
+
factor = Math.sin(Math::PI / 4)
|
39
|
+
|
40
|
+
"(#{dy} * ABS(#{lat_attr} - #{latitude.to_f}) * #{factor}) + " +
|
41
|
+
"(#{dx} * ABS(#{lon_attr} - #{longitude.to_f}) * #{factor})"
|
42
|
+
end
|
43
|
+
|
44
|
+
def within_bounding_box(sw_lat, sw_lng, ne_lat, ne_lng, lat_attr, lon_attr)
|
45
|
+
spans = "#{lat_attr} BETWEEN #{sw_lat} AND #{ne_lat} AND "
|
46
|
+
# handle box that spans 180 longitude
|
47
|
+
if sw_lng.to_f > ne_lng.to_f
|
48
|
+
spans + "#{lon_attr} BETWEEN #{sw_lng} AND 180 OR " +
|
49
|
+
"#{lon_attr} BETWEEN -180 AND #{ne_lng}"
|
50
|
+
else
|
51
|
+
spans + "#{lon_attr} BETWEEN #{sw_lng} AND #{ne_lng}"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
##
|
56
|
+
# Fairly accurate bearing calculation. Takes a latitude, longitude,
|
57
|
+
# and an options hash which must include a :bearing value
|
58
|
+
# (:linear or :spherical).
|
59
|
+
#
|
60
|
+
# Based on:
|
61
|
+
# http://www.beginningspatial.com/calculating_bearing_one_point_another
|
62
|
+
#
|
63
|
+
def full_bearing(latitude, longitude, lat_attr, lon_attr, options = {})
|
64
|
+
case options[:bearing]
|
65
|
+
when :linear
|
66
|
+
"CAST(" +
|
67
|
+
"DEGREES(ATAN2( " +
|
68
|
+
"RADIANS(#{lon_attr} - #{longitude.to_f}), " +
|
69
|
+
"RADIANS(#{lat_attr} - #{latitude.to_f})" +
|
70
|
+
")) + 360 " +
|
71
|
+
"AS decimal) % 360"
|
72
|
+
when :spherical
|
73
|
+
"CAST(" +
|
74
|
+
"DEGREES(ATAN2( " +
|
75
|
+
"SIN(RADIANS(#{lon_attr} - #{longitude.to_f})) * " +
|
76
|
+
"COS(RADIANS(#{lat_attr})), (" +
|
77
|
+
"COS(RADIANS(#{latitude.to_f})) * SIN(RADIANS(#{lat_attr}))" +
|
78
|
+
") - (" +
|
79
|
+
"SIN(RADIANS(#{latitude.to_f})) * COS(RADIANS(#{lat_attr})) * " +
|
80
|
+
"COS(RADIANS(#{lon_attr} - #{longitude.to_f}))" +
|
81
|
+
")" +
|
82
|
+
")) + 360 " +
|
83
|
+
"AS decimal) % 360"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
##
|
88
|
+
# Totally lame bearing calculation. Basically useless except that it
|
89
|
+
# returns *something* in databases without trig functions.
|
90
|
+
#
|
91
|
+
def approx_bearing(latitude, longitude, lat_attr, lon_attr, options = {})
|
92
|
+
"CASE " +
|
93
|
+
"WHEN (#{lat_attr} >= #{latitude.to_f} AND " +
|
94
|
+
"#{lon_attr} >= #{longitude.to_f}) THEN 45.0 " +
|
95
|
+
"WHEN (#{lat_attr} < #{latitude.to_f} AND " +
|
96
|
+
"#{lon_attr} >= #{longitude.to_f}) THEN 135.0 " +
|
97
|
+
"WHEN (#{lat_attr} < #{latitude.to_f} AND " +
|
98
|
+
"#{lon_attr} < #{longitude.to_f}) THEN 225.0 " +
|
99
|
+
"WHEN (#{lat_attr} >= #{latitude.to_f} AND " +
|
100
|
+
"#{lon_attr} < #{longitude.to_f}) THEN 315.0 " +
|
101
|
+
"END"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'geocoder/sql'
|
1
2
|
require 'geocoder/stores/base'
|
2
3
|
|
3
4
|
##
|
@@ -36,11 +37,13 @@ module Geocoder::Store
|
|
36
37
|
if Geocoder::Calculations.coordinates_present?(latitude, longitude)
|
37
38
|
near_scope_options(latitude, longitude, *args)
|
38
39
|
else
|
39
|
-
|
40
|
+
# If no lat/lon given we don't want any results, but we still
|
41
|
+
# need distance and bearing columns so you can add, for example:
|
42
|
+
# .order("distance")
|
43
|
+
select(select_clause(nil, "NULL", "NULL")).where(false_condition)
|
40
44
|
end
|
41
45
|
}
|
42
46
|
|
43
|
-
|
44
47
|
##
|
45
48
|
# Find all objects within the area of a given bounding box.
|
46
49
|
# Bounds must be an array of locations specifying the southwest
|
@@ -49,14 +52,15 @@ module Geocoder::Store
|
|
49
52
|
#
|
50
53
|
scope :within_bounding_box, lambda{ |bounds|
|
51
54
|
sw_lat, sw_lng, ne_lat, ne_lng = bounds.flatten if bounds
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
55
|
+
if sw_lat && sw_lng && ne_lat && ne_lng
|
56
|
+
{:conditions => Geocoder::Sql.within_bounding_box(
|
57
|
+
sw_lat, sw_lng, ne_lat, ne_lng,
|
58
|
+
full_column_name(geocoder_options[:latitude]),
|
59
|
+
full_column_name(geocoder_options[:longitude])
|
60
|
+
)}
|
56
61
|
else
|
57
|
-
"
|
62
|
+
select(select_clause(nil, "NULL", "NULL")).where(false_condition)
|
58
63
|
end
|
59
|
-
{ :conditions => spans }
|
60
64
|
}
|
61
65
|
end
|
62
66
|
end
|
@@ -69,7 +73,7 @@ module Geocoder::Store
|
|
69
73
|
def distance_from_sql(location, *args)
|
70
74
|
latitude, longitude = Geocoder::Calculations.extract_coordinates(location)
|
71
75
|
if Geocoder::Calculations.coordinates_present?(latitude, longitude)
|
72
|
-
|
76
|
+
distance_sql(latitude, longitude, *args)
|
73
77
|
end
|
74
78
|
end
|
75
79
|
|
@@ -90,163 +94,87 @@ module Geocoder::Store
|
|
90
94
|
# set to false for no bearing calculation.
|
91
95
|
# See Geocoder::Configuration to know how configure default method.
|
92
96
|
# * +:select+ - string with the SELECT SQL fragment (e.g. “id, name”)
|
93
|
-
# * +:order+ - column(s) for ORDER BY SQL clause; default is distance
|
97
|
+
# * +:order+ - column(s) for ORDER BY SQL clause; default is distance;
|
98
|
+
# set to false or nil to omit the ORDER BY clause
|
94
99
|
# * +:exclude+ - an object to exclude (used by the +nearbys+ method)
|
95
100
|
#
|
96
101
|
def near_scope_options(latitude, longitude, radius = 20, options = {})
|
102
|
+
options[:units] ||= (geocoder_options[:units] || Geocoder::Configuration.units)
|
103
|
+
bearing = bearing_sql(latitude, longitude, options)
|
104
|
+
distance = distance_sql(latitude, longitude, options)
|
97
105
|
if using_sqlite?
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
def distance_from_sql_options(latitude, longitude, options = {})
|
105
|
-
if using_sqlite?
|
106
|
-
approx_distance_from_sql(latitude, longitude, options)
|
106
|
+
b = Geocoder::Calculations.bounding_box([latitude, longitude], radius, options)
|
107
|
+
args = b + [
|
108
|
+
full_column_name(geocoder_options[:latitude]),
|
109
|
+
full_column_name(geocoder_options[:longitude])
|
110
|
+
]
|
111
|
+
conditions = Geocoder::Sql.within_bounding_box(*args)
|
107
112
|
else
|
108
|
-
|
113
|
+
conditions = ["#{distance} <= ?", radius]
|
109
114
|
end
|
115
|
+
{
|
116
|
+
:select => select_clause(options[:select], distance, bearing),
|
117
|
+
:conditions => add_exclude_condition(conditions, options[:exclude]),
|
118
|
+
:order => options.include?(:order) ? options[:order] : "distance ASC"
|
119
|
+
}
|
110
120
|
end
|
111
121
|
|
112
122
|
##
|
113
|
-
#
|
114
|
-
#
|
115
|
-
# ATAN2(), DEGREES(), and RADIANS().
|
123
|
+
# SQL for calculating distance based on the current database's
|
124
|
+
# capabilities (trig functions?).
|
116
125
|
#
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
Geocoder::Configuration.distances)
|
126
|
-
bearing = case options[:bearing]
|
127
|
-
when :linear
|
128
|
-
"CAST(" +
|
129
|
-
"DEGREES(ATAN2( " +
|
130
|
-
"RADIANS(#{full_column_name(lon_attr)} - #{longitude}), " +
|
131
|
-
"RADIANS(#{full_column_name(lat_attr)} - #{latitude})" +
|
132
|
-
")) + 360 " +
|
133
|
-
"AS decimal) % 360"
|
134
|
-
when :spherical
|
135
|
-
"CAST(" +
|
136
|
-
"DEGREES(ATAN2( " +
|
137
|
-
"SIN(RADIANS(#{full_column_name(lon_attr)} - #{longitude})) * " +
|
138
|
-
"COS(RADIANS(#{full_column_name(lat_attr)})), (" +
|
139
|
-
"COS(RADIANS(#{latitude})) * SIN(RADIANS(#{full_column_name(lat_attr)}))" +
|
140
|
-
") - (" +
|
141
|
-
"SIN(RADIANS(#{latitude})) * COS(RADIANS(#{full_column_name(lat_attr)})) * " +
|
142
|
-
"COS(RADIANS(#{full_column_name(lon_attr)} - #{longitude}))" +
|
143
|
-
")" +
|
144
|
-
")) + 360 " +
|
145
|
-
"AS decimal) % 360"
|
146
|
-
end
|
147
|
-
options[:units] ||= (geocoder_options[:units] || Geocoder::Configuration.units)
|
148
|
-
distance = full_distance_from_sql(latitude, longitude, options)
|
149
|
-
conditions = ["#{distance} <= ?", radius]
|
150
|
-
default_near_scope_options(latitude, longitude, radius, options).merge(
|
151
|
-
:select => "#{options[:select] || full_column_name("*")}, " +
|
152
|
-
"#{distance} AS distance" +
|
153
|
-
(bearing ? ", #{bearing} AS bearing" : ""),
|
154
|
-
:conditions => add_exclude_condition(conditions, options[:exclude])
|
126
|
+
def distance_sql(latitude, longitude, options = {})
|
127
|
+
method_prefix = using_sqlite? ? "approx" : "full"
|
128
|
+
Geocoder::Sql.send(
|
129
|
+
method_prefix + "_distance",
|
130
|
+
latitude, longitude,
|
131
|
+
full_column_name(geocoder_options[:latitude]),
|
132
|
+
full_column_name(geocoder_options[:longitude]),
|
133
|
+
options
|
155
134
|
)
|
156
135
|
end
|
157
136
|
|
158
|
-
|
159
|
-
# Distance calculations based on the excellent tutorial at:
|
160
|
-
# http://www.scribd.com/doc/2569355/Geo-Distance-Search-with-MySQL
|
161
|
-
|
162
|
-
def full_distance_from_sql(latitude, longitude, options)
|
163
|
-
lat_attr = geocoder_options[:latitude]
|
164
|
-
lon_attr = geocoder_options[:longitude]
|
165
|
-
|
166
|
-
earth = Geocoder::Calculations.earth_radius(options[:units] || :mi)
|
167
|
-
|
168
|
-
"#{earth} * 2 * ASIN(SQRT(" +
|
169
|
-
"POWER(SIN((#{latitude} - #{full_column_name(lat_attr)}) * PI() / 180 / 2), 2) + " +
|
170
|
-
"COS(#{latitude} * PI() / 180) * COS(#{full_column_name(lat_attr)} * PI() / 180) * " +
|
171
|
-
"POWER(SIN((#{longitude} - #{full_column_name(lon_attr)}) * PI() / 180 / 2), 2) ))"
|
172
|
-
end
|
173
|
-
|
174
|
-
def approx_distance_from_sql(latitude, longitude, options)
|
175
|
-
lat_attr = geocoder_options[:latitude]
|
176
|
-
lon_attr = geocoder_options[:longitude]
|
177
|
-
|
178
|
-
dx = Geocoder::Calculations.longitude_degree_distance(30, options[:units] || :mi)
|
179
|
-
dy = Geocoder::Calculations.latitude_degree_distance(options[:units] || :mi)
|
180
|
-
|
181
|
-
# sin of 45 degrees = average x or y component of vector
|
182
|
-
factor = Math.sin(Math::PI / 4)
|
183
|
-
|
184
|
-
"(#{dy} * ABS(#{full_column_name(lat_attr)} - #{latitude}) * #{factor}) + " +
|
185
|
-
"(#{dx} * ABS(#{full_column_name(lon_attr)} - #{longitude}) * #{factor})"
|
186
|
-
end
|
187
|
-
|
188
137
|
##
|
189
|
-
#
|
190
|
-
#
|
191
|
-
# rather than a circle, so results are very approximate (will include
|
192
|
-
# objects outside the given radius).
|
138
|
+
# SQL for calculating bearing based on the current database's
|
139
|
+
# capabilities (trig functions?).
|
193
140
|
#
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
def approx_near_scope_options(latitude, longitude, radius, options)
|
198
|
-
lat_attr = geocoder_options[:latitude]
|
199
|
-
lon_attr = geocoder_options[:longitude]
|
200
|
-
unless options.include?(:bearing)
|
201
|
-
options[:bearing] = (options[:method] || \
|
202
|
-
geocoder_options[:method] || \
|
203
|
-
Geocoder::Configuration.distances)
|
141
|
+
def bearing_sql(latitude, longitude, options = {})
|
142
|
+
if !options.include?(:bearing)
|
143
|
+
options[:bearing] = Geocoder::Configuration.distances
|
204
144
|
end
|
205
145
|
if options[:bearing]
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
146
|
+
method_prefix = using_sqlite? ? "approx" : "full"
|
147
|
+
Geocoder::Sql.send(
|
148
|
+
method_prefix + "_bearing",
|
149
|
+
latitude, longitude,
|
150
|
+
full_column_name(geocoder_options[:latitude]),
|
151
|
+
full_column_name(geocoder_options[:longitude]),
|
152
|
+
options
|
153
|
+
)
|
214
154
|
end
|
215
|
-
|
216
|
-
options[:units] ||= (geocoder_options[:units] || Geocoder::Configuration.units)
|
217
|
-
distance = approx_distance_from_sql(latitude, longitude, options)
|
218
|
-
|
219
|
-
b = Geocoder::Calculations.bounding_box([latitude, longitude], radius, options)
|
220
|
-
conditions = [
|
221
|
-
"#{full_column_name(lat_attr)} BETWEEN ? AND ? AND #{full_column_name(lon_attr)} BETWEEN ? AND ?"] +
|
222
|
-
[b[0], b[2], b[1], b[3]
|
223
|
-
]
|
224
|
-
default_near_scope_options(latitude, longitude, radius, options).merge(
|
225
|
-
:select => "#{options[:select] || full_column_name("*")}, " +
|
226
|
-
"#{distance} AS distance" +
|
227
|
-
(bearing ? ", #{bearing} AS bearing" : ""),
|
228
|
-
:conditions => add_exclude_condition(conditions, options[:exclude])
|
229
|
-
)
|
230
155
|
end
|
231
156
|
|
232
157
|
##
|
233
|
-
#
|
158
|
+
# Generate the SELECT clause.
|
234
159
|
#
|
235
|
-
def
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
160
|
+
def select_clause(columns, distance, bearing = nil)
|
161
|
+
if columns == :geo_only
|
162
|
+
clause = ""
|
163
|
+
else
|
164
|
+
clause = (columns || full_column_name("*")) + ", "
|
165
|
+
end
|
166
|
+
clause + "#{distance} AS distance" +
|
167
|
+
(bearing ? ", #{bearing} AS bearing" : "")
|
241
168
|
end
|
242
169
|
|
243
170
|
##
|
244
171
|
# Adds a condition to exclude a given object by ID.
|
245
|
-
#
|
172
|
+
# Expects conditions as an array or string. Returns array.
|
246
173
|
#
|
247
174
|
def add_exclude_condition(conditions, exclude)
|
175
|
+
conditions = [conditions] if conditions.is_a?(String)
|
248
176
|
if exclude
|
249
|
-
conditions[0] << " AND #{full_column_name(
|
177
|
+
conditions[0] << " AND #{full_column_name(primary_key)} != ?"
|
250
178
|
conditions << exclude.id
|
251
179
|
end
|
252
180
|
conditions
|
@@ -280,8 +208,8 @@ module Geocoder::Store
|
|
280
208
|
do_lookup(false) do |o,rs|
|
281
209
|
if r = rs.first
|
282
210
|
unless r.latitude.nil? or r.longitude.nil?
|
283
|
-
o.
|
284
|
-
o.
|
211
|
+
o.__send__ "#{self.class.geocoder_options[:latitude]}=", r.latitude
|
212
|
+
o.__send__ "#{self.class.geocoder_options[:longitude]}=", r.longitude
|
285
213
|
end
|
286
214
|
r.coordinates
|
287
215
|
end
|
@@ -298,7 +226,7 @@ module Geocoder::Store
|
|
298
226
|
do_lookup(true) do |o,rs|
|
299
227
|
if r = rs.first
|
300
228
|
unless r.address.nil?
|
301
|
-
o.
|
229
|
+
o.__send__ "#{self.class.geocoder_options[:fetched_address]}=", r.address
|
302
230
|
end
|
303
231
|
r.address
|
304
232
|
end
|