geokit-rails 2.3.2 → 2.5.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/.travis.yml +29 -3
- data/CHANGELOG.md +5 -0
- data/README.markdown +7 -7
- data/gemfiles/rails3.gemfile +1 -0
- data/gemfiles/rails4.gemfile +1 -1
- data/gemfiles/rails5.gemfile +0 -1
- data/gemfiles/rails6.0.gemfile +8 -0
- data/gemfiles/rails6.1.gemfile +8 -0
- data/geokit-rails.gemspec +6 -5
- data/lib/geokit-rails/acts_as_mappable/class_methods.rb +297 -0
- data/lib/geokit-rails/acts_as_mappable/glue.rb +54 -0
- data/lib/geokit-rails/acts_as_mappable.rb +0 -347
- data/lib/geokit-rails/{core_extensions.rb → distance_collection.rb} +0 -0
- data/lib/geokit-rails/ip_geocode_lookup.rb +4 -7
- data/lib/geokit-rails/version.rb +1 -1
- data/lib/geokit-rails.rb +3 -1
- data/test/distance_collection_test.rb +42 -0
- data/test/dummy/Rakefile +6 -0
- data/test/dummy/app/assets/images/.keep +0 -0
- data/test/dummy/app/assets/stylesheets/application.css +1 -0
- data/test/dummy/app/channels/application_cable/channel.rb +4 -0
- data/test/dummy/app/channels/application_cable/connection.rb +4 -0
- data/test/dummy/app/controllers/application_controller.rb +3 -0
- data/test/dummy/app/controllers/concerns/.keep +0 -0
- data/test/dummy/app/controllers/location_aware_controller.rb +43 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/jobs/application_job.rb +7 -0
- data/test/dummy/app/mailers/application_mailer.rb +4 -0
- data/test/dummy/app/models/application_record.rb +3 -0
- data/test/dummy/app/models/concerns/.keep +0 -0
- data/test/dummy/app/views/layouts/application.html.erb +15 -0
- data/test/dummy/app/views/layouts/mailer.html.erb +13 -0
- data/test/dummy/app/views/layouts/mailer.text.erb +1 -0
- data/test/dummy/bin/rails +4 -0
- data/test/dummy/bin/rake +4 -0
- data/test/dummy/bin/setup +33 -0
- data/test/dummy/config/application.rb +22 -0
- data/test/dummy/config/boot.rb +5 -0
- data/test/dummy/config/cable.yml +10 -0
- data/test/dummy/config/database.yml +25 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +68 -0
- data/test/dummy/config/environments/production.rb +87 -0
- data/test/dummy/config/environments/test.rb +60 -0
- data/test/dummy/config/initializers/content_security_policy.rb +25 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +8 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/initializers/permissions_policy.rb +11 -0
- data/test/dummy/config/locales/en.yml +33 -0
- data/test/dummy/config/puma.rb +43 -0
- data/test/dummy/config/routes.rb +6 -0
- data/test/dummy/config/storage.yml +34 -0
- data/test/dummy/config.ru +6 -0
- data/test/dummy/lib/assets/.keep +0 -0
- data/test/dummy/log/.keep +0 -0
- data/test/dummy/public/404.html +67 -0
- data/test/dummy/public/422.html +67 -0
- data/test/dummy/public/500.html +66 -0
- data/test/dummy/public/apple-touch-icon-precomposed.png +0 -0
- data/test/dummy/public/apple-touch-icon.png +0 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/storage/.keep +0 -0
- data/test/dummy/tmp/.keep +0 -0
- data/test/dummy/tmp/pids/.keep +0 -0
- data/test/dummy/tmp/storage/.keep +0 -0
- data/test/ip_geocode_lookup_test.rb +49 -0
- data/test/test_helper.rb +17 -0
- metadata +129 -27
- data/test/ip_geocode_lookup_test.disabled.rb +0 -82
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c4bc1edb34957251330bf39e6c777d1b8e01fba0e23388b08b3f07918bb9d1ab
|
4
|
+
data.tar.gz: c4f0899abed4cbc930dba47b122851a8ac34debdf5e05bbc39a8143fc61a3d36
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d3f0175f5717e228e12cf36df832b792ec9da4ea8ba30bee8511222dfacd89a2634308d734c84b514e57d4055400e6899c62d774c1baff0d1d65e9d5b666dae3
|
7
|
+
data.tar.gz: e6939ec41db36ce5603606d3815952f32c2826390bac2a68752cc97c695094e15759ae1417150d5343bebdd606d29e6aa0397a9db62ad65b84bf11114795bc88
|
data/.travis.yml
CHANGED
@@ -5,10 +5,15 @@ rvm:
|
|
5
5
|
- 2.2
|
6
6
|
- 2.3
|
7
7
|
- 2.4
|
8
|
+
- 2.5
|
9
|
+
- 2.6
|
10
|
+
- ruby-head
|
8
11
|
gemfile:
|
9
12
|
- gemfiles/rails3.gemfile
|
10
13
|
- gemfiles/rails4.gemfile
|
11
14
|
- gemfiles/rails5.gemfile
|
15
|
+
- gemfiles/rails6.0.gemfile
|
16
|
+
- gemfiles/rails6.1.gemfile
|
12
17
|
matrix:
|
13
18
|
exclude:
|
14
19
|
- rvm: 2.2
|
@@ -21,9 +26,30 @@ matrix:
|
|
21
26
|
gemfile: gemfiles/rails5.gemfile
|
22
27
|
- rvm: 2.1
|
23
28
|
gemfile: gemfiles/rails5.gemfile
|
29
|
+
- rvm: 2.6
|
30
|
+
gemfile: gemfiles/rails5.gemfile
|
31
|
+
- rvm: ruby-head
|
32
|
+
gemfile: gemfiles/rails5.gemfile
|
33
|
+
- rvm: 2.0
|
34
|
+
gemfile: gemfiles/rails6.0.gemfile
|
35
|
+
- rvm: 2.0
|
36
|
+
gemfile: gemfiles/rails6.1.gemfile
|
37
|
+
- rvm: 2.1
|
38
|
+
gemfile: gemfiles/rails6.0.gemfile
|
39
|
+
- rvm: 2.1
|
40
|
+
gemfile: gemfiles/rails6.1.gemfile
|
41
|
+
- rvm: 2.2
|
42
|
+
gemfile: gemfiles/rails6.0.gemfile
|
43
|
+
- rvm: 2.2
|
44
|
+
gemfile: gemfiles/rails6.1.gemfile
|
45
|
+
- rvm: 2.3
|
46
|
+
gemfile: gemfiles/rails6.0.gemfile
|
47
|
+
- rvm: 2.3
|
48
|
+
gemfile: gemfiles/rails6.1.gemfile
|
49
|
+
- rvm: 2.4
|
50
|
+
gemfile: gemfiles/rails6.0.gemfile
|
51
|
+
- rvm: 2.4
|
52
|
+
gemfile: gemfiles/rails6.1.gemfile
|
24
53
|
script: "bundle exec rake coverage"
|
25
|
-
before_install:
|
26
|
-
- gem install bundler
|
27
54
|
bundler_args: --retry 5
|
28
|
-
sudo: false
|
29
55
|
cache: bundler
|
data/CHANGELOG.md
CHANGED
data/README.markdown
CHANGED
@@ -1,11 +1,10 @@
|
|
1
1
|
Geokit Rails
|
2
2
|
============
|
3
3
|
|
4
|
-
[](https://codeclimate.com/github/geokit/geokit-rails)
|
4
|
+
[](http://badge.fury.io/rb/geokit-rails)
|
5
|
+
[](https://travis-ci.org/geokit/geokit-rails)
|
6
|
+
[](https://coveralls.io/r/geokit/geokit-rails)
|
7
|
+
[](https://codeclimate.com/github/geokit/geokit-rails)
|
9
8
|
|
10
9
|
## COMMUNICATION
|
11
10
|
|
@@ -498,8 +497,9 @@ Pass the lat/lng as a string, array or LatLng instance:
|
|
498
497
|
|
499
498
|
```ruby
|
500
499
|
res=Geokit::Geocoders::GoogleGeocoder.reverse_geocode "37.791821,-122.394679"
|
501
|
-
=> #<Geokit::GeoLoc:0x558ed0 ...
|
502
|
-
res.full_address
|
500
|
+
# => #<Geokit::GeoLoc:0x558ed0 ...
|
501
|
+
res.full_address
|
502
|
+
# => "101-115 Main St, San Francisco, CA 94105, USA"
|
503
503
|
```
|
504
504
|
|
505
505
|
The address will usually appear as a range, as it does in the above example.
|
data/gemfiles/rails3.gemfile
CHANGED
data/gemfiles/rails4.gemfile
CHANGED
data/gemfiles/rails5.gemfile
CHANGED
data/geokit-rails.gemspec
CHANGED
@@ -20,14 +20,15 @@ Gem::Specification.new do |spec|
|
|
20
20
|
spec.add_dependency 'rails', '>= 3.0'
|
21
21
|
spec.add_dependency 'geokit', '~> 1.5'
|
22
22
|
spec.add_development_dependency "bundler", "> 1.0"
|
23
|
-
spec.add_development_dependency "simplecov", "
|
23
|
+
spec.add_development_dependency "simplecov", ">= 0.16.1"
|
24
24
|
spec.add_development_dependency "simplecov-rcov"
|
25
|
+
spec.add_development_dependency 'net-http'
|
25
26
|
spec.add_development_dependency 'rake'
|
26
27
|
spec.add_development_dependency 'test-unit'
|
27
28
|
spec.add_development_dependency "mocha", "~> 0.9"
|
28
|
-
spec.add_development_dependency '
|
29
|
-
spec.add_development_dependency "mysql2", "
|
30
|
-
spec.add_development_dependency "activerecord-mysql2spatial-adapter"
|
31
|
-
spec.add_development_dependency "pg", "
|
29
|
+
spec.add_development_dependency 'coveralls_reborn'
|
30
|
+
spec.add_development_dependency "mysql2", ">= 0.2"
|
31
|
+
# spec.add_development_dependency "activerecord-mysql2spatial-adapter"
|
32
|
+
spec.add_development_dependency "pg", ">= 0.10"
|
32
33
|
spec.add_development_dependency "sqlite3"
|
33
34
|
end
|
@@ -0,0 +1,297 @@
|
|
1
|
+
module Geokit
|
2
|
+
module ActsAsMappable
|
3
|
+
|
4
|
+
# Class methods included in models when +acts_as_mappable+ is called
|
5
|
+
module ClassMethods
|
6
|
+
|
7
|
+
# A proxy to an instance of a finder adapter, inferred from the connection's adapter.
|
8
|
+
def geokit_finder_adapter
|
9
|
+
@geokit_finder_adapter ||= begin
|
10
|
+
unless Adapters.const_defined?(connection.adapter_name.camelcase)
|
11
|
+
filename = connection.adapter_name.downcase
|
12
|
+
require File.join("geokit-rails", "adapters", filename)
|
13
|
+
end
|
14
|
+
klass = Adapters.const_get(connection.adapter_name.camelcase)
|
15
|
+
if klass.class == Module
|
16
|
+
# For some reason Mysql2 adapter was defined in Adapters.constants but was Module instead of a Class
|
17
|
+
filename = connection.adapter_name.downcase
|
18
|
+
require File.join("geokit-rails", "adapters", filename)
|
19
|
+
# Re-init the klass after require
|
20
|
+
klass = Adapters.const_get(connection.adapter_name.camelcase)
|
21
|
+
end
|
22
|
+
klass.load(self) unless klass.loaded || skip_loading
|
23
|
+
klass.new(self)
|
24
|
+
rescue LoadError
|
25
|
+
raise UnsupportedAdapter, "`#{connection.adapter_name.downcase}` is not a supported adapter."
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def within(distance, options = {})
|
30
|
+
options[:within] = distance
|
31
|
+
# Add bounding box to speed up SQL request.
|
32
|
+
bounds = formulate_bounds_from_distance(
|
33
|
+
options,
|
34
|
+
normalize_point_to_lat_lng(options[:origin]),
|
35
|
+
options[:units] || default_units)
|
36
|
+
with_latlng.where(bound_conditions(bounds)).
|
37
|
+
where(distance_conditions(options))
|
38
|
+
end
|
39
|
+
alias inside within
|
40
|
+
|
41
|
+
def beyond(distance, options = {})
|
42
|
+
options[:beyond] = distance
|
43
|
+
#geo_scope(options)
|
44
|
+
where(distance_conditions(options))
|
45
|
+
end
|
46
|
+
alias outside beyond
|
47
|
+
|
48
|
+
def in_range(range, options = {})
|
49
|
+
options[:range] = range
|
50
|
+
#geo_scope(options)
|
51
|
+
where(distance_conditions(options))
|
52
|
+
end
|
53
|
+
|
54
|
+
def in_bounds(bounds, options = {})
|
55
|
+
inclusive = options.delete(:inclusive) || false
|
56
|
+
options[:bounds] = bounds
|
57
|
+
#geo_scope(options)
|
58
|
+
#where(distance_conditions(options))
|
59
|
+
bounds = extract_bounds_from_options(options)
|
60
|
+
where(bound_conditions(bounds, inclusive))
|
61
|
+
end
|
62
|
+
|
63
|
+
def by_distance(options = {})
|
64
|
+
origin = extract_origin_from_options(options)
|
65
|
+
units = extract_units_from_options(options)
|
66
|
+
formula = extract_formula_from_options(options)
|
67
|
+
distance_column_name = distance_sql(origin, units, formula)
|
68
|
+
with_latlng.order(
|
69
|
+
Arel.sql(distance_column_name).send(options[:reverse] ? 'desc' : 'asc')
|
70
|
+
)
|
71
|
+
end
|
72
|
+
|
73
|
+
def with_latlng
|
74
|
+
where("#{qualified_lat_column_name} IS NOT NULL AND #{qualified_lng_column_name} IS NOT NULL")
|
75
|
+
end
|
76
|
+
|
77
|
+
def closest(options = {})
|
78
|
+
by_distance(options).limit(1)
|
79
|
+
end
|
80
|
+
alias nearest closest
|
81
|
+
|
82
|
+
def farthest(options = {})
|
83
|
+
by_distance({:reverse => true}.merge(options)).limit(1)
|
84
|
+
end
|
85
|
+
|
86
|
+
#def geo_scope(options = {})
|
87
|
+
# arel = self.is_a?(ActiveRecord::Relation) ? self : self.scoped
|
88
|
+
|
89
|
+
# origin = extract_origin_from_options(options)
|
90
|
+
# units = extract_units_from_options(options)
|
91
|
+
# formula = extract_formula_from_options(options)
|
92
|
+
# bounds = extract_bounds_from_options(options)
|
93
|
+
|
94
|
+
# if origin || bounds
|
95
|
+
# bounds = formulate_bounds_from_distance(options, origin, units) unless bounds
|
96
|
+
|
97
|
+
# if origin
|
98
|
+
# arel.distance_formula = distance_sql(origin, units, formula)
|
99
|
+
#
|
100
|
+
# if arel.select_values.blank?
|
101
|
+
# star_select = Arel::Nodes::SqlLiteral.new(arel.quoted_table_name + '.*')
|
102
|
+
# arel = arel.select(star_select)
|
103
|
+
# end
|
104
|
+
# end
|
105
|
+
|
106
|
+
# if bounds
|
107
|
+
# bound_conditions = bound_conditions(bounds)
|
108
|
+
# arel = arel.where(bound_conditions) if bound_conditions
|
109
|
+
# end
|
110
|
+
|
111
|
+
# distance_conditions = distance_conditions(options)
|
112
|
+
# arel = arel.where(distance_conditions) if distance_conditions
|
113
|
+
|
114
|
+
# if self.through
|
115
|
+
# arel = arel.includes(self.through)
|
116
|
+
# end
|
117
|
+
# end
|
118
|
+
|
119
|
+
# arel
|
120
|
+
#end
|
121
|
+
|
122
|
+
# Returns the distance calculation to be used as a display column or a condition. This
|
123
|
+
# is provide for anyone wanting access to the raw SQL.
|
124
|
+
def distance_sql(origin, units=default_units, formula=default_formula)
|
125
|
+
case formula
|
126
|
+
when :sphere
|
127
|
+
sql = sphere_distance_sql(origin, units)
|
128
|
+
when :flat
|
129
|
+
sql = flat_distance_sql(origin, units)
|
130
|
+
end
|
131
|
+
sql
|
132
|
+
end
|
133
|
+
|
134
|
+
private
|
135
|
+
|
136
|
+
# Override ActiveRecord::Base.relation to return an instance of Geokit::ActsAsMappable::Relation.
|
137
|
+
# TODO: Do we need to override JoinDependency#relation too?
|
138
|
+
#def relation
|
139
|
+
# # NOTE: This cannot be @relation as ActiveRecord already uses this to
|
140
|
+
# # cache *its* Relation object
|
141
|
+
# @_geokit_relation ||= Relation.new(self, arel_table)
|
142
|
+
# finder_needs_type_condition? ? @_geokit_relation.where(type_condition) : @_geokit_relation
|
143
|
+
#end
|
144
|
+
|
145
|
+
# If it's a :within query, add a bounding box to improve performance.
|
146
|
+
# This only gets called if a :bounds argument is not otherwise supplied.
|
147
|
+
def formulate_bounds_from_distance(options, origin, units)
|
148
|
+
distance = options[:within] if options.has_key?(:within) && options[:within].is_a?(Numeric)
|
149
|
+
distance = options[:range].last-(options[:range].exclude_end?? 1 : 0) if options.has_key?(:range)
|
150
|
+
if distance
|
151
|
+
Geokit::Bounds.from_point_and_radius(origin,distance,:units=>units)
|
152
|
+
else
|
153
|
+
nil
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def distance_conditions(options)
|
158
|
+
origin = extract_origin_from_options(options)
|
159
|
+
units = extract_units_from_options(options)
|
160
|
+
formula = extract_formula_from_options(options)
|
161
|
+
distance_column_name = distance_sql(origin, units, formula)
|
162
|
+
|
163
|
+
if options.has_key?(:within)
|
164
|
+
Arel.sql(distance_column_name).lteq(options[:within])
|
165
|
+
elsif options.has_key?(:beyond)
|
166
|
+
Arel.sql(distance_column_name).gt(options[:beyond])
|
167
|
+
elsif options.has_key?(:range)
|
168
|
+
min_condition = Arel.sql(distance_column_name).gteq(options[:range].begin)
|
169
|
+
max_condition = if options[:range].exclude_end?
|
170
|
+
Arel.sql(distance_column_name).lt(options[:range].end)
|
171
|
+
else
|
172
|
+
Arel.sql(distance_column_name).lteq(options[:range].end)
|
173
|
+
end
|
174
|
+
min_condition.and(max_condition)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def bound_conditions(bounds, inclusive = false)
|
179
|
+
return nil unless bounds
|
180
|
+
if inclusive
|
181
|
+
lt_operator = :lteq
|
182
|
+
gt_operator = :gteq
|
183
|
+
else
|
184
|
+
lt_operator = :lt
|
185
|
+
gt_operator = :gt
|
186
|
+
end
|
187
|
+
sw,ne = bounds.sw, bounds.ne
|
188
|
+
lat, lng = Arel.sql(qualified_lat_column_name), Arel.sql(qualified_lng_column_name)
|
189
|
+
lat.send(gt_operator, sw.lat).and(lat.send(lt_operator, ne.lat)).and(
|
190
|
+
if bounds.crosses_meridian?
|
191
|
+
lng.send(lt_operator, ne.lng).or(lng.send(gt_operator, sw.lng))
|
192
|
+
else
|
193
|
+
lng.send(gt_operator, sw.lng).and(lng.send(lt_operator, ne.lng))
|
194
|
+
end
|
195
|
+
)
|
196
|
+
end
|
197
|
+
|
198
|
+
# Extracts the origin instance out of the options if it exists and returns
|
199
|
+
# it. If there is no origin, looks for latitude and longitude values to
|
200
|
+
# create an origin. The side-effect of the method is to remove these
|
201
|
+
# option keys from the hash.
|
202
|
+
def extract_origin_from_options(options)
|
203
|
+
origin = options.delete(:origin)
|
204
|
+
res = normalize_point_to_lat_lng(origin) if origin
|
205
|
+
res
|
206
|
+
end
|
207
|
+
|
208
|
+
# Extract the units out of the options if it exists and returns it. If
|
209
|
+
# there is no :units key, it uses the default. The side effect of the
|
210
|
+
# method is to remove the :units key from the options hash.
|
211
|
+
def extract_units_from_options(options)
|
212
|
+
units = options[:units] || default_units
|
213
|
+
options.delete(:units)
|
214
|
+
units
|
215
|
+
end
|
216
|
+
|
217
|
+
# Extract the formula out of the options if it exists and returns it. If
|
218
|
+
# there is no :formula key, it uses the default. The side effect of the
|
219
|
+
# method is to remove the :formula key from the options hash.
|
220
|
+
def extract_formula_from_options(options)
|
221
|
+
formula = options[:formula] || default_formula
|
222
|
+
options.delete(:formula)
|
223
|
+
formula
|
224
|
+
end
|
225
|
+
|
226
|
+
def extract_bounds_from_options(options)
|
227
|
+
bounds = options.delete(:bounds)
|
228
|
+
bounds = Geokit::Bounds.normalize(bounds) if bounds
|
229
|
+
end
|
230
|
+
|
231
|
+
# Geocode IP address.
|
232
|
+
def geocode_ip_address(origin)
|
233
|
+
geo_location = Geokit::Geocoders::MultiGeocoder.geocode(origin)
|
234
|
+
return geo_location if geo_location.success
|
235
|
+
raise Geokit::Geocoders::GeocodeError
|
236
|
+
end
|
237
|
+
|
238
|
+
# Given a point in a variety of (an address to geocode,
|
239
|
+
# an array of [lat,lng], or an object with appropriate lat/lng methods, an IP addres)
|
240
|
+
# this method will normalize it into a Geokit::LatLng instance. The only thing this
|
241
|
+
# method adds on top of LatLng#normalize is handling of IP addresses
|
242
|
+
def normalize_point_to_lat_lng(point)
|
243
|
+
res = geocode_ip_address(point) if point.is_a?(String) && /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})?$/.match(point)
|
244
|
+
res = Geokit::LatLng.normalize(point) unless res
|
245
|
+
res
|
246
|
+
end
|
247
|
+
|
248
|
+
# Looks for the distance column and replaces it with the distance sql. If an origin was not
|
249
|
+
# passed in and the distance column exists, we leave it to be flagged as bad SQL by the database.
|
250
|
+
# Conditions are either a string or an array. In the case of an array, the first entry contains
|
251
|
+
# the condition.
|
252
|
+
def substitute_distance_in_where_values(arel, origin, units=default_units, formula=default_formula)
|
253
|
+
pattern = Regexp.new("\\b#{distance_column_name}\\b")
|
254
|
+
value = distance_sql(origin, units, formula)
|
255
|
+
arel.where_values.map! do |where_value|
|
256
|
+
if where_value.is_a?(String)
|
257
|
+
where_value.gsub(pattern, value)
|
258
|
+
else
|
259
|
+
where_value
|
260
|
+
end
|
261
|
+
end
|
262
|
+
arel
|
263
|
+
end
|
264
|
+
|
265
|
+
# Returns the distance SQL using the spherical world formula (Haversine). The SQL is tuned
|
266
|
+
# to the database in use.
|
267
|
+
def sphere_distance_sql(origin, units)
|
268
|
+
# "origin" can be a Geokit::LatLng (with :lat and :lng methods), e.g.
|
269
|
+
# when using geo_scope or it can be an ActsAsMappable with customized
|
270
|
+
# latitude and longitude methods, e.g. when using distance_sql.
|
271
|
+
lat = deg2rad(get_lat(origin))
|
272
|
+
lng = deg2rad(get_lng(origin))
|
273
|
+
multiplier = units_sphere_multiplier(units)
|
274
|
+
geokit_finder_adapter.sphere_distance_sql(lat, lng, multiplier) if geokit_finder_adapter
|
275
|
+
end
|
276
|
+
|
277
|
+
# Returns the distance SQL using the flat-world formula (Phythagorean Theory). The SQL is tuned
|
278
|
+
# to the database in use.
|
279
|
+
def flat_distance_sql(origin, units)
|
280
|
+
lat_degree_units = units_per_latitude_degree(units)
|
281
|
+
lng_degree_units = units_per_longitude_degree(get_lat(origin), units)
|
282
|
+
geokit_finder_adapter.flat_distance_sql(origin, lat_degree_units, lng_degree_units)
|
283
|
+
end
|
284
|
+
|
285
|
+
def get_lat(origin)
|
286
|
+
origin.respond_to?(:lat) ? origin.lat \
|
287
|
+
: origin.send(:"#{lat_column_name}")
|
288
|
+
end
|
289
|
+
|
290
|
+
def get_lng(origin)
|
291
|
+
origin.respond_to?(:lng) ? origin.lng \
|
292
|
+
: origin.send(:"#{lng_column_name}")
|
293
|
+
end
|
294
|
+
|
295
|
+
end # ClassMethods
|
296
|
+
end
|
297
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Geokit
|
2
|
+
module ActsAsMappable
|
3
|
+
|
4
|
+
# Add the +acts_as_mappable+ method into ActiveRecord subclasses
|
5
|
+
module Glue # :nodoc:
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
module ClassMethods # :nodoc:
|
9
|
+
OPTION_SYMBOLS = [ :distance_column_name, :default_units, :default_formula, :lat_column_name, :lng_column_name, :qualified_lat_column_name, :qualified_lng_column_name, :skip_loading ]
|
10
|
+
|
11
|
+
def acts_as_mappable(options = {})
|
12
|
+
metaclass = (class << self; self; end)
|
13
|
+
|
14
|
+
include Geokit::ActsAsMappable
|
15
|
+
|
16
|
+
cattr_accessor :through
|
17
|
+
self.through = options[:through]
|
18
|
+
|
19
|
+
if reflection = Geokit::ActsAsMappable.end_of_reflection_chain(self.through, self)
|
20
|
+
metaclass.instance_eval do
|
21
|
+
OPTION_SYMBOLS.each do |method_name|
|
22
|
+
define_method method_name do
|
23
|
+
reflection.klass.send(method_name)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
else
|
28
|
+
cattr_accessor *OPTION_SYMBOLS
|
29
|
+
|
30
|
+
self.distance_column_name = options[:distance_column_name] || 'distance'
|
31
|
+
self.default_units = options[:default_units] || Geokit::default_units
|
32
|
+
self.default_formula = options[:default_formula] || Geokit::default_formula
|
33
|
+
self.lat_column_name = options[:lat_column_name] || 'lat'
|
34
|
+
self.lng_column_name = options[:lng_column_name] || 'lng'
|
35
|
+
self.skip_loading = options[:skip_loading]
|
36
|
+
self.qualified_lat_column_name = "#{table_name}.#{lat_column_name}"
|
37
|
+
self.qualified_lng_column_name = "#{table_name}.#{lng_column_name}"
|
38
|
+
|
39
|
+
if options.include?(:auto_geocode) && options[:auto_geocode]
|
40
|
+
# if the form auto_geocode=>true is used, let the defaults take over by suppling an empty hash
|
41
|
+
options[:auto_geocode] = {} if options[:auto_geocode] == true
|
42
|
+
cattr_accessor :auto_geocode_field, :auto_geocode_error_message
|
43
|
+
self.auto_geocode_field = options[:auto_geocode][:field] || 'address'
|
44
|
+
self.auto_geocode_error_message = options[:auto_geocode][:error_message] || 'could not locate address'
|
45
|
+
|
46
|
+
# set the actual callback here
|
47
|
+
before_validation :auto_geocode_address, :on => :create
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end # Glue
|
53
|
+
end
|
54
|
+
end
|