geo_units 0.2.6 → 0.3.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.
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