geo_calc 0.6.1 → 0.7.1

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.
Files changed (38) hide show
  1. data/Gemfile +3 -0
  2. data/README.textile +12 -0
  3. data/VERSION +1 -1
  4. data/geo_calc.gemspec +29 -6
  5. data/lib/geo_calc/calc/destination.rb +1 -1
  6. data/lib/geo_calc/calc/distance.rb +1 -1
  7. data/lib/geo_calc/calc/rhumb.rb +2 -2
  8. data/lib/geo_calc/calc.rb +20 -21
  9. data/lib/geo_calc/dms/converter.rb +106 -0
  10. data/lib/geo_calc/dms.rb +5 -0
  11. data/lib/geo_calc/extensions/array.rb +26 -0
  12. data/lib/geo_calc/extensions/hash.rb +23 -0
  13. data/lib/geo_calc/extensions/math.rb +6 -0
  14. data/lib/geo_calc/extensions/numeric.rb +24 -0
  15. data/lib/geo_calc/extensions/string.rb +44 -0
  16. data/lib/geo_calc/extensions/symbol.rb +9 -0
  17. data/lib/geo_calc/extensions.rb +4 -0
  18. data/lib/geo_calc/geo_point/class_methods.rb +15 -0
  19. data/lib/geo_calc/geo_point/core_extension.rb +11 -0
  20. data/lib/geo_calc/geo_point/shared.rb +29 -0
  21. data/lib/geo_calc/geo_point.rb +42 -40
  22. data/lib/geo_calc/pretty_print.rb +2 -2
  23. data/lib/geo_calc.rb +5 -0
  24. data/lib/geo_units/converter.rb +123 -0
  25. data/lib/geo_units/numeric_ext.rb +117 -0
  26. data/lib/geo_units.rb +21 -0
  27. data/spec/geo_calc/core_ext/numeric_geo_ext_spec.rb +48 -50
  28. data/spec/geo_calc/core_ext_spec.rb +49 -51
  29. data/spec/geo_calc/dms/converter_spec.rb +60 -0
  30. data/spec/geo_calc/geo_point/class_methods_spec.rb +31 -0
  31. data/spec/geo_calc/geo_point/initializer_spec.rb +148 -0
  32. data/spec/geo_calc/geo_point/lat_lon.rb +115 -0
  33. data/spec/geo_calc/geo_point_spec.rb +4 -274
  34. data/spec/geo_units/converter_spec.rb +57 -0
  35. metadata +56 -17
  36. data/lib/geo_calc/core_ext.rb +0 -318
  37. data/lib/geo_calc/geo.rb +0 -171
  38. data/spec/geo_calc/geo_spec.rb +0 -99
