geo_vectors 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.rspec +1 -0
- data/Distance calc notes.txt +64 -0
- data/Gemfile +14 -0
- data/LICENSE.txt +20 -0
- data/README.textile +178 -0
- data/Rakefile +53 -0
- data/VERSION +1 -0
- data/geo_vectors.gemspec +115 -0
- data/lib/geo_vectors.rb +7 -0
- data/lib/geo_vectors/bearing_vector.rb +87 -0
- data/lib/geo_vectors/core_ext.rb +54 -0
- data/lib/geo_vectors/direction_vector.rb +59 -0
- data/lib/geo_vectors/geo_point.rb +33 -0
- data/lib/geo_vectors/geo_vector.rb +56 -0
- data/lib/geo_vectors/geo_vectors.rb +92 -0
- data/lib/geo_vectors/point_vector.rb +85 -0
- data/lib/geo_vectors/point_vector/point_ops.rb +23 -0
- data/lib/geo_vectors/point_vector/vector_ops.rb +38 -0
- data/lib/geo_vectors/util.rb +3 -0
- data/lib/geo_vectors/util/calc.rb +89 -0
- data/lib/geo_vectors/util/geo_direction.rb +101 -0
- data/lib/geo_vectors/util/geo_distance.rb +135 -0
- data/lib/geo_vectors/util/geo_units.rb +53 -0
- data/lib/geo_vectors/vector_parser.rb +54 -0
- data/spec/geo_vectors/API proposal guide.txt +142 -0
- data/spec/geo_vectors/bearing_vector/add_vector_spec.rb +30 -0
- data/spec/geo_vectors/bearing_vector/random_spec.rb +18 -0
- data/spec/geo_vectors/bearing_vector_spec.rb +34 -0
- data/spec/geo_vectors/direction_vector/add_vector_spec.rb +30 -0
- data/spec/geo_vectors/direction_vector/point_add_spec.rb +0 -0
- data/spec/geo_vectors/direction_vector/random_spec.rb +16 -0
- data/spec/geo_vectors/direction_vector/subtract_vector_spec.rb +0 -0
- data/spec/geo_vectors/direction_vector_spec.rb +27 -0
- data/spec/geo_vectors/geo_vectors_spec.rb +108 -0
- data/spec/geo_vectors/point_vector/add_vector_spec.rb +135 -0
- data/spec/geo_vectors/point_vector/initializer_spec.rb +82 -0
- data/spec/geo_vectors/point_vector/point_add_spec.rb +45 -0
- data/spec/geo_vectors/point_vector/random_spec.rb +17 -0
- data/spec/geo_vectors/point_vector/scale_vector_spec.rb +52 -0
- data/spec/geo_vectors/point_vector/subtract_vector_spec.rb +80 -0
- data/spec/geo_vectors/point_vector_spec.rb +111 -0
- data/spec/geo_vectors/util/geo_direction_spec.rb +74 -0
- data/spec/geo_vectors/util/geo_distance_spec.rb +70 -0
- data/spec/geo_vectors/util/geo_units_spec.rb +23 -0
- data/spec/spec_helper.rb +7 -0
- metadata +218 -0
data/lib/geo_vectors.rb
ADDED
@@ -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
|
+
|