geo_calc 0.5.3 → 0.6.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.
data/Changelog.textile ADDED
@@ -0,0 +1,4 @@
1
+ h3. May 24, 2011
2
+
3
+ * Refactored main GeoCalc (geo calculations) module into smaller parts
4
+ * Using autoload to load most modules
data/README.textile CHANGED
@@ -16,6 +16,13 @@ From command line, run bundler
16
16
 
17
17
  @$ bundle@
18
18
 
19
+ h2. Objetive
20
+
21
+ The objective of this library is to act as a base for other geo libraries.
22
+
23
+ Please take a look at "geo vectors":https://github.com/kristianmandrup/geo_vectors which builds on _geo calc_ and provides various vector operations on geo points.
24
+ I also have plans to include _geo calc_ in "mongoid geo":https://github.com/kristianmandrup/mongoid_geo, the geo extension library for _Mongoid 2_.
25
+
19
26
  h2. Quick start (Usage)
20
27
 
21
28
  First define the points on the globe you want to work with.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.5.3
1
+ 0.6.0
data/geo_calc.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{geo_calc}
8
- s.version = "0.5.2"
8
+ s.version = "0.6.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = [%q{Kristian Mandrup}]
12
- s.date = %q{2011-05-13}
12
+ s.date = %q{2011-05-23}
13
13
  s.description = %q{Geo calculations in ruby and javascript}
14
14
  s.email = %q{kmandrup@gmail.com}