@@ -0,0 +1,123 @@
1
+ require 'geo_calc/calc'
2
+ require 'geo_calc/extensions'
3
+
4
+ module GeoUnits
5
+ module Converter
6
+ # Convert numeric degrees to deg/min/sec latitude (suffixed with N/S)
7
+ #
8
+ # @param {Number} deg: Degrees
9
+ # @param {String} [format=dms]: Return value as 'd', 'dm', 'dms'
10
+ # @param {Number} [dp=0|2|4]: No of decimal places to use - default 0 for dms, 2 for dm, 4 for d
11
+ # @returns {String} Deg/min/seconds
12
+
13
+ def to_lat deg, format = :dms, dp = 0
14
+ deg = deg.normalize_lat
15
+ _lat = GeoCalc::Dms::Converter.to_dms deg, format, dp
16
+ _lat == '' ? '' : _lat[1..-1] + (deg<0 ? 'S' : 'N') # knock off initial '0' for lat!
17
+ end
18
+
19
+ # Convert numeric degrees to deg/min/sec longitude (suffixed with E/W)
20
+ #
21
+ # @param {Number} deg: Degrees
22
+ # @param {String} [format=dms]: Return value as 'd', 'dm', 'dms'
23
+ # @param {Number} [dp=0|2|4]: No of decimal places to use - default 0 for dms, 2 for dm, 4 for d
24
+ # @returns {String} Deg/min/seconds
25
+
26
+ def to_lon deg, format = :dms, dp = 0
27
+ deg = deg.normalize_lng
28
+ lon = GeoCalc::Dms::Converter.to_dms deg, format, dp
29
+ lon == '' ? '' : lon + (deg<0 ? 'W' : 'E')
30
+ end
31
+
32
+
33
+ # Convert numeric degrees to deg/min/sec as a bearing (0º..360º)
34
+ #
35
+ # @param {Number} deg: Degrees
36
+ # @param {String} [format=dms]: Return value as 'd', 'dm', 'dms'
37
+ # @param {Number} [dp=0|2|4]: No of decimal places to use - default 0 for dms, 2 for dm, 4 for d
38
+ # @returns {String} Deg/min/seconds
39
+
40
+ def to_brng deg, format = :dms, dp = 0
41
+ deg = (deg.to_f + 360) % 360 # normalise -ve values to 180º..360º
42
+ brng = GeoCalc::Dms::Converter.to_dms deg, format, dp
43
+ brng.gsub /360/, '0' # just in case rounding took us up to 360º!
44
+ end
45
+
46
+ protected
47
+
48
+ include ::GeoCalc::NumericCheckExt
49
+
50
+ # Converts numeric degrees to radians
51
+ def to_rad degrees
52
+ degrees * Math::PI / 180
53
+ end
54
+ alias_method :to_radians, :to_rad
55
+ alias_method :as_rad, :to_rad
56
+ alias_method :as_radians, :to_rad
57
+ alias_method :in_rad, :to_rad
58
+ alias_method :in_radians, :to_rad
59
+
60
+
61
+ # Converts radians to numeric (signed) degrees
62
+ # latitude (north to south) from equator +90 up then -90 down (equator again) = 180 then 180 for south = 360 total
63
+ # longitude (west to east) east +180, west -180 = 360 total
64
+ def to_deg radians
65
+ radians * 180 / Math::PI
66
+ end
67
+
68
+ alias_method :to_degrees, :to_deg
69
+ alias_method :as_deg, :to_deg
70
+ alias_method :as_degrees, :to_deg
71
+ alias_method :in_deg, :to_deg
72
+ alias_method :in_degrees, :to_deg
73
+
74
+ extend self
75
+ end
76
+
77
+ # all degrees between -180 and 180
78
+ def normalize_lng deg
79
+ case deg
80
+ when -360..-180
81
+ deg % 180
82
+ when -180..0
83
+ -180 + (deg % 180)
84
+ when 0..180
85
+ deg
86
+ when 180..360
87
+ deg % 180
88
+ else
89
+ raise ArgumentError, "Degrees #{deg} out of range, must be between -360 to 360"
90
+ end
91
+ end
92
+
93
+ # all degrees between -90 and 90
94
+ def normalize_lat deg
95
+ case deg
96
+ when -360..-270
97
+ deg % 90
98
+ when -270..-180
99
+ 90 - (deg % 90)
100
+ when -180..-90
101
+ - (deg % 90)
102
+ when -90..0
103
+ -90 + (deg % 90)
104
+ when 0..90
105
+ deg
106
+ when 90..180
107
+ deg % 90
108
+ when 180..270
109
+ - (deg % 90)
110
+ when 270..360
111
+ - 90 + (deg % 90)
112
+ else
113
+ raise ArgumentError, "Degrees #{deg} out of range, must be between -360 to 360"
114
+ end
115
+ end
116
+
117
+ def normalize_deg degrees, shift = 0
118
+ (degrees + shift) % 360
119
+ end
120
+ alias_method :normalize_degrees, :normalize_deg
121
+
122
+ extend self
123
+ end
@@ -0,0 +1,117 @@
1
+ module GeoUnits
2
+ module NumericExt
3
+ def to_lat
4
+ normalize_lat
5
+ end
6
+
7
+ def to_lng
8
+ normalize_lng
9
+ end
10
+
11
+ def is_between? lower, upper
12
+ (lower..upper).cover? self
13
+ end
14
+
15
+
16
+ def to_dms format = :dms, dp = nil
17
+ GeoCalc::Dms::Converter.to_dms self, format, dp
18
+ end
19
+
20
+ def to_lat_dms format = :dms, dp = nil
21
+ GeoUnits::Converter.to_lat self, format, dp
22
+ end
23
+
24
+ def to_lon_dms format = :dms, dp = nil
25
+ GeoUnits::Converter.to_lon self, format, dp
26
+ end
27
+
28
+ # Converts numeric degrees to radians
29
+ def to_rad
30
+ self * Math::PI / 180
31
+ end
32
+ alias_method :to_radians, :to_rad
33
+ alias_method :as_rad, :to_rad
34
+ alias_method :as_radians, :to_rad
35
+ alias_method :in_rad, :to_rad
36
+ alias_method :in_radians, :to_rad
37
+
38
+
39
+ # Converts radians to numeric (signed) degrees
40
+ # latitude (north to south) from equator +90 up then -90 down (equator again) = 180 then 180 for south = 360 total
41
+ # longitude (west to east) east +180, west -180 = 360 total
42
+ def to_deg
43
+ self * 180 / Math::PI
44
+ end
45
+
46
+ alias_method :to_degrees, :to_deg
47
+ alias_method :as_deg, :to_deg
48
+ alias_method :as_degrees, :to_deg
49
+ alias_method :in_deg, :to_deg
50
+ alias_method :in_degrees, :to_deg
51
+
52
+
53
+ # Formats the significant digits of a number, using only fixed-point notation (no exponential)
54
+ #
55
+ # @param {Number} precision: Number of significant digits to appear in the returned string
56
+ # @returns {String} A string representation of number which contains precision significant digits
57
+ def to_precision precision
58
+ self.round(precision).to_s
59
+ end
60
+ alias_method :to_fixed, :to_precision
61
+
62
+ # all degrees between -180 and 180
63
+ def normalize_lng
64
+ case self
65
+ when -360, 0, 360
66
+ 0
67
+ when -360..-180
68
+ self % 180
69
+ when -180..0
70
+ -180 + (self % 180)
71
+ when 0..180
72
+ self
73
+ when 180..360
74
+ self % 180
75
+ else
76
+ return (self % 360).normalize_lng if self > 360
77
+ return (360 - (self % 360)).normalize_lng if self < -360
78
+ raise ArgumentError, "Degrees #{self} out of range"
79
+ end
80
+ end
81
+
82
+ # all degrees between -90 and 90
83
+ def normalize_lat
84
+ case self
85
+ when -360, 0, 360
86
+ 0
87
+ when -180, 180
88
+ 0
89
+ when -360..-270
90
+ self % 90
91
+ when -270..-180
92
+ 90 - (self % 90)
93
+ when -180..-90
94
+ - (self % 90)
95
+ when -90..0
96
+ -90 + (self % 90)
97
+ when 0..90
98
+ self
99
+ when 90..180
100
+ self % 90
101
+ when 180..270
102
+ - (self % 90)
103
+ when 270..360
104
+ - 90 + (self % 90)
105
+ else
106
+ return (self % 360).normalize_lat if self > 360
107
+ return (360 - (self % 360)).normalize_lat if self < -360
108
+ raise ArgumentError, "Degrees #{self} out of range"
109
+ end
110
+ end
111
+
112
+ def normalize_deg shift = 0
113
+ (self + shift) % 360
114
+ end
115
+ alias_method :normalize_degrees, :normalize_deg
116
+ end
117
+ end
data/lib/geo_units.rb ADDED
@@ -0,0 +1,21 @@
1
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2
+ # Geodesy representation conversion functions (c) Chris Veness 2002-2010
3
+ # - www.movable-type.co.uk/scripts/latlong.html
4
+ #
5
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
6
+
7
+ # Parses string representing degrees/minutes/seconds into numeric degrees
8
+ #
9
+ # This is very flexible on formats, allowing signed decimal degrees, or deg-min-sec optionally
10
+ # suffixed by compass direction (NSEW). A variety of separators are accepted (eg 3º 37' 09"W)
11
+ # or fixed-width format without separators (eg 0033709W). Seconds and minutes may be omitted.
12
+ # (Note minimal validation is done).
13
+ #
14
+ # @param {String|Number} dmsStr: Degrees or deg/min/sec in variety of formats
15
+ # @returns {Number} Degrees as decimal number
16
+ # @throws ArgumentError
17
+
18
+ module GeoUnits
19
+ autoload :Converter, 'geo_units/converter'
20
+ autoload :NumericExt, 'geo_units/numeric_ext'
21
+ end
@@ -3,71 +3,69 @@ require 'spec_helper'
3
3
  # - www.movable-type.co.uk/scripts/latlong.html
