geo_tools 1.0.1 → 1.1.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.
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
@@ -0,0 +1 @@
1
+ 1.9.3-p0
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'http://rubygems.org'
2
+ gemspec
data/README.md CHANGED
@@ -1,6 +1,5 @@
1
1
  # GeoTools
2
2
 
3
-
4
3
  You have lots of plugin choices if you want to geocode North American addresses, or find all the locations near somewhere. But few help you with forms and validation.
5
4
 
6
5
  This plugin does four things:
@@ -11,49 +10,56 @@ This plugin does four things:
11
10
  * Gives you a `within` named scope to find all lcoations within a given bounding box, such as you would have on a Google map.
12
11
 
13
12
 
13
+ ## Compatibility
14
+
15
+ Works on Ruby 1.8+ and 1.9+.
16
+
17
+ Designed for Rails 2.3.
18
+
19
+
14
20
  ## Assumptions
15
21
 
16
22
  * Any model that acts_as_location has integers defined for each component of the latitude and longitude:
17
23
 
18
- # In your model's migration's self.up method:
19
- create_table :thingies do |t|
20
- # Your model's various fields.
21
- t.string :name
22
- t.timestamps
23
- ...
24
-
25
- # Stuff GeoTools needs:
26
- t.integer :latitude_degrees, :latitude_minutes, :latitude_decimal_minutes, :latitude_decimal_minutes_width
27
- t.string :latitude_hemisphere
28
- t.integer :longitude_degrees, :longitude_minutes, :longitude_decimal_minutes, :longitude_decimal_minutes_width
29
- t.string :longitude_hemisphere
30
- end
24
+ # In your model's migration's self.up method:
25
+ create_table :thingies do |t|
26
+ # Your model's various fields.
27
+ t.string :name
28
+ t.timestamps
29
+ ...
30
+
31
+ # Stuff GeoTools needs:
32
+ t.integer :latitude_degrees, :latitude_minutes, :latitude_decimal_minutes, :latitude_decimal_minutes_width
33
+ t.string :latitude_hemisphere
34
+ t.integer :longitude_degrees, :longitude_minutes, :longitude_decimal_minutes, :longitude_decimal_minutes_width
35
+ t.string :longitude_hemisphere
36
+ end
31
37
 
32
- Storing the components separately like this avoids the round-trip rounding errors you get when using floating point numbers. If you need a floating point representation in the database, for example to use a mapping plugin, simply add an after_update callback to your model to write the float value to the database.
38
+ Storing the components separately like this avoids the round-trip rounding errors you get when using floating point numbers. If you need a floating point representation in the database, for example to use a mapping plugin, simply add an after_update callback to your model to write the float value to the database.
33
39
 
34
40
  * A latitude should be entered on a form like this:
35
41
 
36
- xx <degree symbol> yy <decimal point> zz h
42
+ xx <degree symbol> yy <decimal point> zz h
37
43
 
38
- where:
44
+ where:
39
45
 
40
- xx is degrees (0 <= integer <= 90; maximum length of 2 digits)
41
- yy is minutes (0 <= integer <= 59; maximum length of 2 digits; optional; defaults to 0)
42
- zz is decimal-minutes (0 <= integer <= 99; maximum length of 2 digits; optional; defaults to 0)
43
- h is hemisphere ('N' or 'S')
46
+ xx is degrees (0 <= integer <= 90; maximum length of 2 digits)
47
+ yy is minutes (0 <= integer <= 59; maximum length of 2 digits; optional; defaults to 0)
48
+ zz is decimal-minutes (0 <= integer <= 99; maximum length of 2 digits; optional; defaults to 0)
49
+ h is hemisphere ('N' or 'S')
44
50
 
45
- Note with decimal minutes 2, 20 and 200000 are equivalent. This is because 3.2, 3.20 and 3.200000 are equivalent.
51
+ Note with decimal minutes 2, 20 and 200000 are equivalent. This is because 3.2, 3.20 and 3.200000 are equivalent.
46
52
 
47
53
  * Similarly, a longitude should be entered on a form like this:
48
54
 
49
- xxx <degree symbol> yy <decimal point> zz h
55
+ xxx <degree symbol> yy <decimal point> zz h
50
56
 
