geo_vectors 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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