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,7 @@
1
+ require 'geo_vectors/geo_point'
2
+ require 'geo_vectors/geo_vector'
3
+ require 'geo_vectors/vector_parser'
4
+ require 'geo_vectors/point_vector'
5
+ require 'geo_vectors/bearing_vector'
6
+ require 'geo_vectors/direction_vector'
7
+ require 'geo_vectors/geo_vectors'
@@ -0,0 +1,87 @@
1
+ require 'geo_vectors/geo_vector'
2
+
3
+ class BearingVector < GeoVector
4
+ include GeoDistance::Extract
5
+ include GeoUnits::Converter
6
+
7
+ attr_reader :bearing
8
+ attr_reader :distance
9
+
10
+ # should be Distance objects!
11
+ def initialize dist, bearing
12
+ dist, bearing = [bearing, dist] if bearing.kind_of? GeoDistance
13
+ self.bearing = bearing
14
+ self.distance = dist
15
+ end
16
+
17
+ def direction
18
+ begin
19
+ bearing_to_dir bearing
20
+ rescue
21
+ bearing
22
+ end
23
+ end
24
+
25
+ def random_vector
26
+ BearingVector.new distance.random, random_bearing
27
+ end
28
+
29
+ def add_to_point! point
30
+ dest_point = point.destination_point bearing, distance.in_kms
31
+ point.lat = dest_point.lat
32
+ point.lng = dest_point.lng
33
+ point
34
+ end
35
+
36
+ def add_to_point point
37
+ self.dup.add_to_point! point
38
+ end
39
+
40
+ # normalize to within 0-360 degrees
41
+ def bearing= brng
42
+ @bearing = brng.normalize_degrees
43
+ end
44
+
45
+ def distance= dist
46
+ @distance = extract_distance dist
47
+ end
48
+
49
+ def reverse
50
+ self.dup.reverse!
51
+ end
52
+
53
+ def reverse!
54
+ reverse_bearing!
55
+ self
56
+ end
57
+
58
+ def scale! scale
59
+ @distance = distance * scale
60
+ end
61
+
62
+ def unit
63
+ distance.unit
64
+ end
65
+
66
+ def to_s
67
+ "#{distance}, bearing: #{bearing} degrees"
68
+ end
69
+
70
+ protected
71
+
72
+ def reverse_bearing!
73
+ shift = case bearing
74
+ when 0
75
+ 0
76
+ when -360..0
77
+ 180
78
+ when 0..360
79
+ -180
80
+ end
81
+ self.bearing = bearing + shift
82
+ end
83
+
84
+ def random_bearing
85
+ rand(360 * 100) / 100
86
+ end
87
+ end
@@ -0,0 +1,54 @@
1
+ require 'geo_vectors/util/geo_distance'
2
+
3
+ class Fixnum
4
+ include GeoDistance::Unit
5
+ end
6
+
7
+ class Float
8
+ include GeoDistance::Unit
9
+ end
10
+
11
+ class GeoVector
12
+ module Macros
13
+ def point_vector
14
+ PointVector.new self.geo_point
15
+ end
16
+ alias_method :vector, :point_vector
17
+ alias_method :geo_vector, :point_vector
18
+ alias_method :p_vector, :point_vector
19
+ end
20
+ end
21
+
22
+ class GeoPoint
23
+ include ::GeoVector::Macros
24
+ end
25
+
26
+ class Hash
27
+ include GeoVector::Macros
28
+ end
29
+
30
+ class String
31
+ include GeoVector::Macros
32
+ end
33
+
34
+ class Array
35
+ include GeoVector::Macros
36
+
37
+ def b_vector
38
+ raise ArgumentException, "To create a BearingVector, the Array must contain at least 2 elements, one for bearing and oen for distance" if !(size >= 2)
39
+ BearingVector.new self[0], self[1]
40
+ end
41
+ alias_method :bearing_vector, :b_vector
42
+
43
+ def d_vector
44
+ raise ArgumentException, "To create a DirectionVector, the Array must contain at least 2 elements, one for bearing and oen for distance" if !(size >= 2)
45
+ raise ArgumentException, "The second element in the Array must be a Symbol defining the direction of the vector" if !self[1].kind_of?(Symbol)
46
+ DirectionVector.new self[0], self[1]
47
+ end
48
+ alias_method :direction_vector, :d_vector
49
+
50
+ def to_point
51
+ geo_point
52
+ end
53
+ end
54
+
@@ -0,0 +1,59 @@
1
+ require 'geo_vectors/geo_vector'
2
+
3
+ class DirectionVector < GeoVector
4
+ include GeoDistance::Extract
5
+ include GeoDirection
6
+
7
+ attr_reader :direction # direction symbol :N, :S, :SW, etc.
8
+ attr_reader :distance # GeoDistance object
9
+
10
+ def initialize dist, dir
11
+ dist, dir = [dir, dist] if dir.kind_of? GeoDistance
12
+ @distance = extract_distance dist
13
+ @direction = get_valid_direction dir
14
+ end
15
+
16
+ def random_vector
17
+ DirectionVector.new distance.random, random_direction
18
+ end
19
+
20
+ def direction= dir
21
+ @direction = get_valid_direction dir
22
+ end
23
+
24
+ def distance= dist
25
+ @distance = extract_distance dist
26
+ end
27
+
28
+ def scale! scale
29
+ @distance *= scale
30
+ end
31
+
32
+ def unit
33
+ distance.unit
34
+ end
35
+
36
+ def as_bearing_vector
37
+ to_bearing_vector direction, distance
38
+ end
39
+
40
+ def as_point_vector
41
+ to_point_vector direction, distance
42
+ end
43
+
44
+ # return new point from adding vector to point
45
+ def add_to_point point
46
+ vec = as_bearing_vector
47
+ point.destination_point vec.bearing, vec.distance.in_kms
48
+ end
49
+
50
+ # add vector directly to point (destructive update)
51
+ def add_to_point! point
52
+ vec = as_bearing_vector
53
+ dest = point.destination_point vec.bearing, vec.distance.in_kms
54
+ point.lat = dest.lat
55
+ point.lng = dest.lng
56
+ point
57
+ end
58
+
59
+ end
@@ -0,0 +1,33 @@
1
+ class GeoPoint
2
+ include GeoCalc
3
+
4
+ def + *args
5
+ self.dup.add! *args
6
+ end
7
+ alias_method :add, :+
8
+ alias_method :<<, :+
9
+
10
+ def add! *args
11
+ vec = GeoVector::Parser.create_vector *args
12
+ vec.add_to_point! self
13
+ end
14
+
15
+ def - *args
16
+ self.dup.sub! *args
17
+ end
18
+ alias_method :sub, :-
19
+
20
+ def sub! *args
21
+ vec = GeoVector::Parser.create_vector *args
22
+ vec.sub_from_point! self
23
+ end
24
+
25
+ def vector
26
+ to_lat_lng.vector
27
+ end
28
+
29
+ def geo_point
30
+ self
31
+ end
32
+
33
+ end
@@ -0,0 +1,56 @@
1
+ require 'geo_calc'
2
+ require 'geo_vectors/core_ext'
3
+ require 'geo_vectors/util'
4
+
5
+ # A Vector consists of info that can transform a point into a destination point given some operation on that info
6
+ class GeoVector
7
+ attr_reader :unit
8
+
9
+ def initialize
10
+ @unit = :degrees
11
+ end
12
+
13
+ def add! vector
14
+ raise '#add! method must be implemented by including class'
15
+ end
16
+
17
+ def scale! scalar
18
+ raise '#scale! method must be implemented by including class'
19
+ end
20
+
21
+ def direction
22
+ raise '#distance method must be implemented by including class'
23
+ end
24
+
25
+ def distance
26
+ raise '#distance method must be implemented by including class'
27
+ end
28
+
29
+ def add vector
30
+ self.dup.add! vector
31
+ end
32
+
33
+ alias_method :<<, :add
34
+ alias_method :+, :add
35
+
36
+ def sub! vector
37
+ add! vector.reverse
38
+ end
39
+
40
+ def sub vector
41
+ self.dup.sub vector
42
+ end
43
+ alias_method :-, :sub
44
+
45
+ def scale scalar
46
+ self.dup.scale! scalar
47
+ end
48
+ alias_method :*, :scale
49
+ alias_method :enhance, :*
50
+ alias_method :grow_by, :*
51
+
52
+ def / scalar
53
+ scale (1.0 / scalar)
54
+ end
55
+ alias_method :reduce, :/
56
+ end
@@ -0,0 +1,92 @@
1
+ # Operations that apply on a collection of GeoVector
2
+ class GeoVectors < Array
3
+ include Enumerable
4
+ # include GeoVector
5
+ attr_reader :vectors
6
+
7
+ def initialize *vectors
8
+ self.vectors = vectors
9
+ end
10
+
11
+ def vectors= *vectors
12
+ vectors = vectors.flatten
13
+ raise ArgumentError, "GeoVectors can only contain GeoVector instances" if !vectors.only_kinds_of? GeoVector
14
+ @vectors = vectors
15
+ end
16
+
17
+ # iterate each and call direction
18
+ def directions
19
+ vectors.map {|v| v.direction }
20
+ end
21
+
22
+ def distances
23
+ vectors.map {|v| v.distance }
24
+ end
25
+
26
+ def each &block
27
+ vectors.each { |v| yield v }
28
+ end
29
+
30
+ # return new point from adding vector to point
31
+ def add_to_point point
32
+ add_to_point! point.dup
33
+ end
34
+
35
+ # add vector directly to point (destructive update)
36
+ def add_to_point! point
37
+ vectors.each do |v|
38
+ point.add! v
39
+ end
40
+ point
41
+ end
42
+
43
+ def sub_from_point point
44
+ reverse_directions.add_to_point point
45
+ end
46
+
47
+ def sub_from_point! point
48
+ reverse_directions.add_to_point! point
49
+ end
50
+
51
+ def scale! scalar
52
+ each {|v| v.scale! scalar }
53
+ self
54
+ end
55
+
56
+ def scale scalar
57
+ self.dup.scale! scalar
58
+ end
59
+ alias_method :*, :scale
60
+ alias_method :enhance, :*
61
+ alias_method :grow_by, :*
62
+
63
+ def / scalar
64
+ scale (1.0 / scalar)
65
+ end
66
+ alias_method :reduce, :/
67
+
68
+
69
+ def reverse_directions
70
+ vectors.each do |v|
71
+ v.reverse!
72
+ end
73
+ self
74
+ end
75
+
76
+ def to_s
77
+ vectors.inject([]) do |res, v|
78
+ res << v.to_s
79
+ res
80
+ end.join(' ; ')
81
+ end
82
+
83
+ protected
84
+
85
+ def check_valid!
86
+ raise "Only works for an Array of GeoVector" if !valid?
87
+ end
88
+
89
+ def valid?
90
+ kind_of?(Array) && only_kinds_of?(GeoVector)
91
+ end
92
+ end
@@ -0,0 +1,85 @@
1
+ require 'geo_vectors/geo_vector'
2
+ require 'sugar-high/kind_of'
3
+ require 'proxy_party'
4
+ require 'geo_vectors/point_vector/vector_ops'
5
+ require 'geo_vectors/point_vector/point_ops'
6
+
7
+ class PointVector < GeoVector
8
+ include GeoCalc
9
+ include VectorOps
10
+ include PointOps
11
+
12
+ attr_accessor :point
13
+ proxy :point
14
+
15
+ def initialize *args
16
+ if args[0].kind_of?(GeoPoint) && args.size == 1
17
+ @point = args[0]
18
+ else
19
+ args = normalize_points(args[0], args[1]) if args.only_kinds_of?(GeoPoint) && args.size == 2
20
+ @point = GeoPoint.new *args
21
+ end
22
+ end
23
+
24
+ def direction
25
+ brng = bearing
26
+ begin
27
+ bearing_to_dir brng
28
+ rescue
29
+ brng
30
+ end
31
+ end
32
+
33
+ def bearing
34
+ origin.bearing_to(point).to_precision(2).to_f
35
+ end
36
+
37
+ def point= *args
38
+ @point = GeoPoint.new *args
39
+ end
40
+
41
+ def length unit = :kms
42
+ origin.distance_to(point)
43
+ end
44
+
45
+ def distance unit = :kms
46
+ origin.distance_to(point).send unit
47
+ end
48
+
49
+ def x; lng; end
50
+ def y; lat; end
51
+
52
+ def scale! scalar
53
+ self.lat = self.lat * scalar
54
+ self.lng = self.lng * scalar
55
+ self
56
+ end
57
+
58
+ def reverse
59
+ self.dup.reverse!
60
+ end
61
+
62
+ def reverse!
63
+ self.point.reverse_point!
64
+ self
65
+ end
66
+
67
+ protected
68
+
69
+ def normalize_points p1, p2
70
+ lng, lat = [ delta_lat(p1, p2), delta_lng(p1, p2) ]
71
+ end
72
+
73
+ def delta_lat p1, p2
74
+ p2.lat - p1.lat
75
+ end
76
+
77
+ def delta_lng p1, p2
78
+ p2.lng - p1.lng
79
+ end
80
+
81
+ def origin
82
+ [0, 0].geo_point
83
+ end
84
+ end
85
+