51
- where:
57
+ where:
52
58
 
53
- xxx is degrees (0 <= integer <= 180; maximum length of 3 digits)
54
- yy is minutes (0 <= integer <= 59; maximum length of 2 digits; optional; defaults to 0)
55
- zz is decimal-minutes (0 <= integer <= 99; maximum length of 2 digits; optional; defaults to 0)
56
- h is hemisphere ('E' or 'W')
59
+ xxx is degrees (0 <= integer <= 180; maximum length of 3 digits)
60
+ yy is minutes (0 <= integer <= 59; maximum length of 2 digits; optional; defaults to 0)
61
+ zz is decimal-minutes (0 <= integer <= 99; maximum length of 2 digits; optional; defaults to 0)
62
+ h is hemisphere ('E' or 'W')
57
63
 
58
64
 
59
65
  ## Example
@@ -87,20 +93,14 @@ Here's an example script/console session:
87
93
  -12.576
88
94
 
89
95
 
90
- ## To Do
96
+ ## Someday / Maybe
91
97
 
92
- * Get tests to run transactionally so we don't have to clean out database in every single #setup method.
98
+ * Refactor the multiple string columns into a single string column (per lat. and per lng.), and use virtual attributes to map the single db field back and forth to multiple form fields. Add a float to 'cache' the lat/lng values. This will simplify the code significantly.
93
99
  * Add a validation for the overall latitude and longitude values (to catch for example 90°00.01′N).
94
100
  * Use `method` in the form helpers so user can give database columns different names (e.g. my_lat_degrees, etc).
95
101
  See the way Paperclip allows different attachment names.
96
102
  * DRY up form helper methods.
97
103
  * DRY up location.rb.
98
- * Investigate implementing with ActiveRecord's multiparameter assignment.
99
-
100
-
101
- ## Feedback
102
-
103
- Yes please! --> boss@airbladesoftware.com
104
104
 
105
105
 
106
106
  ## Intellectual Property
