geo_vectors 0.5.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.
Files changed (47) hide show
  1. data/.document +5 -0
  2. data/.rspec +1 -0
  3. data/Distance calc notes.txt +64 -0
  4. data/Gemfile +14 -0
  5. data/LICENSE.txt +20 -0
  6. data/README.textile +178 -0
  7. data/Rakefile +53 -0
  8. data/VERSION +1 -0
  9. data/geo_vectors.gemspec +115 -0
  10. data/lib/geo_vectors.rb +7 -0
  11. data/lib/geo_vectors/bearing_vector.rb +87 -0
  12. data/lib/geo_vectors/core_ext.rb +54 -0
  13. data/lib/geo_vectors/direction_vector.rb +59 -0
  14. data/lib/geo_vectors/geo_point.rb +33 -0
  15. data/lib/geo_vectors/geo_vector.rb +56 -0
  16. data/lib/geo_vectors/geo_vectors.rb +92 -0
  17. data/lib/geo_vectors/point_vector.rb +85 -0
  18. data/lib/geo_vectors/point_vector/point_ops.rb +23 -0
  19. data/lib/geo_vectors/point_vector/vector_ops.rb +38 -0
  20. data/lib/geo_vectors/util.rb +3 -0
  21. data/lib/geo_vectors/util/calc.rb +89 -0
  22. data/lib/geo_vectors/util/geo_direction.rb +101 -0
  23. data/lib/geo_vectors/util/geo_distance.rb +135 -0
  24. data/lib/geo_vectors/util/geo_units.rb +53 -0
  25. data/lib/geo_vectors/vector_parser.rb +54 -0
  26. data/spec/geo_vectors/API proposal guide.txt +142 -0
  27. data/spec/geo_vectors/bearing_vector/add_vector_spec.rb +30 -0
  28. data/spec/geo_vectors/bearing_vector/random_spec.rb +18 -0
  29. data/spec/geo_vectors/bearing_vector_spec.rb +34 -0
  30. data/spec/geo_vectors/direction_vector/add_vector_spec.rb +30 -0
  31. data/spec/geo_vectors/direction_vector/point_add_spec.rb +0 -0
  32. data/spec/geo_vectors/direction_vector/random_spec.rb +16 -0
  33. data/spec/geo_vectors/direction_vector/subtract_vector_spec.rb +0 -0
  34. data/spec/geo_vectors/direction_vector_spec.rb +27 -0
  35. data/spec/geo_vectors/geo_vectors_spec.rb +108 -0
  36. data/spec/geo_vectors/point_vector/add_vector_spec.rb +135 -0
  37. data/spec/geo_vectors/point_vector/initializer_spec.rb +82 -0
  38. data/spec/geo_vectors/point_vector/point_add_spec.rb +45 -0
  39. data/spec/geo_vectors/point_vector/random_spec.rb +17 -0
  40. data/spec/geo_vectors/point_vector/scale_vector_spec.rb +52 -0
  41. data/spec/geo_vectors/point_vector/subtract_vector_spec.rb +80 -0
  42. data/spec/geo_vectors/point_vector_spec.rb +111 -0
  43. data/spec/geo_vectors/util/geo_direction_spec.rb +74 -0
  44. data/spec/geo_vectors/util/geo_distance_spec.rb +70 -0
  45. data/spec/geo_vectors/util/geo_units_spec.rb +23 -0
  46. data/spec/spec_helper.rb +7 -0
  47. metadata +218 -0