4
4
  describe GeoPoint do
5
5
  describe 'ruby core Class extensions' do
6
- describe NumericGeoExt do
7
- describe '#to_rad' do
8
- it 'should convert 0 degrees to 0 radians' do
9
- 0.to_rad.should == 0
10
- end
6
+ describe '#to_rad' do
7
+ it 'should convert 0 degrees to 0 radians' do
8
+ 0.to_rad.should == 0
9
+ end
11
10
 
12
- it 'should convert 180 degrees to PI radians' do
13
- 180.to_rad.should == Math::PI
14
- end
11
+ it 'should convert 180 degrees to PI radians' do
12
+ 180.to_rad.should == Math::PI
13
+ end
15
14
 
16
- it 'should convert 360 degrees to 6.28 radians' do
17
- 360.to_rad.should == Math::PI*2
18
- end
15
+ it 'should convert 360 degrees to 6.28 radians' do
16
+ 360.to_rad.should == Math::PI*2
19
17
  end
18
+ end
20
19
 
21
- describe '#to_radians' do
22
- it 'should alias to_rad' do
23
- 360.to_rad.should == 360.to_radians
24
- end
20
+ describe '#to_radians' do
21
+ it 'should alias to_rad' do
22
+ 360.to_rad.should == 360.to_radians
25
23
  end
