geo_units 0.2.6 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
data/.rspec CHANGED
@@ -1 +1 @@
1
- --color
1
+ --color --format nested
data/Gemfile CHANGED
@@ -2,6 +2,7 @@ source "http://rubygems.org"
2
2
  # Add dependencies required to use your gem here.
3
3
 
4
4
  gem "sugar-high", '>= 0.6.0'
5
+ gem 'sweetloader'
5
6
  gem 'i18n'
6
7
  gem 'activesupport'
7
8
 
@@ -15,6 +15,21 @@ Then run the bundler!
15
15
 
16
16
  @$ bundle@
17
17
 
18
+ h2. Status update (Sept 21, 2011)
19
+
20
+ GeoUnits is currently undergoing a major refactoring effort in order to group functionality in a more granular fashion, to allow for greater flexibility.
21
+ Also, the new GeoUnits will not specifically target use for geo calculations for Earth, but also other globes/worlds, such as in a fantasy setting, planets, suns etc.
22
+ Please help in this effort :)
23
+
24
+ The master branch has now been updated in order to allow specification of the order:
25
+ Set the `GeoUnit.default_coords_order` to fit your usage scenario.
26
+
27
+ `GeoUnit.default_coords_order = :lng_lat`
28
+
29
+ `GeoUnit.default_coords_order = :lat_lng`
30
+
31
+ All specs pass again and the dependencies have been updated :)
32
+
18
33
  h2. GeoUnits API
19
34
 
20
35
  <pre>GeoUnits.key(:foot) # will convert any kind of unit into one of (:feet, :meters, :kms, :miles, :radians)
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.6
1
+ 0.3.1
@@ -5,7 +5,7 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "geo_units"
8
- s.version = "0.2.6"
8
+ s.version = "0.3.1"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Kristian Mandrup"]
@@ -28,15 +28,28 @@ Gem::Specification.new do |s|
28
28
  "lib/geo_units.rb",
29
29
  "lib/geo_units/constants.rb",
30
30
  "lib/geo_units/converter.rb",
31
+ "lib/geo_units/converter/dms.rb",
32
+ "lib/geo_units/converter/normalizer.rb",
33
+ "lib/geo_units/converter/units.rb",
31
34
  "lib/geo_units/core_ext.rb",
32
- "lib/geo_units/dms_converter.rb",
33
35
  "lib/geo_units/maps.rb",
34
- "lib/geo_units/numeric_ext.rb",
36
+ "lib/geo_units/maps/earth.rb",
37
+ "lib/geo_units/maps/meters.rb",
38
+ "lib/geo_units/numeric.rb",
39
+ "lib/geo_units/numeric/dms.rb",
40
+ "lib/geo_units/numeric/normalizer.rb",
35
41
  "lib/geo_units/unit_conversions.rb",
42
+ "spec/geo_units/converter/dms_spec.rb",
43
+ "spec/geo_units/converter/normalizer_spec.rb",
44
+ "spec/geo_units/converter/units_spec.rb",
36
45
  "spec/geo_units/converter_spec.rb",
37
46
  "spec/geo_units/core_ext_spec.rb",
38
- "spec/geo_units/dms_converter_spec.rb",
39
- "spec/geo_units/numeric_ext_spec.rb",
47
+ "spec/geo_units/maps/earth_spec.rb",
48
+ "spec/geo_units/maps/meters_spec.rb",
49
+ "spec/geo_units/maps_spec.rb",
50
+ "spec/geo_units/numeric/dms_spec.rb",
51
+ "spec/geo_units/numeric/normalizer_spec.rb",
52
+ "spec/geo_units/numeric_spec.rb",
40
53
  "spec/geo_units_spec.rb",
41
54
  "spec/spec_helper.rb"
42
55
  ]
@@ -51,6 +64,7 @@ Gem::Specification.new do |s|
51
64
 
52
65
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
53
66
  s.add_runtime_dependency(%q<sugar-high>, [">= 0.6.0"])
67
+ s.add_runtime_dependency(%q<sweetloader>, [">= 0"])
54
68
  s.add_runtime_dependency(%q<i18n>, [">= 0"])
55
69
  s.add_runtime_dependency(%q<activesupport>, [">= 0"])
56
70
  s.add_development_dependency(%q<rspec>, [">= 2.5.0"])
@@ -58,6 +72,7 @@ Gem::Specification.new do |s|
58
72
  s.add_development_dependency(%q<jeweler>, [">= 1.6.4"])
59
73
  else
60
74
  s.add_dependency(%q<sugar-high>, [">= 0.6.0"])