15
15
  s.extra_rdoc_files = [
@@ -19,6 +19,7 @@ Gem::Specification.new do |s|
19
19
  s.files = [
20
20
  ".document",
21
21
  ".rspec",
22
+ "Changelog.textile",
22
23
  "Gemfile",
23
24
  "LICENSE.txt",
24
25
  "README.textile",
@@ -26,49 +27,56 @@ Gem::Specification.new do |s|
26
27
  "VERSION",
27
28
  "geo_calc.gemspec",
28
29
  "lib/geo_calc.rb",
29
- "lib/geo_calc/calculations.rb",
30
+ "lib/geo_calc/calc.rb",
31
+ "lib/geo_calc/calc/bearing.rb",
32
+ "lib/geo_calc/calc/destination.rb",
33
+ "lib/geo_calc/calc/distance.rb",
34
+ "lib/geo_calc/calc/intersection.rb",
35
+ "lib/geo_calc/calc/midpoint.rb",
36
+ "lib/geo_calc/calc/rhumb.rb",
30
37
  "lib/geo_calc/core_ext.rb",
31
38
  "lib/geo_calc/geo.rb",
32
39
  "lib/geo_calc/geo_point.rb",
33
- "lib/geo_calc/js/geo_calc.js",
40
+ "lib/geo_calc/pretty_print.rb",
34
41
  "spec/geo_calc/calculations_spec.rb",
42
+ "spec/geo_calc/core_ext/array_ext_spec.rb",
43
+ "spec/geo_calc/core_ext/hash_ext_spec.rb",
44
+ "spec/geo_calc/core_ext/numeric_geo_ext_spec.rb",
45
+ "spec/geo_calc/core_ext/string_ext_spec.rb",
35
46
  "spec/geo_calc/core_ext_spec.rb",
36
47
  "spec/geo_calc/geo_point_spec.rb",
37
48
  "spec/geo_calc/geo_spec.rb",
38
- "spec/spec_helper.rb"
49
+ "spec/spec_helper.rb",
50
+ "vendor/assets/javascript/geo_calc.js"
39
51
  ]
40
52
  s.homepage = %q{http://github.com/kristianmandrup/geo_calc}
41
53
  s.licenses = [%q{MIT}]
42
54
  s.require_paths = [%q{lib}]
43
- s.rubygems_version = %q{1.8.0}
55
+ s.rubygems_version = %q{1.8.3}
44
56
  s.summary = %q{Geo calculation library}
45
- s.test_files = [
46
- "spec/geo_calc/calculations_spec.rb",
47
- "spec/geo_calc/core_ext_spec.rb",
48
- "spec/geo_calc/geo_point_spec.rb",
49
- "spec/geo_calc/geo_spec.rb",
50
- "spec/spec_helper.rb"
51
- ]
52
57
 
53
58
  if s.respond_to? :specification_version then
54
59
  s.specification_version = 3
55
60
 
56
61
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
57
62
  s.add_development_dependency(%q<rspec>, [">= 2.5.0"])
58
- s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
59
- s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
63
+ s.add_development_dependency(%q<bundler>, [">= 1"])
64
+ s.add_development_dependency(%q<jeweler>, [">= 1.5.2"])
60
65
  s.add_development_dependency(%q<rcov>, [">= 0"])
66
+ s.add_development_dependency(%q<rake>, [">= 0.9"])
61
67
  else
62
68
  s.add_dependency(%q<rspec>, [">= 2.5.0"])
63
- s.add_dependency(%q<bundler>, ["~> 1.0.0"])
64
- s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
69
+ s.add_dependency(%q<bundler>, [">= 1"])
70
+ s.add_dependency(%q<jeweler>, [">= 1.5.2"])
65
71
  s.add_dependency(%q<rcov>, [">= 0"])
72
+ s.add_dependency(%q<rake>, [">= 0.9"])
66
73
  end
67
74
  else
68
75
  s.add_dependency(%q<rspec>, [">= 2.5.0"])
69
- s.add_dependency(%q<bundler>, ["~> 1.0.0"])
70
- s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
76
+ s.add_dependency(%q<bundler>, [">= 1"])
77
+ s.add_dependency(%q<jeweler>, [">= 1.5.2"])
71
78
  s.add_dependency(%q<rcov>, [">= 0"])
79
+ s.add_dependency(%q<rake>, [">= 0.9"])
72
80
  end
73
81
  end
74
82
 
@@ -0,0 +1,51 @@
1
+ module GeoCalc::Calc
2
+ module Bearing
3
+ def bearing_to point
4
+ GeoCalc::Calc::Bearing.bearing_to self, point
5
+ end
6
+
7
+ # Returns the (initial) bearing from this point to the supplied point, in degrees
8
+ # see http:#williams.best.vwh.net/avform.htm#Crs
9
+ #
10
+ # - Point point: Latitude/longitude of destination point
11
+ #
12
+ # Returns - Numeric: Initial bearing in degrees from North
13
+
14
+ def self.bearing_to base_point, point
15
+ lat1 = base_point.lat.to_rad
16
+ lat2 = point.lat.to_rad
17
+ dlon = (point.lon - base_point.lon).to_rad
18
+
19
+ y = Math.sin(dlon) * Math.cos(lat2)
20
+ x = Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(dlon)
21
+ bearing = Math.atan2(y, x)
22
+
23
+ (bearing.to_deg + 360) % 360
24
+ end
25
+
26
+ def final_bearing_to point
27
+ GeoCalc::Calc::Bearing.final_bearing_to self, point
28
+ end
29
+
30
+ # Returns final bearing arriving at supplied destination point from this point; the final bearing
31
+ # will differ from the initial bearing by varying degrees according to distance and latitude
32
+ #
33
+ # - GeoPoint point: Latitude/longitude of destination point
34
+ #
35
+ # Returns Numeric: Final bearing in degrees from North
36
+
37
+ def self.final_bearing_to base_point, point
38
+ # get initial bearing from supplied point back to this point...
39
+ lat1 = point.lat.to_rad
40
+ lat2 = base_point.lat.to_rad
41
+ dlon = (base_point.lon - point.lon).to_rad
42
+
43
+ y = Math.sin(dlon) * Math.cos(lat2)
44
+ x = Math.cos(lat1)*Math.sin(lat2) - Math.sin(lat1)*Math.cos(lat2)*Math.cos(dlon)
45
+ bearing = Math.atan2(y, x)
46
+
47
+ # ... & reverse it by adding 180°
48
+ (bearing.to_deg+180) % 360
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,30 @@
1
+ module GeoCalc::Calc
2
+ module Destination
3
+ def destination_point brng, dist
4
+ GeoCalc::Calc::Destination.destination_point self, brng, dist
5
+ end
6
+
7
+ # Returns the destination point from this point having travelled the given distance (in km) on the
8
+ # given initial bearing (bearing may vary before destination is reached)
9
+ #
10
+ # see http:#williams.best.vwh.net/avform.htm#LL
11
+ #
12
+ # - Numeric bearing: Initial bearing in degrees
13
+ # - Numeric dist: Distance in km
14
+ # Returns GeoPoint: Destination point
15
+
16
+ def self.destination_point base_point, brng, dist
17
+ dist = dist / base_point.radius # convert dist to angular distance in radians
18
+ brng = brng.to_rad
19
+ lat1 = base_point.lat.to_rad
20
+ lon1 = base_point.lon.to_rad
21
+
22
+ lat2 = Math.asin( Math.sin(lat1) * Math.cos(dist) + Math.cos(lat1) * Math.sin(dist) * Math.cos(brng) )
23
+ lon2 = lon1 + Math.atan2(Math.sin(brng) * Math.sin(dist) * Math.cos(lat1), Math.cos(dist) - Math.sin(lat1) * Math.sin(lat2))
24
+
25
+ lon2 = (lon2 + 3*Math::PI) % (2*Math::PI) - Math::PI # normalise to -180...+180
26
+
27
+ GeoPoint.new lat2.to_deg, lon2.to_deg
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,43 @@
1
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2
+ # Latitude/longitude spherical geodesy formulae & scripts (c) Chris Veness 2002-2010
3
+ # - www.movable-type.co.uk/scripts/latlong.html
4
+ #
5
+
6
+ module GeoCalc::Calc
7
+ module Distance
8
+ def distance_to point, precision = 4
9
+ GeoCalc::Calc::Distance.distance_to self, point, precision
10
+ end
11
+
12
+ # Returns the distance from this point to the supplied point, in km
13
+ # (using Haversine formula)
14
+ #
15
+ # from: Haversine formula - R. W. Sinnott, "Virtues of the Haversine",
16
+ # Sky and Telescope, vol 68, no 2, 1984
17
+ #
18
+ # GeoPoint point: Latitude/longitude of destination point
19
+ # - Numeric precision=4: number of significant digits to use for returned value
20
+ #
21
+ # Returns - Numeric distance in km between this point and destination point
22
+
23
+ def self.distance_to base_point, point, precision = 4
24
+ # default 4 sig figs reflects typical 0.3% accuracy of spherical model
25
+ precision ||= 4
26
+
27
+ lat1 = base_point.lat.to_rad
28
+ lon1 = base_point.lon.to_rad
29
+
30
+ lat2 = point.lat.to_rad
31
+ lon2 = point.lon.to_rad
32
+
33
+ dlat = lat2 - lat1
34
+ dlon = lon2 - lon1
35
+
36
+ a = Math.sin(dlat/2) * Math.sin(dlat/2) + Math.cos(lat1) * Math.cos(lat2) * Math.sin(dlon/2) * Math.sin(dlon/2)
37
+ c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a))
38
+ d = base_point.radius * c
39
+ d.round(precision)
40
+ end
41
+ end
42
+ end
43
+
@@ -0,0 +1,72 @@
1
+ module GeoCalc::Calc
2
+ module Intersection
3
+ def intersection brng1, p2, brng2
4
+ GeoCalc::Calc::Intersection.intersection self, brng1, p2, brng2
5
+ end
6
+
7
+ # Returns the point of intersection of two paths defined by point and bearing
8
+ #
9
+ # see http:#williams.best.vwh.net/avform.htm#Intersection
10
+ #
11
+ # @param {LatLon} p1: First point
12
+ # @param {Number} brng1: Initial bearing from first point
13
+ # @param {LatLon} p2: Second point
14
+ # @param {Number} brng2: Initial bearing from second point
15
+ # @returns {LatLon} Destination point (null if no unique intersection defined)
16
+
17
+ def self.intersection p1, brng1, p2, brng2
18
+ lat1 = p1.lat.to_rad
19
+ lon1 = p1.lon.to_rad
20
+
21
+ lat2 = p2.lat.to_rad
22
+ lon2 = p2.lon.to_rad
23
+
24
+ brng13 = brng1.to_rad
25
+ brng23 = brng2.to_rad
26
+
27
+ dlat = lat2-lat1
28
+ dlon = lon2-lon1;
29
+
30
+ dist12 = 2*Math.asin( Math.sqrt( Math.sin(dlat/2)*Math.sin(dlat/2) + Math.cos(lat1)*Math.cos(lat2)*Math.sin(dlon/2)*Math.sin(dlon/2) ) )
31
+ return nil if dist12 == 0
32
+
33
+ # initial/final bearings between points
34
+ brng_a = begin
35
+ Math.acos( ( Math.sin(lat2) - Math.sin(lat1)*Math.cos(dist12) ) / ( Math.sin(dist12)*Math.cos(lat1) ) )
36
+ rescue # protect against rounding
37
+ 0
38
+ end
39
+
40
+ brng_b = Math.acos( ( Math.sin(lat1) - Math.sin(lat2)*Math.cos(dist12) ) / ( Math.sin(dist12)*Math.cos(lat2) ) )
41
+
42
+ brng12, brng21 = if Math.sin(lon2-lon1) > 0
43
+ [brng_a, 2*Math::PI - brng_b]
44
+ else
45
+ [2*Math::PI - brng_a, brng_b]
46
+ end
47
+
48
+ alpha1 = (brng13 - brng12 + Math::PI) % (2*Math::PI) - Math::PI # angle 2-1-3
49
+ alpha2 = (brng21 - brng23 + Math::PI) % (2*Math::PI) - Math::PI # angle 1-2-3
50
+
51
+ return nil if (Math.sin(alpha1)==0 && Math.sin(alpha2)==0) # infinite intersections
52
+ return nil if (Math.sin(alpha1)*Math.sin(alpha2) < 0) # ambiguous intersection
53
+
54
+ # alpha1 = Math.abs(alpha1);
55
+ # alpha2 = Math.abs(alpha2);
56
+ # ... Ed Williams takes abs of alpha1/alpha2, but seems to break calculation?
57
+
58
+ alpha3 = Math.acos( -Math.cos(alpha1)*Math.cos(alpha2) + Math.sin(alpha1)*Math.sin(alpha2)*Math.cos(dist12) )
59
+
60
+ dist13 = Math.atan2( Math.sin(dist12)*Math.sin(alpha1)*Math.sin(alpha2), Math.cos(alpha2)+Math.cos(alpha1)*Math.cos(alpha3) )
61
+
62
+ lat3 = Math.asin( Math.sin(lat1)*Math.cos(dist13) + Math.cos(lat1)*Math.sin(dist13)*Math.cos(brng13) )
63
+
64
+ dlon13 = Math.atan2( Math.sin(brng13)*Math.sin(dist13)*Math.cos(lat1), Math.cos(dist13)-Math.sin(lat1)*Math.sin(lat3) )
65
+
66
+ lon3 = lon1 + dlon13;
67
+ lon3 = (lon3 + Math::PI) % (2*Math::PI) - Math::PI # normalise to -180..180º
68
+
69
+ GeoPoint.new lat3.to_deg, lon3.to_deg
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,29 @@
1
+ module GeoCalc::Calc
2
+ module Midpoint
3
+ def midpoint_to point
4
+ GeoCalc::Calc::Midpoint.midpoint_to self, point
5
+ end
6
+
7
+ # Returns the midpoint between this point and the supplied point.
8
+ # see http:#mathforum.org/library/drmath/view/51822.html for derivation
9
+ #
10
+ # - GeoPoint point: Latitude/longitude of destination point
11
+ # Returns GeoPoint: Midpoint between this point and the supplied point
12
+
13
+ def self.midpoint_to base_point, point
14
+ lat1 = base_point.lat.to_rad
15
+ lon1 = base_point.lon.to_rad;
16
+ lat2 = point.lat.to_rad
17
+ dlon = (point.lon - base_point.lon).to_rad
18
+
19
+ bx = Math.cos(lat2) * Math.cos(dlon)
20
+ by = Math.cos(lat2) * Math.sin(dlon)
21
+
22
+ lat3 = Math.atan2(Math.sin(lat1)+Math.sin(lat2), Math.sqrt( (Math.cos(lat1)+bx)*(Math.cos(lat1)+bx) + by*by) )
23
+
24
+ lon3 = lon1 + Math.atan2(by, Math.cos(lat1) + bx)
25
+
26
+ GeoPoint.new lat3.to_deg, lon3.to_deg
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,98 @@
1
+ module GeoCalc::Calc
2
+ module Rhumb
3
+ def rhumb_distance_to point
4
+ GeoCalc::Calc::Rhumb.rhumb_distance_to self, point
5
+ end
6
+
7
+ # Returns the distance from this point to the supplied point, in km, travelling along a rhumb line
8
+ #
9
+ # see http:#williams.best.vwh.net/avform.htm#Rhumb
10
+ #
11
+ # - GeoPoint point: Latitude/longitude of destination point
12
+ # Returns Numeric: Distance in km between this point and destination point
13
+
14
+ def self.rhumb_distance_to base_point, point
15
+ lat1 = base_point.lat.to_rad
16
+ lat2 = point.lat.to_rad
17
+
18
+ dlat = (point.lat - base_point.lat).to_rad
19
+ dlon = (point.lon - base_point.lon).abs.to_rad
20
+
21
+ dphi = Math.log(Math.tan(lat2/2 + Math::PI/4) / Math.tan(lat1/2 + Math::PI/4))
22
+
23
+ q = begin
24
+ dlat / dphi
25
+ rescue
26
+ Math.cos(lat1) # E-W line gives dPhi=0
27
+ end
28
+
29
+ # if dlon over 180° take shorter rhumb across 180° meridian:
30
+ dlon = 2*Math::PI - dlon if (dlon > Math::PI)
31
+
32
+ dist = Math.sqrt(dlat*dlat + q*q*dlon*dlon) * base_point.radius;
33
+
34
+ dist.round(4) # 4 sig figures reflects typical 0.3% accuracy of spherical model
35
+ end
36
+
37
+ def rhumb_bearing_to point
38
+ GeoCalc::Calc::Rhumb.rhumb_bearing_to self, point
39
+ end
40
+ # Returns the bearing from this point to the supplied point along a rhumb line, in degrees
41
+ #
42
+ # - GeoPoint point: Latitude/longitude of destination point
43
+ # Returns Numeric: Bearing in degrees from North
44
+
45
+ def self.rhumb_bearing_to base_point, point
46
+ lat1 = base_point.lat.to_rad
47
+ lat2 = point.lat.to_rad
48
+
49
+ dlon = (point.lon - base_point.lon).to_rad
50
+
51
+ dphi = Math.log(Math.tan(lat2/2+Math::PI/4) / Math.tan(lat1/2+Math::PI/4))
52
+ if dlon.abs > Math::PI
53
+ dlon = dlon>0 ? -(2*Math::PI-dlon) : (2*Math::PI+dlon);
54
+ end
55
+
56
+ brng = Math.atan2(dlon, dphi);
57
+
58
+ (brng.to_deg+360) % 360
59
+ end
60
+
61
+ def rhumb_destination_point brng, dist
62
+ GeoCalc::Calc::Rhumb.rhumb_destination_point self, brng, dist
63
+ end
64
+ # Returns the destination point from this point having travelled the given distance (in km) on the
65
+ # given bearing along a rhumb line
66
+ #
67
+ # @param {Number} brng: Bearing in degrees from North
68
+ # @param {Number} dist: Distance in km
69
+ # @returns {LatLon} Destination point
70
+
71
+ def self.rhumb_destination_point base_point, brng, dist
72
+ d = dist / base_point.radius # d = angular distance covered on earth's surface
73
+ lat1 = base_point.lat.to_rad
74
+ lon1 = base_point.lon.to_rad
75
+ brng = brng.to_rad
76
+
77
+ lat2 = lat1 + d*Math.cos(brng);
78
+ dlat = lat2 - lat1;
79
+ dphi = Math.log(Math.tan(lat2/2+Math::PI/4) / Math.tan(lat1/2+Math::PI/4))
80
+
81
+ q = begin
82
+ dlat / dphi
83
+ rescue
84
+ Math.cos(lat1) # E-W line gives dPhi=0
85
+ end
86
+
87
+ dlon = d * Math.sin(brng) / q
88
+ # check for some daft bugger going past the pole
89
+
90
+ if lat2.abs > Math::PI/2
91
+ lat2 = lat2>0 ? Math::PI-lat2 : -(Math::PI-lat2)
92
+ end
93
+ lon2 = (lon1+dlon+3*Math::PI) % (2*Math::PI) - Math::PI
94
+
95
+ GeoPoint.new lat2.to_deg, lon2.to_deg
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,27 @@
1
+ module GeoCalc
2
+ autoload :PrettyPrint, 'geo_calc/pretty_print'
3
+
4
+ module Calc
5
+ end
6
+ end
7
+
8
+ module GeoCalc::Calc
9
+ autoload :Bearing, 'geo_calc/calc/bearing'
10
+ autoload :Destination, 'geo_calc/calc/destination'
11
+ autoload :Distance, 'geo_calc/calc/distance'
12
+ autoload :Intersection, 'geo_calc/calc/intersection'
13
+ autoload :Midpoint, 'geo_calc/calc/midpoint'
14
+ autoload :Rhumb, 'geo_calc/calc/rhumb'
15
+
16
+ module All
17
+ def self.included base
18
+ base.send :include, GeoCalc::Calc::Bearing
19
+ base.send :include, GeoCalc::Calc::Destination
20
+ base.send :include, GeoCalc::Calc::Distance
21
+ base.send :include, GeoCalc::Calc::Intersection
22
+ base.send :include, GeoCalc::Calc::Midpoint
23
+ base.send :include, GeoCalc::Calc::Rhumb
24
+ base.send :include, GeoCalc::PrettyPrint
25
+ end
26
+ end
27
+ end
@@ -36,6 +36,46 @@ module GeoUnits
36
36
  alias_method :in_deg, :to_deg