24
+ end
26
25
 
27
- describe '#to_deg' do
28
- it 'should convert 0 radians to 0 degrees' do
29
- 0.to_deg.should == 0
30
- end
26
+ describe '#to_deg' do
27
+ it 'should convert 0 radians to 0 degrees' do
28
+ 0.to_deg.should == 0
29
+ end
31
30
 
32
- it 'should convert PI radians to 180 degrees' do
33
- 180.to_rad.to_deg.should be_within(0.01).of(180)
34
- end
31
+ it 'should convert PI radians to 180 degrees' do
32
+ 180.to_rad.to_deg.should be_within(0.01).of(180)
33
+ end
35
34
 
36
- it 'should convert 6.28 radians to 360 degrees' do
37
- 360.to_rad.to_deg.should be_within(0.01).of(360)
38
- end
35
+ it 'should convert 6.28 radians to 360 degrees' do
36
+ 360.to_rad.to_deg.should be_within(0.01).of(360)
39
37
  end
40
-
41
- describe '#to_degrees' do
42
- it 'should alias to_deg' do
43
- 360.to_deg.should == 360.to_degrees
44
- end
38
+ end
39
+
40
+ describe '#to_degrees' do
41
+ it 'should alias to_deg' do
42
+ 360.to_deg.should == 360.to_degrees
45
43
  end
46
-
47
- describe '#to_fixed' do
48
- it 'should make precision 4' do
49
- 1.123456.to_fixed(2).should == '1.12'
50
- end
44
+ end
45
+
46
+ describe '#to_fixed' do
47
+ it 'should make precision 4' do
48
+ 1.123456.to_fixed(2).should == '1.12'
51
49
  end
50
+ end
52
51
 
53
- describe '#to_precision' do
54
- it 'should alis to_fixed' do
55
- 1.123456.to_precision(4).should == '1.1235'
56
- end
52
+ describe '#to_precision' do
53
+ it 'should alis to_fixed' do
54
+ 1.123456.to_precision(4).should == '1.1235'
57
55
  end