75
+ s.add_dependency(%q<sweetloader>, [">= 0"])
61
76
  s.add_dependency(%q<i18n>, [">= 0"])
62
77
  s.add_dependency(%q<activesupport>, [">= 0"])
63
78
  s.add_dependency(%q<rspec>, [">= 2.5.0"])
@@ -66,6 +81,7 @@ Gem::Specification.new do |s|
66
81
  end
67
82
  else
68
83
  s.add_dependency(%q<sugar-high>, [">= 0.6.0"])
84
+ s.add_dependency(%q<sweetloader>, [">= 0"])
69
85
  s.add_dependency(%q<i18n>, [">= 0"])
70
86
  s.add_dependency(%q<activesupport>, [">= 0"])
71
87
  s.add_dependency(%q<rspec>, [">= 2.5.0"])
@@ -16,34 +16,63 @@
16
16
  # @throws ArgumentError
17
17
 
18
18
  require 'sugar-high/numeric'
19
- require 'sugar-high/class_ext'
19
+ require 'sweetloader'
20
20
 
21
21
  module GeoUnits
22
- autoload_modules :Converter, :DmsConverter, :NumericExt, :Maps, :Constants, :UnitConversions, :from => 'geo_units'
23
-
24
- def self.included(base)
25
- [Maps, Constants, UnitConversions].each do |module_name|
22
+ autoload_modules :Constants, :Converter, :Maps, :Numeric
23
+
24
+ class << self
25
+ attr_accessor :default_coords_order
26
+
27
+ def default_coords_order
28
+ @default_coords_order ||= :lng_lat
29
+ end
30
+ end
31
+
32
+ def self.included(base)
33
+ [:Maps, :Constants, :"Converter::Units"].each do |module_name|
34
+ module_name = "GeoUnits::#{module_name.to_s.camelize}".constantize
26
35
  base.send :include, module_name
27
- base.extend module_name
36
+ base.extend module_name
28
37
  end
29
38
  end
30
39
 
31
40
  def self.units
32
- [:feet, :meters, :kms, :miles, :radians]
41
+ [:feet, :meters, :kms, :kilometers, :miles, :radians]
33
42
  end
34
43
 