37
37
  alias_method :in_degrees, :to_deg
38
38
  end
39
+
40
+ # all degrees between -180 and 180
41
+ def normalize_lng deg
42
+ case deg
43
+ when -360..-180
44
+ deg % 180
45
+ when -180..0
46
+ -180 + (deg % 180)
47
+ when 0..180
48
+ deg
49
+ when 180..360
50
+ deg % 180
51
+ else
52
+ raise ArgumentError, "Degrees #{deg} out of range, must be between -360 to 360"
53
+ end
54
+ end
55
+
56
+ # all degrees between -90 and 90
57
+ def normalize_lat deg
58
+ case deg
59
+ when -360..-270
60
+ deg % 90
61
+ when -270..-180
62
+ 90 - (deg % 90)
63
+ when -180..-90
64
+ - (deg % 90)
65
+ when -90..0
66
+ -90 + (deg % 90)
67
+ when 0..90
68
+ deg
69
+ when 90..180
70
+ deg % 90
71
+ when 180..270
72
+ - (deg % 90)
73
+ when 270..360
74
+ - 90 + (deg % 90)
75
+ else
76
+ raise ArgumentError, "Degrees #{deg} out of range, must be between -360 to 360"
77
+ end
78
+ end
39
79
 
40
80
  def normalize_deg degrees, shift = 0