data/Rakefile CHANGED
@@ -1,37 +1 @@
1
- require 'rake'
2
- require 'rake/testtask'
3
- require 'rake/rdoctask'
4
-
5
- desc 'Default: run unit tests.'
6
- task :default => :test
7
-
8
- desc 'Test the geo_tools plugin.'
9
- Rake::TestTask.new(:test) do |t|
10
- t.libs << 'lib'
11
- t.libs << 'test'
12
- t.pattern = 'test/**/*_test.rb'
13
- t.verbose = true
14
- end
15
-
16
- desc 'Generate documentation for the geo_tools plugin.'
17
- Rake::RDocTask.new(:rdoc) do |rdoc|
18
- rdoc.rdoc_dir = 'rdoc'
19
- rdoc.title = 'GeoTools'
20
- rdoc.options << '--line-numbers' << '--inline-source'
21
- rdoc.rdoc_files.include('README')
22
- rdoc.rdoc_files.include('lib/**/*.rb')
23
- end
24
-
25
- begin
26
- require 'jeweler'
27
- Jeweler::Tasks.new do |gemspec|
28
- gemspec.name = 'geo_tools'
29
- gemspec.summary = 'View helpers, validations, and named scopes for locations.'
30
- gemspec.email = 'boss@airbladesoftware.com'
31
- gemspec.homepage = 'http://github.com/airblade/geo_tools'
32
- gemspec.authors = ['Andy Stewart']
33
- end
34
- Jeweler::GemcutterTasks.new
35
- rescue LoadError
36
- puts 'Jeweler not available. Install it with: gem install jeweler'
37
- end
1
+ require 'bundler/gem_tasks'
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "geo_tools/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'geo_tools'
7
+ s.version = GeoTools::VERSION
8
+ s.authors = ['Andy Stewart']
9
+ s.email = ['boss@airbladesoftware.com']
10
+ s.homepage = 'https://github.com/airblade/geo_tools'
11
+ s.summary = 'Makes using latitudes and longitudes on forms easier.'
12
+ s.description = s.summary
13
+
14
+ s.rubyforge_project = 'geo_tools'
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_runtime_dependency 'rails', '~> 2.3'
22
+
23
+ s.add_development_dependency 'sqlite3-ruby'
24
+ s.add_development_dependency 'shoulda-context', '~> 1.0.0'
25
+ end
@@ -1,3 +1,4 @@
1
- require File.dirname(__FILE__) + '/air_blade/geo_tools/validations'
2
- require File.dirname(__FILE__) + '/air_blade/geo_tools/location'
3
- require File.dirname(__FILE__) + '/air_blade/geo_tools/form_helpers'
1
+ require 'geo_tools/validations'
2
+ require 'geo_tools/location'
3
+ require 'geo_tools/form_helpers'
4
+ require 'geo_tools/version'
@@ -0,0 +1,103 @@
1
+ # encoding: utf-8
2
+ module GeoTools
3
+ module FormHelpers
4
+
5
+ # Options:
6
+ # :latitude
7
+ # :degrees
8
+ # :symbol
9
+ # :minutes
10
+ # :symbol
11
+ # :decimal_minutes
12
+ # :symbol
13
+ # :maxlength
14
+ #
15
+ # Assumes the latitude field is called 'latitude'.
16
+ #
17
+ # The 'method' argument is for consistency with other field helpers. We don't use it
18
+ # when using the normal Rails form builder.
19
+ #
20
+ # 1/100th of a minute of latitude (or equitorial longitude) is approximately 20m.
21
+ def latitude_field(method, options = {})
22
+ opts = {
23
+ :degrees => { :symbol => '&deg;' },
24
+ :minutes => { :symbol => '.' },
25
+ :decimal_minutes => { :symbol => '&prime;', :maxlength => 2 },
26
+ }
27
+ lat_options = options.delete :latitude
28
+ opts.merge! lat_options if lat_options
29
+
30
+ output = []
31
+
32
+ # Degrees
33
+ width = 2
34
+ output << text_field("latitude_degrees",
35
+ options.merge(:maxlength => width,
36
+ :value => "%0#{width}d" % (@object.send("latitude_degrees") || 0)))
37
+ output << opts[:degrees][:symbol]
38
+
39
+ # Minutes
40
+ width = 2
41
+ output << text_field("latitude_minutes",
42
+ options.merge(:maxlength => width,
43
+ :value => "%0#{width}d" % (@object.send("latitude_minutes") || 0)))
44
+ output << opts[:minutes][:symbol]
45
+
46
+ # Decimal minutes
47
+ width = opts[:decimal_minutes][:maxlength]
48
+ output << text_field("latitude_decimal_minutes",
49
+ options.merge(:maxlength => width,
50
+ :value => @object.send("latitude_decimal_minutes_as_string").ljust(width, '0')))
51
+ output << opts[:decimal_minutes][:symbol]
52
+
53
+ # Hemisphere.
54
+ # Hmm, we pass the options in the html_options position.
55
+ output << select("latitude_hemisphere", %w( N S ), {}, options)
56
+
57
+ output.join "\n"
58
+ end
59
+
60
+ def longitude_field(method, options = {})
61
+ opts = {
62
+ :degrees => { :symbol => '&deg;' },
63
+ :minutes => { :symbol => '.' },
64
+ :decimal_minutes => { :symbol => '&prime;', :maxlength => 2 },
65
+ }
66
+ long_options = options.delete :longitude
67
+ opts.merge! long_options if long_options
68
+
69
+ output = []
70
+
71
+ # Degrees
72
+ width = 3
73
+ output << text_field("longitude_degrees",
74
+ options.merge(:maxlength => width,
75
+ :value => "%0#{width}d" % (@object.send("longitude_degrees") || 0)))
76
+ output << opts[:degrees][:symbol]
77
+
78
+ # Minutes
79
+ width = 2
80
+ output << text_field("longitude_minutes",
81
+ options.merge(:maxlength => width,
82
+ :value => "%0#{width}d" % (@object.send("longitude_minutes") || 0)))
83
+ output << opts[:minutes][:symbol]
84
+
85
+ # Decimal minutes
86
+ width = opts[:decimal_minutes][:maxlength]
87
+ output << text_field("longitude_decimal_minutes",
88
+ options.merge(:maxlength => width,
89
+ :value => @object.send("longitude_decimal_minutes_as_string").ljust(width, '0')))
90
+ output << opts[:decimal_minutes][:symbol]
91
+
92
+ # Hemisphere.
93
+ # Hmm, we pass the options in the html_options position.
94
+ output << select("longitude_hemisphere", %w( E W ), {}, options)
95
+
96
+ output.join "\n"
97
+ end
98
+
99
+ end
100
+ end
101
+
102
+
103
+ ActionView::Helpers::FormBuilder.send :include, GeoTools::FormHelpers
@@ -0,0 +1,259 @@
1
+ # encoding: utf-8
2
+ module GeoTools
3
+ module Location
4
+
5
+ def self.included(base)
6
+ base.extend ClassMethods
7
+ end
8
+
9
+ module ClassMethods
10
+ def acts_as_location
11
+ include InstanceMethodsOnActivation
12
+
13
+ validates_numericality_of_for :latitude_degrees,
14
+ :only_integer => true,
15
+ :greater_than_or_equal_to => 0,
16
+ :less_than_or_equal_to => 90,
17
+ :message => 'Degrees are invalid',
18
+ :for => :latitude
19
+
20
+ validates_numericality_of_for :latitude_minutes,
21
+ :only_integer => true,
22
+ :greater_than_or_equal_to => 0,
23
+ :less_than => 60,
24
+ :message => 'Minutes are invalid',
25
+ :for => :latitude
26
+
27
+ validates_numericality_of_for :latitude_decimal_minutes,
28
+ :only_integer => true,
29
+ :greater_than_or_equal_to => 0,
30
+ :message => 'Decimal minutes are invalid',
31
+ :for => :latitude
32
+
33
+ validates_numericality_of_for :latitude_decimal_minutes_width,
34
+ :only_integer => true,
35
+ :greater_than_or_equal_to => 0,
36
+ :for => :latitude
37
+
38
+ validates_inclusion_of_for :latitude_hemisphere,
39
+ :in => %w( N S ),
40
+ :message => 'Hemisphere is invalid',
41
+ :for => :latitude
42
+
43
+ validates_numericality_of_for :longitude_degrees,
44
+ :only_integer => true,
45
+ :greater_than_or_equal_to => 0,
46
+ :less_than_or_equal_to => 180,
47
+ :message => 'Degrees are invalid',
48
+ :for => :longitude
49
+
50
+ validates_numericality_of_for :longitude_minutes,
51
+ :only_integer => true,
52
+ :greater_than_or_equal_to => 0,
53
+ :less_than => 60,
54
+ :message => 'Minutes are invalid',
55
+ :for => :longitude
56
+
57
+ validates_numericality_of_for :longitude_decimal_minutes,
58
+ :only_integer => true,
59
+ :greater_than_or_equal_to => 0,
60
+ :message => 'Decimal minutes are invalid',
61
+ :for => :longitude
62
+
63
+ validates_numericality_of_for :longitude_decimal_minutes_width,
64
+ :only_integer => true,
65
+ :greater_than_or_equal_to => 0,
66
+ :for => :longitude
67
+
68
+ validates_inclusion_of_for :longitude_hemisphere,
69
+ :in => %w( E W ),
70
+ :message => 'Hemisphere is invalid',
71
+ :for => :longitude
72
+
73
+ before_validation :set_empty_values
74
+
75
+ # Returns all locations within the given bounding box, to an accuracy of 1 minute.
76
+ #
77
+ # This is useful for finding all locations within the area covered by a Google map.
78
+ #
79
+ # The parameters should be positive/negative floats.
80
+ named_scope :within, lambda { |sw_lat, sw_lng, ne_lat, ne_lng|
81
+ sw_lat_degs = sw_lat.to_i.abs
82
+ sw_lat_mins = ((sw_lat - sw_lat.to_i) * 60.0).round.abs
83
+ ne_lat_degs = ne_lat.to_i.abs
84
+ ne_lat_mins = ((ne_lat - ne_lat.to_i) * 60.0).round.abs
85
+
86
+ sw_lng_degs = sw_lng.to_i.abs
87
+ sw_lng_mins = ((sw_lng - sw_lng.to_i) * 60.0).round.abs
88
+ ne_lng_degs = ne_lng.to_i.abs
89
+ ne_lng_mins = ((ne_lng - ne_lng.to_i) * 60.0).round.abs
90
+
91
+ # Latitude conditions.
92
+ if sw_lat > 0 && ne_lat > 0 # northern hemisphere
93
+ condition_lat_h = 'latitude_hemisphere = "N"'
94
+ condition_lat_sw = ["(latitude_degrees > ?) OR (latitude_degrees = ? AND latitude_minutes >= ?)", sw_lat_degs, sw_lat_degs, sw_lat_mins]
95
+ condition_lat_ne = ["(latitude_degrees < ?) OR (latitude_degrees = ? AND latitude_minutes <= ?)", ne_lat_degs, ne_lat_degs, ne_lat_mins]
96
+ condition_lat = merge_conditions condition_lat_h, condition_lat_sw, condition_lat_ne
97
+
98
+ elsif sw_lat < 0 && ne_lat < 0 # southern hemisphere
99
+ condition_lat_h = 'latitude_hemisphere = "S"'
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 # straddles equator
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_s = merge_conditions condition_lat_h, condition_lat_sw
108
+
109
+ condition_lat_h = 'latitude_hemisphere = "N"'
110
+ condition_lat_ne = ["(latitude_degrees < ?) OR (latitude_degrees = ? AND latitude_minutes <= ?)", ne_lat_degs, ne_lat_degs, ne_lat_mins]
111
+ condition_lat_n = merge_conditions condition_lat_h, condition_lat_ne
112
+
113
+ condition_lat = merge_or_conditions condition_lat_s, condition_lat_n
114
+ end
115
+
116
+ # Longitude conditions.
117
+ if sw_lng > 0 && ne_lng > 0 # eastern hemisphere
118
+ condition_lng_h = 'longitude_hemisphere = "E"'
119
+ condition_lng_sw = ["(longitude_degrees > ?) OR (longitude_degrees = ? AND longitude_minutes >= ?)", sw_lng_degs, sw_lng_degs, sw_lng_mins]
120
+ condition_lng_ne = ["(longitude_degrees < ?) OR (longitude_degrees = ? AND longitude_minutes <= ?)", ne_lng_degs, ne_lng_degs, ne_lng_mins]
121
+ condition_lng = merge_conditions condition_lng_h, condition_lng_sw, condition_lng_ne
122
+
123
+ elsif sw_lng < 0 && ne_lng < 0 # western hemisphere
124
+ condition_lng_h = 'longitude_hemisphere = "W"'
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 # straddles prime meridian
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_w = merge_conditions condition_lng_h, condition_lng_sw
133
+
134
+ condition_lng_h = 'longitude_hemisphere = "E"'
135
+ condition_lng_ne = ["(longitude_degrees < ?) OR (longitude_degrees = ? AND longitude_minutes <= ?)", ne_lng_degs, ne_lng_degs, ne_lng_mins]
136
+ condition_lng_e = merge_conditions condition_lng_h, condition_lng_ne
137
+
138
+ condition_lng = merge_or_conditions condition_lng_w, condition_lng_e
139
+ end
140
+
141
+ # Combined latitude and longitude conditions.
142
+ {:conditions => merge_conditions(condition_lat, condition_lng)}
143
+ }
144
+
145
+ private
146
+
147
+ # Merges conditions so that the result is a valid +condition+.
148
+ # Adapted from ActiveRecord::Base#merge_conditions.
149
+ def merge_or_conditions(*conditions)
150
+ segments = []
151
+
152
+ conditions.each do |condition|
153
+ unless condition.blank?
154
+ sql = sanitize_sql(condition)
155
+ segments << sql unless sql.blank?
156
+ end
157
+ end
158
+
159
+ "(#{segments.join(') OR (')})" unless segments.empty?
160
+ end
161
+
162
+ end # def acts_as_location
163
+ end # module ClassMethods
164
+
165
+
166
+ module InstanceMethodsOnActivation
167
+ def latitude_decimal_minutes=(value)
168
+ unless value.nil?
169
+ width = value.to_s.length
170
+ value = value.to_i
171
+
172
+ write_attribute :latitude_decimal_minutes, value
173
+ write_attribute :latitude_decimal_minutes_width, width
174
+ end
175
+ end
176
+
177
+ def latitude_decimal_minutes_as_string
178
+ "%0#{latitude_decimal_minutes_width}d" % (latitude_decimal_minutes || 0)
179
+ end
180
+
181
+ def longitude_decimal_minutes=(value)
182
+ unless value.nil?
183
+ width = value.to_s.length
184
+ value = value.to_i
185
+
186
+ write_attribute :longitude_decimal_minutes, value
187
+ write_attribute :longitude_decimal_minutes_width, width
188
+ end
189
+ end
190
+
191
+ def longitude_decimal_minutes_as_string
192
+ "%0#{longitude_decimal_minutes_width}d" % (longitude_decimal_minutes || 0)
193
+ end
194
+
195
+ def latitude
196
+ to_float latitude_degrees, latitude_minutes, latitude_decimal_minutes,
197
+ latitude_decimal_minutes_width, latitude_hemisphere
198
+ end
199
+
200
+ def longitude
201
+ to_float longitude_degrees, longitude_minutes, longitude_decimal_minutes,
202
+ longitude_decimal_minutes_width, longitude_hemisphere
203
+ end
204
+
205
+ def to_s
206
+ # Unicode degree symbol, full stop, Unicode minute symbol.
207
+ units = [ "\xc2\xb0", '.', "\xe2\x80\xb2" ]
208
+
209
+ lat_fields = ["%02d" % (latitude_degrees || 0),
210
+ "%02d" % (latitude_minutes || 0),
211
+ latitude_decimal_minutes_as_string.ljust(2, '0'),
212
+ latitude_hemisphere]
213
+ lat = lat_fields.zip(units).map{ |f| f.join }.join
214
+
215
+ long_fields = ["%02d" % (longitude_degrees || 0),
216
+ "%02d" % (longitude_minutes || 0),
217
+ longitude_decimal_minutes_as_string.ljust(2, '0'),
218
+ longitude_hemisphere]
219
+ long = long_fields.zip(units).map{ |f| f.join }.join
220
+
221
+ "#{lat}, #{long}"
222
+ end
223
+
224
+ private
225
+
226
+ def to_float(degrees, minutes, decimal_minutes, decimal_minutes_width, hemisphere)
227
+ return nil if degrees.nil? and minutes.nil? and decimal_minutes.nil?
228
+ degrees ||= 0
229
+ minutes ||= 0
230
+ decimal_minutes ||= 0
231
+
232
+ f = degrees.to_f
233
+ f = f + (minutes.to_f + decimal_minutes.to_f / 10 ** decimal_minutes_width) / 60.0
234
+ f = f * -1 if hemisphere == 'S' or hemisphere == 'W'
235
+ f
236
+ end
237
+
238
+ # If some of the fields are empty, set them to zero. This is to speed up data entry.
239
+ # If all the fields are empty, leave them empty.
240
+ def set_empty_values
241
+ unless latitude_degrees.blank? and latitude_minutes.blank? and latitude_decimal_minutes.blank?
242
+ self.latitude_degrees = 0 if latitude_degrees.blank?
243
+ self.latitude_minutes = 0 if latitude_minutes.blank?
244
+ self.latitude_decimal_minutes = 0 if latitude_decimal_minutes.blank?
245
+ end
246
+
247
+ unless longitude_degrees.blank? and longitude_minutes.blank? and longitude_decimal_minutes.blank?
248
+ self.longitude_degrees = 0 if longitude_degrees.blank?
249
+ self.longitude_minutes = 0 if longitude_minutes.blank?
250
+ self.longitude_decimal_minutes = 0 if longitude_decimal_minutes.blank?
251
+ end
252
+ end
253
+ end # InstanceMethodsOnActivation
254
+
255
+ end # module Location
256
+ end # module GeoTools
257
+
258
+
259
+ ActiveRecord::Base.send :include, GeoTools::Location