35
- units.each do |unit|
36
- class_eval %{
37
- def self.#{unit}_to unit, number = 0
38
- return 0 if number <= 0
39
- unit = key(unit)
40
- m = number / GeoUnits::Maps.meters_map[:#{unit}]
41
- m * GeoUnits::Maps.meters_map[unit]
42
- end
43
- }
44
+ (units - [:radians]).each do |unit_type|
45
+ define_singleton_method "#{unit_type}_to" do |unit, number = 0|
46
+ return 0 if number <= 0
47
+ unit = normalized(unit)
48
+
49
+ converter = GeoUnits::Maps::Meters
50
+ from = converter.from_unit[unit_type]
51
+ to = converter.to_unit[unit]
52
+
53
+ m = number * from * to
54
+ end
55
+ end
56
+
57
+ def self.radians_to unit, number, lat = 0
58
+ unit = normalized(unit)
59
+ # factor = GeoUnits::Converter::Units.units_per_longitude_degree(lat, unit)
60
+ # puts "factor: #{factor} - #{unit}"
61
+ (GeoUnits::Maps::Earth.distance_per_latitude_degree[unit] * number.to_f)
44
62
  end
45
63
 
46
64
  module ClassMethods
65
+ def normalized unit = :km
66
+ unit = key(unit)
67
+ return :feet if feet_unit.include? unit
68
+ return :meters if meters_unit.include? unit
69
+ return :kilometers if kms_unit.include? unit
70
+ return :miles if miles_unit.include? unit
71
+ return :radians if radins_unit.include? unit
72
+
73
+ raise ArgumentError, "Normalize unit error, unit key: #{unit}"
74
+ end
75
+
47
76
  def key unit = :km
48
77
  unit = unit.to_sym
49
78
  methods.grep(/_unit$/).each do |meth|
@@ -76,11 +105,11 @@ module GeoUnits
76
105
 
77
106
  def radians_unit
78
107
  [:rad, :radians]
79
- end
108
+ end
80
109
  end
81
-
110
+
82
111
  extend ClassMethods
83
- end
112
+ end
84
113
 
85
114
  require 'geo_units/core_ext'
86
115
 
@@ -4,30 +4,18 @@ module GeoUnits
4
4
  Math::PI / 180.0
5
5
  end
6
6
 
7
- def pi_div_rad
7
+ def pi_div_rad
8
8
  0.0174
9
9
  end
10
10
 
11
- def kms_per_mile
11
+ def kms_per_mile
12
12
  1.609
13
13
  end
14
14
 
15
- def meters_per_feet
15
+ def meters_per_feet
16
16
  3.2808399
17
17
  end
18
18
 
19
- def miles_per_latitude_degree
20
- 69.1
21
- end
22
-
23
- def kms_per_latitude_degree
24
- miles_per_latitude_degree * kms_per_mile
25
- end
26
-
27
- def latitude_degrees
28
- earth_radius_map[:miles] / miles_per_latitude_degree
29
- end
30
-
31
- extend self
19
+ extend self
32
20
  end
33
- end
21
+ end
@@ -1,5 +1,9 @@
1
1
  module GeoUnits
2
2
  module Converter
3
+ autoload_modules :Normalizer, :Dms, :Units
4
+
5
+ include Normalizer
6
+
3
7
  # Convert numeric degrees to deg/min/sec latitude (suffixed with N/S)
4
8
  #
5
9
  # @param {Number} deg: Degrees
@@ -9,7 +13,7 @@ module GeoUnits
9
13
 
10
14
  def to_lat deg, format = :dms, dp = 0
11
15
  deg = deg.normalize_lat
12
- _lat = DmsConverter.to_dms deg, format, dp
16
+ _lat = Dms.to_dms deg, format, dp
13
17
  _lat == '' ? '' : _lat[1..-1] + (deg<0 ? 'S' : 'N') # knock off initial '0' for lat!
14
18
  end
15
19
 
@@ -22,7 +26,7 @@ module GeoUnits
22
26
 
23
27
  def to_lon deg, format = :dms, dp = 0
24
28
  deg = deg.normalize_lng
25
- lon = DmsConverter.to_dms deg, format, dp
29
+ lon = Dms.to_dms deg, format, dp
26
30
  lon == '' ? '' : lon + (deg<0 ? 'W' : 'E')
27
31
  end
28
32
 
@@ -36,14 +40,12 @@ module GeoUnits
36
40
 
37
41
  def to_brng deg, format = :dms, dp = 0
38
42
  deg = (deg.to_f + 360) % 360 # normalise -ve values to 180º..360º
39
- brng = DmsConverter.to_dms deg, format, dp
43
+ brng = Dms.to_dms deg, format, dp
40
44
  brng.gsub /360/, '0' # just in case rounding took us up to 360º!
41
- end
42
-
43
- protected
45
+ end
44
46
 
45
47
  include NumericCheckExt # from sugar-high/numeric
46
-
48
+
47
49
  # Converts numeric degrees to radians
48
50
  def to_rad degrees
49
51
  degrees * Math::PI / 180
@@ -67,54 +69,8 @@ module GeoUnits
67
69
  alias_method :as_degrees, :to_deg
68
70
  alias_method :in_deg, :to_deg
69
71
  alias_method :in_degrees, :to_deg
70
-
71
- extend self
72
- end
73
72
 
74
- # all degrees between -180 and 180
75
- def normalize_lng deg
76
- case deg
77
- when -360..-180
78
- deg % 180
79
- when -180..0
80
- -180 + (deg % 180)
81
- when 0..180
82
- deg
83
- when 180..360
84
- deg % 180
85
- else
86
- raise ArgumentError, "Degrees #{deg} out of range, must be between -360 to 360"
87
- end
88
- end
89
-
90
- # all degrees between -90 and 90
91
- def normalize_lat deg
92
- case deg
93
- when -360..-270
94
- deg % 90
95
- when -270..-180
96
- 90 - (deg % 90)
97
- when -180..-90
98
- - (deg % 90)
99
- when -90..0
100
- -90 + (deg % 90)
101
- when 0..90
102
- deg
103
- when 90..180
104
- deg % 90
105
- when 180..270
106
- - (deg % 90)
107
- when 270..360
108
- - 90 + (deg % 90)
109
- else
110
- raise ArgumentError, "Degrees #{deg} out of range, must be between -360 to 360"
111
- end
112
- end
113
-
114
- def normalize_deg degrees, shift = 0
115
- (degrees + shift) % 360
73
+ extend self
116
74
  end
117
- alias_method :normalize_degrees, :normalize_deg
118
-
119
75
  extend self
120
- end
76
+ end
@@ -0,0 +1,112 @@
1
+ require 'sugar-high/numeric'
2
+ require 'sugar-high/string'
3
+
4
+ module GeoUnits
5
+ module Converter
6
+ module Dms
7
+ include NumericCheckExt
8
+
9
+ def parse_dms dms_str, options= {}
10
+ # check for signed decimal degrees without NSEW, if so return it directly
11
+ return dms_str if is_numeric?(dms_str)
12
+
13
+ raise "DMS parse error: #{dms_str}" if !(dms_str =~ /[NSEW]$/) || (dms_str =~ /\./)
14
+
15
+ # strip off any sign or compass dir'n & split out separate d/m/s
16
+ dms = dms_str.strip.gsub(/^-/,'').gsub(/[NSEW]$/i,'').split(/[^0-9.,]+/).map(&:strip).map(&:to_f)
17
+ return nil if dms.empty?
18
+
19
+ # and convert to decimal degrees...
20
+ deg = case dms.length
21
+ when 3 # interpret 3-part result as d/m/s
22
+ dms[0]/1 + dms[1]/60 + dms[2]/3600
23
+ when 2 # interpret 2-part result as d/m
24
+ dms[0]/1 + dms[1]/60
25
+ when 1 # just d (possibly decimal) or non-separated dddmmss
26
+ d = dms[0];
27
+ # check for fixed-width unseparated format eg 0033709W
28
+ d = "0#{d}" if (/[NS]/i.match(dms_str)) # - normalise N/S to 3-digit degrees
29
+ d = "#{d.slice(0,3)/1}#{deg.slice(3,5)/60}#{deg.slice(5)/3600}" if (/[0-9]{7}/.match(deg))
30
+ d
31
+ else
32
+ nil
33
+ end
34
+
35
+ raise "DMS parse error: #{deg} for #{dms_str}" if !deg
36
+
37
+ deg = (deg * -1) if (/^-|[WS]$/i.match(dms_str.strip)) # take '-', west and south as -ve
38
+ deg.to_f
39
+ end
40
+
41
+ # Convert decimal degrees to deg/min/sec format
42
+ # - degree, prime, double-prime symbols are added, but sign is discarded, though no compass
43
+ # direction is added
44
+ #
45
+ #
46
+ # @param {Number} deg: Degrees
47
+ # @param {String} [format=dms]: Return value as 'd', 'dm', 'dms'
48
+ # @param {Number} [dp=0|2|4]: No of decimal places to use - default 0 for dms, 2 for dm, 4 for d
49
+ # @returns {String} deg formatted as deg/min/secs according to specified format
50
+ # @throws {TypeError} deg is an object, perhaps DOM object without .value?
51
+
52
+ def to_dms deg, format = :dms, dp = nil
53
+ deg = begin
54
+ deg.to_f
55
+ rescue
56
+ nil
57
+ end
58
+ return nil if !deg # give up here if we can't make a number from deg
59
+
60
+ # default values
61
+ format ||= :dms
62
+ dp = if dp.nil?
63
+ case format.to_sym
64
+ when :d
65
+ 4
66
+ when :dm
67
+ 2
68
+ else
69
+ 0 # default
70
+ end
71
+ end
72
+ dp ||= 0
73
+
74
+ deg = deg.abs # (unsigned result ready for appending compass dir'n)
75
+
76
+ case format
77
+ when :d
78
+ d = deg.round(dp) # round degrees
79
+ ds = "0#{d}" if (d <100) # pad with leading zeros
80
+ ds = "0#{ds}" if (d <10)
81
+ dms = ds.to_s.concats("\u00B0") # add º symbol
82
+ when :dm
83
+ min = (deg*60).round(dp) # convert degrees to minutes & round
84
+ d = d.to_i
85
+ d = (min / 60).floor # get component deg/min
86
+ m = (min % 60).round(dp) # pad with trailing zeros
87
+ ds = d
88
+ ms = m
89
+ ds = "0#{d}" if (d<100) # pad with leading zeros
90
+ ds = "0#{d}" if (d<10)
91
+ ms = "0#{m}" if (m<10)
92
+ dms = ds.to_s.concats("\u00B0", ms, "\u2032") # add º, ' symbols
93
+ when :dms
94
+ sec = (deg * 3600).round # convert degrees to seconds & round
95
+ d = (sec / 3600).floor # get component deg/min/sec
96
+ m = ((sec / 60) % 60).floor
97
+ s = (sec % 60).round(dp) # pad with trailing zeros
98
+ ds = d
99
+ ms = m
100
+ ss = s
101
+ ds = "0#{d}" if (d < 100) # pad with leading zeros
102
+ ds = "0#{ds}" if (d < 10)
103
+ ms = "0#{m}" if (m < 10)
104
+ ss = "0#{s}" if (s < 10)
105
+ dms = ds.to_s.concats("\u00B0", ms, "\u2032", ss, "\u2033") # add º, ', " symbols
106
+ end
107
+ return dms
108
+ end
109
+ extend self
110
+ end
111
+ end
112
+ end