41
81
  (degrees + shift) % 360
@@ -87,46 +127,63 @@ module NumericGeoExt
87
127
  # @returns {String} A string representation of number which contains precision significant digits
88
128
  def to_precision precision
89
129
  self.round(precision).to_s
90
-
91
- # numb = self.abs # can't take log of -ve number...
92
- # sign = self < 0 ? '-' : '';
93
- #
94
- # # can't take log of zero
95
- # if (numb == 0)
96
- # n = '0.'
97
- # while (precision -= 1) > 0
98
- # n += '0'
99
- # end
100
- # return n
101
- # end
102
- #
103
- # scale = (Math.log(numb) * Math.log10e).ceil # no of digits before decimal
104
- # n = (numb * (precision - scale)**10).round.to_s
105
- # if (scale > 0) # add trailing zeros & insert decimal as required
106
- # l = scale - n.length
107
- #
108
- # while (l -= 1) > 0
109
- # n += '0'
110
- # end
111
- #
112
- # if scale < n.length
113
- # n = n.slice(0,scale) + '.' + n.slice(scale)
114
- # else # prefix decimal and leading zeros if required
115
- # while (scale += 1) < 0
116
- # n = '0' + n
117
- # end
118
- # n = '0.' + n
119
- # end
120
- # end
121
- # sign + n
122
130
  end