56
+ end
58
57
 
59
- describe '#normalize' do
60
- it 'should turn 180 deg and normalize' do
61
- 361.normalize_deg(180).should == 181
62
- end
63
- it 'should normalize deg' do
64
- 361.normalize_deg.should == 1
65
- end
58
+ describe '#normalize' do
59
+ it 'should turn 180 deg and normalize' do
60
+ 361.normalize_deg(180).should == 181
61
+ end
62
+ it 'should normalize deg' do
63
+ 361.normalize_deg.should == 1
64
+ end
66
65
 
67
- it 'should alias with #normalize_degrees' do
68
- 362.normalize_degrees.should == 2
69
- end
66
+ it 'should alias with #normalize_degrees' do
67
+ 362.normalize_degrees.should == 2
70
68
  end
71
- end # NumericGeoExt
69
+ end
72
70
  end
73
71
  end
@@ -3,56 +3,54 @@ require 'spec_helper'
3
3
  # - www.movable-type.co.uk/scripts/latlong.html
4
4
  describe GeoPoint do
5
5
  describe 'ruby core Class extensions' do
6
- describe NumericLatLngExt do
7
- describe 'Fixnum extension' do
8
- describe '#to_lat' do
9
- it 'should set origin at 0,0' do
10
- origin = [0, 0].geo_point
11
- origin.lat.should == 0
12
- origin.lng.should == 0
13
- end
14
-
15
- it 'should return latitude degree value for 360' do
16
- 360.to_lat.should == 0
17
- end
18
-
19
- it 'should normalize degrees before converting to latitude, so 361 becomes 1' do
20
- 361.to_lat.should == 1
21
- end
22
- end
23
-
24
- describe '#to_lng' do
25
- it 'should return latitude degree value for 360' do
26
- 90.to_lng.should == 90
27
- end
28
-
29
- it 'should normalize degrees before converting to latitude, so 361 becomes 1' do
30
- 91.to_lng.should == 91
31
- end
32
- end
33
- end
34
-
35
- describe 'Float extension' do
36
- describe '#to_lat' do
37
- it 'should return latitude degree value for 360' do
38
- (360.0).to_lat.should == 0
39
- end
40
-
41
- it 'should normalize degrees before converting to latitude, so 361 becomes 1' do
42
- (361.1).to_lat.should be_within(0.01).of(1.1)
43
- end
44
- end
45
-
46
- describe '#to_lng' do
47
- it 'should return latitude degree value for 360' do
48
- (360.0).to_lng.should == 0
49
- end
50
-
51
- it 'should normalize degrees before converting to latitude, so 361 becomes 1' do
52
- (361.1).to_lng.should be_within(0.01).of(1.1)
53
- end
54
- end
55
- end
6
+ describe 'Fixnum extension' do
7
+ describe '#to_lat' do
8
+ it 'should set origin at 0,0' do
9
+ origin = [0, 0].geo_point
10
+ origin.lat.should == 0
11
+ origin.lng.should == 0
12
+ end
13
+
14
+ it 'should return latitude degree value for 360' do
15
+ 360.to_lat.should == 0
16
+ end
17
+
18
+ it 'should normalize degrees before converting to latitude, so 361 becomes 1' do
19
+ 361.to_lat.should == 1
20
+ end
21
+ end
22
+
23
+ describe '#to_lng' do
24
+ it 'should return latitude degree value for 360' do
25
+ 90.to_lng.should == 90
26
+ end
27
+
28
+ it 'should normalize degrees before converting to latitude, so 361 becomes 1' do
29
+ 91.to_lng.should == 91
30
+ end
31
+ end
56
32
  end