@@ -0,0 +1,23 @@
1
+ class PointVector < GeoVector
2
+ module PointOps
3
+ # return new point from adding vector to point
4
+ def add_to_point point
5
+ add_to_point! point.dup
6
+ end
7
+
8
+ # add vector directly to point (destructive update)
9
+ def add_to_point! point
10
+ point.lat = lat + point.lat
11
+ point.lng = lng + point.lng
12
+ point
13
+ end
14
+
15
+ def sub_from_point point
16
+ reverse.add_to_point point
17
+ end
18
+
19
+ def sub_from_point! point
20
+ reverse.add_to_point! point
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,38 @@
1
+ class PointVector < GeoVector
2
+ module VectorOps
3
+ def random_vector
4
+ lat_max = point.lat.abs
5
+ lng_max = point.lng.abs
6
+ rand_lat = rand(lat_max * 2) - lat_max
7
+ rand_lng = rand(lng_max * 2) - lng_max
8
+ PointVector.new [rand_lat, rand_lng]
9
+ end
10
+
11
+ def add *args
12
+ self.dup.add! *args
13
+ end
14
+ alias_method :<<, :add
15
+ alias_method :+, :add
16
+
17
+ def add! *args
18
+ vector = GeoVector::Parser.create_vector *args
19
+ case vector
20
+ when PointVector
21
+ v2 = add_to_point vector
22
+ self.point = v2.to_arr
23
+ self
24
+ else
25
+ GeoVectors.new self, vector
26
+ end
27
+ end
28
+
29
+ def sub *args
30
+ self.dup.sub! *args
31
+ end
32
+
33
+ def sub! *args
34
+ vector = GeoVector::Parser.create_vector *args
35
+ add! vector.reverse
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,3 @@
1
+ require 'geo_vectors/util/geo_direction'
2
+ require 'geo_vectors/util/geo_distance'
3
+ require 'geo_vectors/util/geo_units'
@@ -0,0 +1,89 @@
1
+ module Calc
2
+ protected
3
+
4
+ def earth_radius_radians
5
+ 2 * Math::PI
6
+ end
7
+
8
+ def bounding_box(point, radius, options = {})
9
+ lat,lon = extract_coordinates(point)
10
+ radius = radius.to_f
11
+ units = options[:units] || :mi
12
+ [
13
+ lat - (radius / latitude_degree_distance(units)),
14
+ lon - (radius / longitude_degree_distance(lat, units)),
15
+ lat + (radius / latitude_degree_distance(units)),
16
+ lon + (radius / longitude_degree_distance(lat, units))
17
+ ]
18
+ end
19
+
20
+ def calc_point vector, mode
21
+ vector = relative_vector(vector) if mode == :relative
22
+ GeoPoint.new lat + vector.lat, lng + vector.lng
23
+ end
24
+
25
+ def relative_vector vector
26
+ delta_lng = vector.lng / longitude_radian_distance(lat)
27
+ delta_lat = vector.lat / latitude_radian_distance
28
+ Vector.new delta_lat, delta_lng
29
+ end
30
+
31
+ def degrees_to_radians degrees
32
+ degrees / 57.29578
33
+ end
34
+
35
+ def delta_lat degrees, distance
36
+ rad = degrees_to_radians(degrees)
37
+ Math.sin(rad) * distance
38
+ end
39
+
40
+ def delta_lng degrees, distance
41
+ rad = degrees_to_radians(degrees)
42
+ Math.cos(rad) * distance
43
+ end
44
+
45
+ def latitude_radian_distance
46
+ 0.017453292519943295
47
+ end
48
+
49
+ def longitude_radian_distance(latitude)
50
+ latitude_radian_distance * Math.cos(latitude)
51
+ end
52
+
53
+ def distance_to_radians(distance, units = :mi)
54
+ distance.to_f / earth_radius(units)
55
+ end
56
+
57
+ def radians_to_distance(radians, units = :mi)
58
+ radians * earth_radius(units)
59
+ end
60
+
61
+ ##
62
+ # Distance spanned by one degree of latitude in the given units.
63
+ #
64
+ def latitude_degree_distance(units = :mi)
65
+ 2 * Math::PI * earth_radius(units) / 360
66
+ end
67
+
68
+ ##
69
+ # Convert kilometers to miles.
70
+ #
71
+ def to_miles(km)
72
+ km * km_in_mi
73
+ end
74
+
75
+ ##
76
+ # Radius of the Earth in the given units (:mi or :km). Default is :mi.
77
+ #
78
+ def earth_radius(units = :mi)
79
+ units == :km ? EARTH_RADIUS : to_miles(EARTH_RADIUS)
80
+ end
81
+
82
+ ##
83
+ # Distance spanned by one degree of longitude at the given latitude.
84
+ # This ranges from around 69 miles at the equator to zero at the poles.
85
+ #
86
+ def longitude_degree_distance(latitude, units = :mi)
87
+ latitude_degree_distance(units) * Math.cos(to_radians(latitude))
88
+ end
89
+ end
@@ -0,0 +1,101 @@
1
+ module GeoDirection
2
+ def valid_directions
3
+ [:north, :south, :east, :west, :N, :S, :E, :W, :NW, :NE, :SW, :SE]
4
+ end
5
+
6
+ def directions
7
+ [:N, :S, :E, :W, :NW, :NE, :SW, :SE]
8
+ end
9
+
10
+ def random_direction
11
+ directions[rand(directions.size)]
12
+ end
13
+
14
+ def valid_direction? dir
15
+ valid_directions.include? dir
16
+ end
17
+
18
+ def dir_to_bearing dir
19
+ dir = dir_to_bearing_map[dir]
20
+ raise "No bearing for direction #{dir} could be found" if !dir
21
+ dir
22
+ end
23
+
24
+ def bearing_to_dir brng
25
+ dir = bearing_to_dir_map[:"_#{brng}"]
26
+ raise "No direction could be for the bearing #{brng}" if !dir
27
+ dir
28
+ end
29
+
30
+ def get_valid_direction dir
31
+ return dir_map[dir] if dir_map[dir]
32
+ raise "Not a valid direction: #{dir}" if !directions.include? dir
33
+ dir
34
+ end
35
+
36
+ def to_bearing_vector direction, distance
37
+ distance, direction = [direction, distance] if direction.kind_of?(GeoDistance) || distance.kind_of?(Symbol)
38
+ BearingVector.new distance, dir_to_bearing(direction)
39
+ end
40
+
41
+ def to_point_vector direction, distance
42
+ # dist degrees
43
+ dd = distance.to_deg
44
+
45
+ lng, lat = case direction
46
+ when :N
47
+ [0, -dd]
48
+ when :S
49
+ [0, dd]
50
+ when :W
51
+ [-dd, 0]
52
+ when :E
53
+ [dd, 0]
54
+ when :NW
55
+ [dd, b]
56
+ when :SW
57
+ [dd, -dd]
58
+ when :NE
59
+ [-dd, dd]
60
+ when :SE
61
+ [-dd, -dd]
62
+ end
63
+ [lat, lng].vector
64
+ end
65
+
66
+ protected
67
+
68
+ def check_direction! dir
69
+ raise ArgumentError, "Not a valid direction: #{dir}" if !valid_direction?
70
+ end
71
+
72
+ def dir_map
73
+ {:east => :E, :north => :N, :west => :W, :south => :S}
74
+ end
75
+
76
+ def bearing_to_dir_map
77
+ {
78
+ :_0 => :N, # East
79
+ :_45 => :NE, # North East
80
+ :_90 => :N, # North
81
+ :_135 => :NW, # North West
82
+ :_180 => :W, # West (2*90)
83
+ :_225 => :SW, # South West
84
+ :_270 => :S, # South (3*90)
85
+ :_315 => :SE # South East
86
+ }
87
+ end
88
+
89
+ def dir_to_bearing_map
90
+ {
91
+ :E => 0, # East
92
+ :NE => 45, # North East
93
+ :N => 90, # North
94
+ :NW => 135, # North West
95
+ :W => 180, # West (2*90)
96
+ :SW => 225, # South West
97
+ :S => 270, # South (3*90)
98
+ :SE => 315 # South East
99
+ }
100
+ end
101
+ end
@@ -0,0 +1,135 @@
1
+ require 'geo_vectors/util/geo_units'
2
+ require 'active_support/inflector'
3
+
4
+ class GeoDistance
5
+ include NumericCheckExt
6
+ include GeoUnits
7
+ include Comparable
8
+
9
+ attr_accessor :unit, :number
10
+
11
+ def initialize number, unit = :kms
12
+ check_unit! unit
13
+ check_numeric! number
14
+
15
+ @unit = unit
16
+ @number = number
17
+ end
18
+
19
+ def to_s
20
+ "distance: #{number} #{unit}"
21
+ end
22
+
23
+ def * factor
24
+ dist = self.dup
25
+ dist.number *= factor
26
+ dist
27
+ end
28
+
29
+ def / factor
30
+ dist = self.dup
31
+ dist.number /= factor
32
+ dist
33
+ end
34
+
35
+ # compare 2 distances
36
+ def <=> dist
37
+ dist = extract_distance(dist).as(unit)
38
+ if number < dist.number
39
+ -1
40
+ elsif number > dist.number
41
+ 1
42
+ else
43
+ 0
44
+ end
45
+ end
46
+
47
+ def convert_to_meters
48
+ (unit == :radians) ? radians_to(:meters) : number / meters_map[unit]
49
+ end
50
+
51
+ # convert to unit (see GeoMagic)
52
+ def as unit
53
+ check_unit! unit
54
+ dist = self.dup
55
+ dist.number = convert_to_meters * meters_map[unit]
56
+ dist.unit = unit
57
+ dist
58
+ end
59
+
60
+ # from GeoUnits
61
+ GeoUnits.valid_units.map(&:to_s).each do |unit|
62
+ one_unit = unit.singularize
63
+ class_eval %{
64
+ def #{one_unit}
65
+ as(:#{unit}).number
66
+ end
67
+
68
+ def as_#{unit}
69
+ as(:#{unit})
70
+ end
71
+
72
+ alias_method :to_#{unit}, :#{one_unit}
73
+ alias_method :in_#{unit}, :#{one_unit}
74
+ alias_method :#{unit.pluralize}, :#{one_unit}
75
+ }
76
+ end
77
+
78
+ def random
79
+ rand(number.to_f * 100) / 100.0
80
+ end
81
+
82
+ def radians_to unit
83
+ check_unit! unit
84
+ earth_radius[unit] * number
85
+ end
86
+
87
+ protected
88
+
89
+ include GeoUnits::UnitMaps
90
+
91
+ # converts a number into a distance of same unit
92
+ def extract_distance dist
93
+ is_numeric?(dist) ? dist.send(unit) : dist
94
+ end
95
+
96
+ # used to extend Fixnum and Float
97
+ module Unit
98
+ extend GeoUnits
99
+ extend GeoUnits::UnitMaps
100
+
101
+ # from GeoUnits
102
+ valid_units.map(&:to_s).each do |unit|
103
+ one_unit = unit.singularize
104
+ class_eval %{
105
+ def #{one_unit}
106
+ GeoDistance.new self, :#{unit}
107
+ end
108
+ alias_method :to_#{unit}, :#{one_unit}
109
+ alias_method :in_#{unit}, :#{one_unit}
110
+ alias_method :as_#{unit}, :#{one_unit}
111
+ alias_method :#{unit.pluralize}, :#{one_unit}
112
+ }
113
+ end
114
+
115
+ def [] key
116
+ raise ArgumentError, "Invalid unit key #{key}" if !respond_to? key
117
+ earth_radius[key] * self
118
+ end
119
+ end
120
+
121
+ module Extract
122
+ def extract_distance dist
123
+ case dist
124
+ when Fixnum, Float
125
+ dist.km
126
+ when GeoDistance
127
+ dist
128
+ else
129
+ raise ArgumentError, "Could not convert #{dist} to a GeoDistance"
130
+ end
131
+ end
132
+ end
133
+
134
+ include Extract
135
+ end
@@ -0,0 +1,53 @@
1
+ module GeoUnits
2
+ module Methods
3
+ def valid_units
4
+ [:feet, :meters, :kms, :miles, :radians]
5
+ end
6
+
7
+ def valid_unit? unit
8
+ valid_units.include? unit
9
+ end
10
+
11
+ # The default unit is assumed to be kms
12
+ # This can be changed
13
+ # Example:
14
+ # GeoVector.default_unit = :km
15
+
16
+ def default_unit
17
+ @default_unit || :kms
18
+ end
19
+
20
+ def default_unit= unit
21
+ @default_unit || :kms
22
+ end
23
+
24
+ def check_unit! unit
25
+ raise ArgumentError, "Not a valid unit" if !valid_unit? unit
26
+ end
27
+ end
28
+
29
+ include Methods
30
+ extend Methods
31
+
32
+ module UnitMaps
33
+
34
+ def earth_radius
35
+ {
36
+ :feet => 20895592,
37
+ :meters => 6371000,
38
+ :kms => 6371,
39
+ :miles => 3956
40
+ }
41
+ end
42
+
43
+ def meters_map
44
+ {
45
+ :feet => 3.2808,
46
+ :meters => 1,
47
+ :kms => 0.001,
48
+ :miles => 0.00062137,
49
+ :radians => 111170
50
+ }
51
+ end
52
+ end
53
+ end