123
131
  alias_method :to_fixed, :to_precision
124
132
 
133
+ # all degrees between -180 and 180
134
+ def normalize_lng
135
+ case self
136
+ when -360, 0, 360
137
+ 0
138
+ when -360..-180
139
+ self % 180
140
+ when -180..0
141
+ -180 + (self % 180)
142
+ when 0..180
143
+ self
144
+ when 180..360
145
+ self % 180
146
+ else
147
+ return (self % 360).normalize_lng if self > 360
148
+ return (360 - (self % 360)).normalize_lng if self < -360
149
+ raise ArgumentError, "Degrees #{self} out of range"
150
+ end
151
+ end
152
+
153
+ # all degrees between -90 and 90
154
+ def normalize_lat
155
+ case self
156
+ when -360, 0, 360
157
+ 0
158
+ when -180, 180
159
+ 0
160
+ when -360..-270
161
+ self % 90
162
+ when -270..-180
163
+ 90 - (self % 90)
164
+ when -180..-90
165
+ - (self % 90)
166
+ when -90..0
167
+ -90 + (self % 90)
168
+ when 0..90
169
+ self
170
+ when 90..180
171
+ self % 90
172
+ when 180..270
173
+ - (self % 90)
174
+ when 270..360
175
+ - 90 + (self % 90)
176
+ else
177
+ return (self % 360).normalize_lat if self > 360
178
+ return (360 - (self % 360)).normalize_lat if self < -360
179
+ raise ArgumentError, "Degrees #{self} out of range"
180
+ end
181
+ end
182
+
125
183
  def normalize_deg shift = 0