57
- end
33
+
34
+ describe 'Float extension' do
35
+ describe '#to_lat' do
36
+ it 'should return latitude degree value for 360' do
37
+ (360.0).to_lat.should == 0
38
+ end
39
+
40
+ it 'should normalize degrees before converting to latitude, so 361 becomes 1' do
41
+ (361.1).to_lat.should be_within(0.01).of(1.1)
42
+ end
43
+ end
44
+
45
+ describe '#to_lng' do
46
+ it 'should return latitude degree value for 360' do
47
+ (360.0).to_lng.should == 0
48
+ end
49
+
50
+ it 'should normalize degrees before converting to latitude, so 361 becomes 1' do
51
+ (361.1).to_lng.should be_within(0.01).of(1.1)
52
+ end
53
+ end
54
+ end
55
+ end
58
56
  end
@@ -0,0 +1,60 @@
1
+ require 'spec_helper'
2
+
3
+ class Parser
4
+ include GeoCalc::Dms::Converter
5
+ end
6
+
7
+ def parser
8
+ Parser.new
9
+ end
10
+
11
+ # - www.movable-type.co.uk/scripts/latlong.html
12
+ describe GeoCalc::Dms::Converter do
13
+ # # @param {String|Number} dmsStr: Degrees or deg/min/sec in variety of formats
14
+ # @returns {Number} Degrees as decimal number
15
+ describe '#parse_dms' do
16
+ it 'should convert "58 38 38N" to a Float of degrees (58..59)' do
17
+ deg = parser.parse_dms("58 38 38N")
18
+ deg.should be_a(Float)
19
+ deg.should be_between(58, 59)
20
+ end
21
+
22
+ it 'should convert "01 38 38W" to a Float of degrees (1..2)' do
23
+ deg = parser.parse_dms("01 38 38W")
24
+ deg.should be_a(Float)
25
+ deg.should < 0
26
+ deg.should > -2
27
+ end
28
+
29
+ it 'should convert "005 38 E" to a Float of degrees (5..6)' do
30
+ deg = parser.parse_dms("005 38 E")
31
+ deg.should be_a(Float)
32
+ deg.should be_between(5, 6)
33
+ end
34
+ end
35
+
36
+ # deg, format = :dms, dp = 0
37
+ describe '#to_dms' do
38
+ it 'should convert 58.3 to a String in DMS format' do
39
+ dms = parser.to_dms(58.3)
40
+ dms.should be_a(String)
41
+ expr = Regexp.escape "058".concat("\u00B0", "18", "\u2032", "00", "\u2033")
42
+ dms.should match expr
43
+ end
44
+
45
+ it 'should convert 58.3 to a String in DM format' do
46
+ dm = parser.to_dms(58.3, :dm, 2)
47
+ dm.should be_a(String)
48
+ expr = Regexp.escape "058".concat("\u00B0", "18", "\u2032")
49
+ dm.should match expr
50
+ end
51
+
52
+ it 'should convert 58.3 to a String in D format' do
53
+ d = parser.to_dms(58.3, :d, 2)
54
+ d.should be_a(String)
55
+ m = Regexp.escape "058".concat("\u00B0")
56
+ d.should match m
57
+ end
58
+ end
59
+ end
60
+
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ # - www.movable-type.co.uk/scripts/latlong.html
4
+ describe GeoPoint do
5
+ describe 'Class methods' do
6
+ describe '#coord_mode' do
7
+ it 'should change global coordinates mode' do
8
+ GeoPoint.coord_mode = :lng_lat
9
+ GeoPoint.coord_mode.should == :lng_lat
10
+
11
+ GeoPoint.coord_mode = :lat_lng
12
+ GeoPoint.coord_mode.should == :lat_lng
13
+ end
14
+
15
+ it 'shoould not allow setting invalid coord mode' do
16
+ lambda { GeoPoint.coord_mode = :blip }.should raise_error
17
+ end
18
+ end
19
+
20
+ describe '#earth_radius_km' do
21
+ it 'should change global earth_radius_km' do
22
+ GeoPoint.earth_radius_km = 6360
23
+ GeoPoint.earth_radius_km.should == 6360
24
+ end
25
+
26
+ it 'shoould not allow setting invalid earth radius' do
27
+ lambda { GeoPoint.earth_radius_km = 6100 }.should raise_error
28
+ end
29
+ end
30
+ end
31
+ end