geo_tools 1.0.1 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,266 +0,0 @@
1
- module AirBlade
2
- module GeoTools
3
- module Location
4
-
5
- def self.included(base)
6
- # Lazy loading pattern.
7
- base.extend ActMethods
8
- end
9
-
10
- module ActMethods
11
- def acts_as_location
12
- unless included_modules.include? InstanceMethods
13
- extend ClassMethods
14
- include InstanceMethods
15
-
16
- code = <<-END
17
- validates_numericality_of_for :latitude_degrees,
18
- :only_integer => true,
19
- :greater_than_or_equal_to => 0,
20
- :less_than_or_equal_to => 90,
21
- :message => 'Degrees are invalid',
22
- :for => :latitude
23
-
24
- validates_numericality_of_for :latitude_minutes,
25
- :only_integer => true,
26
- :greater_than_or_equal_to => 0,
27
- :less_than => 60,
28
- :message => 'Minutes are invalid',
29
- :for => :latitude
30
-
31
- validates_numericality_of_for :latitude_decimal_minutes,
32
- :only_integer => true,
33
- :greater_than_or_equal_to => 0,
34
- :message => 'Decimal minutes are invalid',
35
- :for => :latitude
36
-
37
- validates_numericality_of_for :latitude_decimal_minutes_width,
38
- :only_integer => true,
39
- :greater_than_or_equal_to => 0,
40
- :for => :latitude
41
-
42
- validates_inclusion_of_for :latitude_hemisphere,
43
- :in => %w( N S ),
44
- :message => 'Hemisphere is invalid',
45
- :for => :latitude
46
-
47
- validates_numericality_of_for :longitude_degrees,
48
- :only_integer => true,
49
- :greater_than_or_equal_to => 0,
50
- :less_than_or_equal_to => 180,
51
- :message => 'Degrees are invalid',
52
- :for => :longitude
53
-
54
- validates_numericality_of_for :longitude_minutes,
55
- :only_integer => true,
56
- :greater_than_or_equal_to => 0,
57
- :less_than => 60,
58
- :message => 'Minutes are invalid',
59
- :for => :longitude
60
-
61
- validates_numericality_of_for :longitude_decimal_minutes,
62
- :only_integer => true,
63
- :greater_than_or_equal_to => 0,
64
- :message => 'Decimal minutes are invalid',
65
- :for => :longitude
66
-
67
- validates_numericality_of_for :longitude_decimal_minutes_width,
68
- :only_integer => true,
69
- :greater_than_or_equal_to => 0,
70
- :for => :longitude
71
-
72
- validates_inclusion_of_for :longitude_hemisphere,
73
- :in => %w( E W ),
74
- :message => 'Hemisphere is invalid',
75
- :for => :longitude
76
-
77
- before_validation :set_empty_values
78
- END
79
- class_eval code, __FILE__, __LINE__
80
-
81
- # Returns all locations within the given bounding box, to an accuracy of 1 minute.
82
- #
83
- # This is useful for finding all locations within the area covered by a Google map.
84
- #
85
- # The parameters should be positive/negative floats.
86
- named_scope :within, lambda { |sw_lat, sw_lng, ne_lat, ne_lng|
87
- sw_lat_degs = sw_lat.to_i.abs
88
- sw_lat_mins = ((sw_lat - sw_lat.to_i) * 60.0).round.abs
89
- ne_lat_degs = ne_lat.to_i.abs
90
- ne_lat_mins = ((ne_lat - ne_lat.to_i) * 60.0).round.abs
91
-
92
- sw_lng_degs = sw_lng.to_i.abs
93
- sw_lng_mins = ((sw_lng - sw_lng.to_i) * 60.0).round.abs
94
- ne_lng_degs = ne_lng.to_i.abs
95
- ne_lng_mins = ((ne_lng - ne_lng.to_i) * 60.0).round.abs
96
-
97
- # Latitude conditions.
98
- if sw_lat > 0 && ne_lat > 0 # northern hemisphere
99
- condition_lat_h = 'latitude_hemisphere = "N"'
100
- condition_lat_sw = ["(latitude_degrees > ?) OR (latitude_degrees = ? AND latitude_minutes >= ?)", sw_lat_degs, sw_lat_degs, sw_lat_mins]
101
- condition_lat_ne = ["(latitude_degrees < ?) OR (latitude_degrees = ? AND latitude_minutes <= ?)", ne_lat_degs, ne_lat_degs, ne_lat_mins]
102
- condition_lat = merge_conditions condition_lat_h, condition_lat_sw, condition_lat_ne
103
-
104
- elsif sw_lat < 0 && ne_lat < 0 # southern hemisphere
105
- condition_lat_h = 'latitude_hemisphere = "S"'
106
- condition_lat_sw = ["(latitude_degrees < ?) OR (latitude_degrees = ? AND latitude_minutes <= ?)", sw_lat_degs, sw_lat_degs, sw_lat_mins]
107
- condition_lat_ne = ["(latitude_degrees > ?) OR (latitude_degrees = ? AND latitude_minutes >= ?)", ne_lat_degs, ne_lat_degs, ne_lat_mins]
108
- condition_lat = merge_conditions condition_lat_h, condition_lat_sw, condition_lat_ne
109
-
110
- elsif sw_lat <= 0 && ne_lat >= 0 # straddles equator
111
- condition_lat_h = 'latitude_hemisphere = "S"'
112
- condition_lat_sw = ["(latitude_degrees < ?) OR (latitude_degrees = ? AND latitude_minutes <= ?)", sw_lat_degs, sw_lat_degs, sw_lat_mins]
113
- condition_lat_s = merge_conditions condition_lat_h, condition_lat_sw
114
-
115
- condition_lat_h = 'latitude_hemisphere = "N"'
116
- condition_lat_ne = ["(latitude_degrees < ?) OR (latitude_degrees = ? AND latitude_minutes <= ?)", ne_lat_degs, ne_lat_degs, ne_lat_mins]
117
- condition_lat_n = merge_conditions condition_lat_h, condition_lat_ne
118
-
119
- condition_lat = merge_or_conditions condition_lat_s, condition_lat_n
120
- end
121
-
122
- # Longitude conditions.
123
- if sw_lng > 0 && ne_lng > 0 # eastern hemisphere
124
- condition_lng_h = 'longitude_hemisphere = "E"'
125
- condition_lng_sw = ["(longitude_degrees > ?) OR (longitude_degrees = ? AND longitude_minutes >= ?)", sw_lng_degs, sw_lng_degs, sw_lng_mins]
126
- condition_lng_ne = ["(longitude_degrees < ?) OR (longitude_degrees = ? AND longitude_minutes <= ?)", ne_lng_degs, ne_lng_degs, ne_lng_mins]
127
- condition_lng = merge_conditions condition_lng_h, condition_lng_sw, condition_lng_ne
128
-
129
- elsif sw_lng < 0 && ne_lng < 0 # western hemisphere
130
- condition_lng_h = 'longitude_hemisphere = "W"'
131
- condition_lng_sw = ["(longitude_degrees < ?) OR (longitude_degrees = ? AND longitude_minutes <= ?)", sw_lng_degs, sw_lng_degs, sw_lng_mins]
132
- condition_lng_ne = ["(longitude_degrees > ?) OR (longitude_degrees = ? AND longitude_minutes >= ?)", ne_lng_degs, ne_lng_degs, ne_lng_mins]
133
- condition_lng = merge_conditions condition_lng_h, condition_lng_sw, condition_lng_ne
134
-
135
- elsif sw_lng <= 0 && ne_lng >= 0 # straddles prime meridian
136
- condition_lng_h = 'longitude_hemisphere = "W"'
137
- condition_lng_sw = ["(longitude_degrees < ?) OR (longitude_degrees = ? AND longitude_minutes <= ?)", sw_lng_degs, sw_lng_degs, sw_lng_mins]
138
- condition_lng_w = merge_conditions condition_lng_h, condition_lng_sw
139
-
140
- condition_lng_h = 'longitude_hemisphere = "E"'
141
- condition_lng_ne = ["(longitude_degrees < ?) OR (longitude_degrees = ? AND longitude_minutes <= ?)", ne_lng_degs, ne_lng_degs, ne_lng_mins]
142
- condition_lng_e = merge_conditions condition_lng_h, condition_lng_ne
143
-
144
- condition_lng = merge_or_conditions condition_lng_w, condition_lng_e
145
- end
146
-
147
- # Combined latitude and longitude conditions.
148
- {:conditions => merge_conditions(condition_lat, condition_lng)}
149
- }
150
-
151
- end
152
- end
153
- end
154
-
155
- module ClassMethods
156
- # Merges conditions so that the result is a valid +condition+.
157
- # Adapted from ActiveRecord::Base#merge_conditions.
158
- def merge_or_conditions(*conditions)
159
- segments = []
160
-
161
- conditions.each do |condition|
162
- unless condition.blank?
163
- sql = sanitize_sql(condition)
164
- segments << sql unless sql.blank?
165
- end
166
- end
167
-
168
- "(#{segments.join(') OR (')})" unless segments.empty?
169
- end
170
- end
171
-
172
- module InstanceMethods
173
-
174
- def latitude_decimal_minutes=(value)
175
- unless value.nil?
176
- width = value.to_s.length
177
- value = value.to_i
178
-
179
- write_attribute :latitude_decimal_minutes, value
180
- write_attribute :latitude_decimal_minutes_width, width
181
- end
182
- end
183
-
184
- def latitude_decimal_minutes_as_string
185
- "%0#{latitude_decimal_minutes_width}d" % latitude_decimal_minutes
186
- end
187
-
188
- def longitude_decimal_minutes=(value)
189
- unless value.nil?
190
- width = value.to_s.length
191
- value = value.to_i
192
-
193
- write_attribute :longitude_decimal_minutes, value
194
- write_attribute :longitude_decimal_minutes_width, width
195
- end
196
- end
197
-
198
- def longitude_decimal_minutes_as_string
199
- "%0#{longitude_decimal_minutes_width}d" % longitude_decimal_minutes
200
- end
201
-
202
- def latitude
203
- to_float latitude_degrees, latitude_minutes, latitude_decimal_minutes,
204
- latitude_decimal_minutes_width, latitude_hemisphere
205
- end
206
-
207
- def longitude
208
- to_float longitude_degrees, longitude_minutes, longitude_decimal_minutes,
209
- longitude_decimal_minutes_width, longitude_hemisphere
210
- end
211
-
212
- def to_s
213
- # Unicode degree symbol, full stop, Unicode minute symbol.
214
- units = [ "\xc2\xb0", '.', "\xe2\x80\xb2" ]
215
-
216
- lat_fields = ["%02d" % latitude_degrees,
217
- "%02d" % latitude_minutes,
218
- latitude_decimal_minutes_as_string.ljust(2, '0'),
219
- latitude_hemisphere]
220
- lat = lat_fields.zip(units).map{ |f| f.join }.join
221
-
222
- long_fields = ["%02d" % longitude_degrees,
223
- "%02d" % longitude_minutes,
224
- longitude_decimal_minutes_as_string.ljust(2, '0'),
225
- longitude_hemisphere]
226
- long = long_fields.zip(units).map{ |f| f.join }.join
227
-
228
- "#{lat}, #{long}"
229
- end
230
-
231
- private
232
-
233
- def to_float(degrees, minutes, decimal_minutes, decimal_minutes_width, hemisphere)
234
- return nil if degrees.nil? and minutes.nil? and decimal_minutes.nil?
235
- degrees ||= 0
236
- minutes ||= 0
237
- decimal_minutes ||= 0
238
-
239
- f = degrees.to_f
240
- f = f + (minutes.to_f + decimal_minutes.to_f / 10 ** decimal_minutes_width) / 60.0
241
- f = f * -1 if hemisphere == 'S' or hemisphere == 'W'
242
- f
243
- end
244
-
245
- # If some of the fields are empty, set them to zero. This is to speed up data entry.
246
- # If all the fields are empty, leave them empty.
247
- def set_empty_values
248
- unless latitude_degrees.blank? and latitude_minutes.blank? and latitude_decimal_minutes.blank?
249
- self.latitude_degrees = 0 if latitude_degrees.blank?
250
- self.latitude_minutes = 0 if latitude_minutes.blank?
251
- self.latitude_decimal_minutes = 0 if latitude_decimal_minutes.blank?
252
- end
253
-
254
- unless longitude_degrees.blank? and longitude_minutes.blank? and longitude_decimal_minutes.blank?
255
- self.longitude_degrees = 0 if longitude_degrees.blank?
256
- self.longitude_minutes = 0 if longitude_minutes.blank?
257
- self.longitude_decimal_minutes = 0 if longitude_decimal_minutes.blank?
258
- end
259
- end
260
- end
261
-
262
- end
263
- end
264
- end
265
-
266
- ActiveRecord::Base.send :include, AirBlade::GeoTools::Location
@@ -1,75 +0,0 @@
1
- module AirBlade
2
- module GeoTools
3
- module Validations
4
-
5
- # Sames as validates_numericality_of but additionally supports :for option
6
- # which lets you attach an error to a different attribute.
7
- def validates_inclusion_of_for(*attr_names)
8
- configuration = { :on => :save }
9
- configuration.update(attr_names.extract_options!)
10
-
11
- enum = configuration[:in] || configuration[:within]
12
-
13
- raise(ArgumentError, "An object with the method include? is required must be supplied as the :in option of the configuration hash") unless enum.respond_to?(:include?)
14
-
15
- validates_each(attr_names, configuration) do |record, attr_name, value|
16
- unless enum.include?(value)
17
- attr_for = configuration[:for] || attr_name
18
- record.errors.add(attr_for, :inclusion, :default => configuration[:message], :value => value)
19
- end
20
- end
21
- end
22
-
23
- # Sames as validates_numericality_of but additionally supports :for option
24
- # which lets you attach an error to a different attribute.
25
- def validates_numericality_of_for(*attr_names)
26
- configuration = { :on => :save, :only_integer => false, :allow_nil => false }
27
- configuration.update(attr_names.extract_options!)
28
-
29
- numericality_options = ActiveRecord::Validations::ClassMethods::ALL_NUMERICALITY_CHECKS.keys & configuration.keys
30
-
31
- (numericality_options - [ :odd, :even ]).each do |option|
32
- raise ArgumentError, ":#{option} must be a number" unless configuration[option].is_a?(Numeric)
33
- end
34
-
35
- validates_each(attr_names,configuration) do |record, attr_name, value|
36
- raw_value = record.send("#{attr_name}_before_type_cast") || value
37
-
38
- next if configuration[:allow_nil] and raw_value.nil?
39
-
40
- attr_for = configuration[:for] || attr_name
41
-
42
- if configuration[:only_integer]
43
- unless raw_value.to_s =~ /\A[+-]?\d+\Z/
44
- record.errors.add(attr_for, :not_a_number, :value => raw_value, :default => configuration[:message])
45
- next
46
- end
47
- raw_value = raw_value.to_i
48
- else
49
- begin
50
- raw_value = Kernel.Float(raw_value)
51
- rescue ArgumentError, TypeError
52
- record.errors.add(attr_for, :not_a_number, :value => raw_value, :default => configuration[:message])
53
- next
54
- end
55
- end
56
-
57
- numericality_options.each do |option|
58
- case option
59
- when :odd, :even
60
- unless raw_value.to_i.method( ActiveRecord::Validations::ClassMethods::ALL_NUMERICALITY_CHECKS[option])[]
61
- record.errors.add(attr_for, option, :value => raw_value, :default => configuration[:message])
62
- end
63
- else
64
- record.errors.add(attr_for, option, :default => configuration[:message], :value => raw_value, :count => configuration[option]) unless raw_value.method( ActiveRecord::Validations::ClassMethods::ALL_NUMERICALITY_CHECKS[option])[configuration[option]]
65
- end
66
- end
67
- end
68
- end
69
-
70
- end
71
- end
72
- end
73
-
74
-
75
- ActiveRecord::Base.send :extend, AirBlade::GeoTools::Validations
@@ -1,4 +0,0 @@
1
- # desc "Explaining what the task does"
2
- # task :geo_tools do
3
- # # Task goes here
4
- # end
@@ -1 +0,0 @@
1
- # Uninstall hook code here