126
184
  (self + shift) % 360
127
185
  end
128
- alias_method :normalize_degrees, :normalize_deg
129
-
186
+ alias_method :normalize_degrees, :normalize_deg
130
187
  end
131
188
 
132
189
  module Math
@@ -137,9 +194,12 @@ end
137
194
 
138
195
  module NumericLatLngExt
139
196
  def to_lat
140
- normalize_deg
197
+ normalize_lat
198
+ end
199
+
200
+ def to_lng
201
+ normalize_lng
141
202
  end
142
- alias_method :to_lng, :to_lat
143
203
 
144
204
  def is_between? lower, upper
145
205
  (lower..upper).cover? self
@@ -247,8 +307,6 @@ class String
247
307
  end
248
308
  end
249
309
 
250
-
251
-
252
310
  class Fixnum
253
311
  include NumericGeoExt
254
312
  include NumericLatLngExt
@@ -258,4 +316,4 @@ class Float
258
316
  include NumericGeoExt
259
317
  include NumericLatLngExt
260
318
  end
261
-
319
+
data/lib/geo_calc/geo.rb CHANGED
@@ -61,7 +61,7 @@ module Geo
61
61
  # @returns {String} deg formatted as deg/min/secs according to specified format
62
62
  # @throws {TypeError} deg is an object, perhaps DOM object without .value?
63
63
 
64
- def to_dms deg, format = :dms, dp = nil
64
+ def to_dms deg, format = :dms, dp = nil
65
65
  deg = begin
66
66
  deg.to_f
67
67
  rescue
@@ -128,6 +128,7 @@ module Geo
128
128
  # @returns {String} Deg/min/seconds
129
129
 
130
130
  def to_lat deg, format = :dms, dp = 0
131
+ deg = deg.normalize_lat
131
132
  _lat = to_dms deg, format, dp
132
133
  _lat == '' ? '' : _lat[1..-1] + (deg<0 ? 'S' : 'N') # knock off initial '0' for lat!
133
134
  end
@@ -141,7 +142,7 @@ module Geo
141
142
  # @returns {String} Deg/min/seconds
142
143
 
143
144
  def to_lon deg, format = :dms, dp = 0
144
- deg = (360 - deg) * -1 if deg % 360 > 180
145
+ deg = deg.normalize_lng
145
146
  lon = to_dms deg, format, dp
146
147
  lon == '' ? '' : lon + (deg<0 ? 'W' : 'E')
147
148
  end