vector_geometry 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/COPYING ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 by Andrew Berkeley (andrew.berkeley.is@googlemail.com)
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
20
+
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Add dependencies to develop your gem here.
4
+ # Include everything needed to run rake, tests, features, etc.
5
+ group :development do
6
+ gem "jeweler", "~> 1.6.4"
7
+ gem 'rspec', '~> 2.6.0'
8
+ gem 'rdoc'
9
+ end
@@ -0,0 +1,153 @@
1
+ Vector geometry
2
+ ========
3
+
4
+ Author: Andrew Berkeley (andrew.berkeley.is@googlemail.com)
5
+
6
+ Introduction
7
+ ------------
8
+ This library provides an interface for handling vector geometry in 2- and 3-dimensional space.
9
+
10
+ The following classes are defined
11
+
12
+ <table>
13
+ <tr>
14
+ <td>Geometry::Vector</td>
15
+ <td> Basic 2- or 3D vector and operations</td>
16
+ </tr>
17
+ <tr>
18
+ <td>Geometry::GeoVector</td>
19
+ <td>Subclass of Vector providing additional geometric operations relating to the surface of a spheroid</td>
20
+ </tr>
21
+ <tr>
22
+ <td>Geometry::EarthVector</td>
23
+ <td>Subclass of GeoVector relating specifically to Earth</td>
24
+ </tr>
25
+ <tr>
26
+ <td>Geometry::Spheroid::Base</td>
27
+ <td>Create a representation of an arbitrary spheriod for associating with a given instance of GeoVector</td>
28
+ </tr>
29
+ </table>
30
+
31
+ For example, calculating the distance between two points on the Earth's surface
32
+
33
+ ```ruby
34
+ # Instantiate using geographic coordinates.
35
+ vector_1 = Geometry::EarthVector.from_geographic(80.0,0.0) #=> <EarthVector [1111.1648732245053, 0.0, 6259.542946575613]>
36
+ vector_2 = Geometry::EarthVector.from_geographic(80.0,1.0) #=> <EarthVector [1110.9956374322653, 19.392500986346672, 6259.542946575613]>
37
+
38
+ # Distance across 1 degree longitude at latitude of 80 degrees (km)
39
+ vector_1.great_circle_distance(vector_2) #=> 19.43475314292549
40
+ ```
41
+
42
+ Or the distance of point from a line described by two points
43
+ ```ruby
44
+ vector_3 = Geometry::EarthVector.from_geographic(75.0,5.0) #=> <EarthVector [1649.6615168294907, 144.3266813775607, 6138.7656676742445]>
45
+
46
+ vector_3.great_circle_distance_from_arc(vector_1,vector_2) #=> 567.3634356328112
47
+
48
+ ```
49
+
50
+ Vector
51
+ ------
52
+
53
+ Basic vector operations can be performed as follows.
54
+
55
+ Initialize a vector.
56
+
57
+ ```ruby
58
+ # 2D - pass x, y
59
+ vector = Geometry::Vector.new(15,20)
60
+
61
+ # 3D - pass x, y, z
62
+ vector = Geometry::Vector.new(15,20,4)
63
+
64
+ # 2D - pass magnitude and angle
65
+ vector = Geometry::Vector.from_polar(25, Math::PI)
66
+ ```
67
+
68
+ Vector attributes.
69
+
70
+ ```ruby
71
+ vector = Geometry::Vector.new(15,20,4)
72
+
73
+ vector.x #=> 15
74
+ vector.y #=> 20
75
+ vector.z #=> 4
76
+
77
+ ```
78
+
79
+ Vector operations.
80
+
81
+ ```ruby
82
+
83
+ vector = Geometry::Vector.new(3,4,5)
84
+
85
+ # Magnitude
86
+ vector.magnitude #=> 7.0710678118654755
87
+
88
+ # Normalize
89
+ vector.normalize #=> <Vector [0.4242640687119285, 0.565685424949238, 0.7071067811865475]>
90
+
91
+ vector_1 = Geometry::Vector.new(2,2,1)
92
+ vector_2 = Geometry::Vector.new(2,3,10)
93
+
94
+ # Addition
95
+ vector_1 + vector_2 #=> <Vector [4.0, 5.0, 11.0]>
96
+
97
+ # Subtraction
98
+ vector_1 - vector_2 #=> <Vector [0.0, -1.0, -9.0]>
99
+
100
+ # Multiply by scalar value
101
+ vector_1 * 4 #=> <Vector [8.0, 8.0, 4.0]>
102
+
103
+ # Divide by scalar value
104
+ vector * 4 #=> <Vector [0.5, 0.5, 0.25]>
105
+
106
+ # Calculate dot product of 2 vectors
107
+ vector_1.dot(vector_2) #=> 20.0
108
+
109
+ # Calculate cross product of 2 vectors
110
+ vector_1.cross(vector_2) #=> <Vector [17.0, -18.0, 2.0]>
111
+
112
+ # Calculate angle between 2 vectors (in radians)
113
+ vector_1.angle(vector_2) #=> 0.8929110789963546
114
+
115
+ ```
116
+
117
+ Compare vectors.
118
+
119
+ ```ruby
120
+
121
+ # Are two vectors parallel or orthogonal?
122
+ vector_1 = Geometry::Vector.new(10,0,0)
123
+ vector_2 = Geometry::Vector.new(20,0,0)
124
+ vector_3 = Geometry::Vector.new(0,10,0)
125
+
126
+ vector_1.parallel?(vector_2) #=> true
127
+ vector_1.parallel?(vector_3) #=> false
128
+
129
+ vector_1.orthogonal?(vector_2) #=> false
130
+ vector_1.orthogonal?(vector_3) #=> true
131
+
132
+ # Are two vectors equal
133
+ vector_1 == vector_2 #=> false
134
+ ```
135
+
136
+ Contributing
137
+ ------------
138
+
139
+ If you find a bug or think that you improve on the code, feel free to contribute.
140
+
141
+ You can:
142
+
143
+ * Send the author a message ("andrew.berkeley.is@googlemail.com":mailto:andrew.berkeley.is@googlemail.com)
144
+ * Create an issue
145
+ * Fork the project and submit a pull request.
146
+
147
+
148
+ License
149
+ -------
150
+
151
+ © Copyright 2012 Andrew Berkeley.
152
+
153
+ Licensed under the MIT license (See COPYING file for details)
@@ -0,0 +1,47 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+
6
+ begin
7
+ Bundler.setup(:default, :development)
8
+ rescue Bundler::BundlerError => e
9
+ $stderr.puts e.message
10
+ $stderr.puts "Run `bundle install` to install missing gems"
11
+ exit e.status_code
12
+ end
13
+
14
+ require 'rake'
15
+ require 'rspec'
16
+ require 'rspec/core/rake_task'
17
+
18
+ task :default => [:spec]
19
+
20
+ desc "Run specs"
21
+ RSpec::Core::RakeTask.new do |t|
22
+ # Put spec opts in a file named .rspec in root
23
+ end
24
+
25
+ require 'jeweler'
26
+
27
+ Jeweler::Tasks.new do |gem|
28
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
29
+ gem.name = "vector_geometry"
30
+ gem.homepage = "https://github.com/spatchcock/vector_geometry"
31
+ gem.license = "GNU Affero General Public License"
32
+ gem.summary = %Q{Cartesian and spherical geometry using vectors}
33
+ gem.description = %Q{Cartesian and spherical geometry using vectors}
34
+ gem.email = "andrew.berkeley.is@googlemail.com"
35
+ gem.authors = ["Andrew Berkeley"]
36
+ # dependencies defined in Gemfile
37
+ end
38
+
39
+ Jeweler::RubygemsDotOrgTasks.new
40
+
41
+ require 'rdoc/task'
42
+ RDoc::Task.new do |rd|
43
+ rd.title = "Vector Geometry"
44
+ rd.rdoc_dir = 'doc'
45
+ rd.main = "README"
46
+ rd.rdoc_files.include("README", "lib/**/*.rb")
47
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
@@ -0,0 +1,47 @@
1
+ module Geometry
2
+
3
+ def self.deg_to_rad(deg)
4
+ deg * Math::PI / 180.0
5
+ end
6
+
7
+ def self.rad_to_deg(rad)
8
+ (rad * 180.0) / Math::PI
9
+ end
10
+
11
+ def self.haversine_distance(point_1,point_2,radius, options = {})
12
+ lat_1 = point_1[0]
13
+ lng_1 = point_1[1]
14
+ lat_2 = point_2[0]
15
+ lng_2 = point_2[1]
16
+
17
+ if options[:unit] = :deg
18
+ lat_1 = deg_to_rad(lat_1)
19
+ lng_1 = deg_to_rad(lng_1)
20
+ lat_2 = deg_to_rad(lat_2)
21
+ lng_2 = deg_to_rad(lng_2)
22
+ end
23
+
24
+ lat_diff = 0.5 * (lat_2 - lat_1);
25
+ lat_diff = Math.sin(lat_diff);
26
+ lat_diff = lat_diff * lat_diff;
27
+
28
+ lng_diff = 0.5 * (lng_2 - lng_1);
29
+ lng_diff = Math.sin(lng_diff);
30
+ lng_diff = lng_diff * lng_diff;
31
+
32
+ result = lat_diff;
33
+ result += Math.cos(lat_1) * Math.cos(lat_2) * lng_diff;
34
+ result = Math.sqrt(result);
35
+
36
+ return radius * 2 * Math.asin(result);
37
+
38
+ end
39
+
40
+ def self.pythagorean_distance(point_1, point_2)
41
+ x = point_2[0] - point_1[0]
42
+ y = point_2[1] - point_1[1]
43
+
44
+ Math.sqrt(x * x + y * y).abs
45
+ end
46
+
47
+ end
@@ -0,0 +1,106 @@
1
+ module Geometry
2
+
3
+ module Spheroid
4
+
5
+ # Ellipsoid - 3D analogue of an ellipse. Has 3 axis: a,b and c
6
+ # Spheroid - Ellipsoid with two equal semi-diameters (a = b)
7
+ # Sphere - Ellipsoid with three equal semi-diameters (a = b = c)
8
+ # Geoid -
9
+
10
+ # class Ellipsoid
11
+
12
+ # def initialize(a,b,c)
13
+ # @a_radius = a
14
+ # @b_radius = b
15
+ # @c_radius = c
16
+ # end
17
+
18
+ # def volume
19
+ # (4.0/3.0) * (Math::PI * a_radius * b_radius * c_radius)
20
+ # end
21
+
22
+ # end
23
+
24
+ class Base
25
+
26
+ attr_accessor :polar_radius # semi-minor axis
27
+ attr_accessor :equatorial_radius # semi-major axis
28
+ attr_accessor :unit
29
+
30
+ # Unit is not functional but is simply memoized so that the basis for any calculations
31
+ # is clear
32
+ def initialize(equatorial_radius, polar_radius, unit = :km)
33
+ @polar_radius = polar_radius
34
+ @equatorial_radius = equatorial_radius
35
+ @unit = unit
36
+ end
37
+
38
+ def mean_radius
39
+ # http://en.wikipedia.org/wiki/Earth_radius
40
+ @mean_radius ||= (@polar_radius + 2 * @equatorial_radius) / 3.0
41
+ end
42
+
43
+ def flattening
44
+ # http://en.wikipedia.org/wiki/Reference_ellipsoid
45
+ @flattening ||= (@equatorial_radius - @polar_radius) / @equatorial_radius
46
+ end
47
+
48
+ def inverse_flattening
49
+ @inverse_flattening ||= 1.0 / flattening
50
+ end
51
+
52
+ def flattening_complement_squared
53
+ # http://www.mathworks.co.uk/help/aeroblks/geodetictogeocentriclatitude.html
54
+ @flattening_complement_squared ||= (1.0 - flattening) * (1.0 - flattening)
55
+ end
56
+
57
+ def volume
58
+ @volume ||= (4.0 / 3.0) * (Math::PI * @polar_radius * @equatorial_radius ** 2)
59
+ end
60
+
61
+ # http://en.wikipedia.org/wiki/Earth_radius
62
+ def radius_at_geodetic_latitude(lat, options = {})
63
+ lat = Geometry.deg_to_rad(lat) unless options[:unit] == :radians
64
+
65
+ numerator = (@equatorial_radius**2 * Math.cos(lat))**2 + (@polar_radius**2 * Math.sin(lat))**2
66
+ denominator = (@equatorial_radius * Math.cos(lat))**2 + (@polar_radius * Math.sin(lat))**2
67
+
68
+ Math.sqrt(numerator/denominator)
69
+ end
70
+
71
+ def geodetic_to_geocentric_latitude(lat, options = {})
72
+ lat = Geometry.deg_to_rad(lat) unless options[:unit] == :radians
73
+
74
+ Math.atan(Math.tan(lat) * flattening_complement_squared)
75
+ end
76
+
77
+ # Invoke the haversine formula in the context of the spheroid represented by self
78
+ def haversine_distance(point_1, point_2, options = {})
79
+ Geometry.haversine_distance(point_1, point_2, mean_radius, options)
80
+ end
81
+
82
+ end
83
+
84
+ Mercury = Geometry::Spheroid::Base.new(2439.7, 2439.7)
85
+
86
+ Venus = Geometry::Spheroid::Base.new(6051.8, 6051.8)
87
+
88
+ Earth = Geometry::Spheroid::Base.new(6378.1370, 6356.7523)
89
+
90
+ Moon = Geometry::Spheroid::Base.new(1738.1, 1736.0)
91
+
92
+ Mars = Geometry::Spheroid::Base.new(3396.2, 3376.2)
93
+
94
+ Jupiter = Geometry::Spheroid::Base.new(71492, 66854)
95
+
96
+ Saturn = Geometry::Spheroid::Base.new(60268, 54364)
97
+
98
+ Uranus = Geometry::Spheroid::Base.new(25559, 24973)
99
+
100
+ Neptune = Geometry::Spheroid::Base.new(24764, 24341)
101
+
102
+ Pluto = Geometry::Spheroid::Base.new(1195, 1195)
103
+
104
+ end
105
+
106
+ end
@@ -0,0 +1,29 @@
1
+ module Geometry
2
+
3
+ module Spheroid
4
+
5
+ class Sphere < Spheroid::Base
6
+
7
+ def initialize(radius, unit = :km)
8
+ super(radius,radius,unit)
9
+ end
10
+
11
+ def radius
12
+ @equatorial_radius
13
+ end
14
+
15
+ def diameter
16
+ @diameter ||= radius * 2.0
17
+ end
18
+
19
+ def circumference
20
+ @circumference ||= 2.0 * Math::PI * radius
21
+ end
22
+
23
+ def surface_area
24
+ @surface_area ||= 4.0 * (Math::PI * radius ** 2)
25
+ end
26
+
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,7 @@
1
+ module Geometry
2
+
3
+ class EarthVector < GeoVector
4
+ @@spheroid = Geometry::Spheroid::Earth
5
+ end
6
+
7
+ end
@@ -0,0 +1,234 @@
1
+ module Geometry
2
+
3
+ class GeoVector < Vector
4
+
5
+ @@spheroid = nil
6
+
7
+ def self.from_great_circle_intersection(vector_1,vector_2,vector_3,vector_4)
8
+ normal_to_great_circle_1 = vector_1.cross_normal(vector_2)
9
+ normal_to_great_circle_2 = vector_3.cross_normal(vector_4)
10
+
11
+ unit_vector = normal_to_great_circle_1.cross_normal(normal_to_great_circle_2)
12
+
13
+ if @@spheroid
14
+ unit_vector.scale(@@spheroid.mean_radius)
15
+ else
16
+ unit_vector
17
+ end
18
+ end
19
+
20
+ # Return a 3-dimensional cartesian vector representing the given latitude and longitude.
21
+ def self.from_geographic(lat, lng, options = {})
22
+
23
+ spheroid = @@spheroid || options[:spheroid]
24
+
25
+ unless options[:unit] == :radians
26
+ lat = Geometry.deg_to_rad(lat)
27
+ lng = Geometry.deg_to_rad(lng)
28
+ end
29
+
30
+ # Convert the geodetic latitude to geocentric if required
31
+ geocentric_latitude = options[:geocentric] ? lat : spheroid.geodetic_to_geocentric_latitude(lat, :unit => :radians)
32
+
33
+ # Find the projection of the point on the equatorial plane.
34
+ equatorial_plane_projection = Math.cos(geocentric_latitude)
35
+
36
+ x = Math.cos(lng) * equatorial_plane_projection
37
+ y = Math.sin(lng) * equatorial_plane_projection
38
+ z = Math.sin(geocentric_latitude)
39
+
40
+ unit_vector = self.new(x,y,z)
41
+
42
+ if options[:unit_vector]
43
+ unit_vector
44
+ else
45
+ raise ArgumentError, "No spheroid defined" unless spheroid
46
+
47
+ geodetic_radius = spheroid.radius_at_geodetic_latitude(lat, :unit => :radians)
48
+ unit_vector.scale(geodetic_radius)
49
+ end
50
+ end
51
+
52
+ attr_accessor :geodetic_radius
53
+ attr_accessor :latitude
54
+ attr_accessor :longitude
55
+
56
+ def latitude(options = {})
57
+ @latitude ||= begin
58
+ lat = Math.atan2(@z,@x)
59
+
60
+ lat = Math::PI - lat if lat > Math::PI/2.0
61
+ lat = lat.abs if lat == -0.0
62
+
63
+ lat
64
+ end
65
+
66
+ options[:unit] == :radians ? @latitude : Geometry.rad_to_deg(@latitude)
67
+ end
68
+
69
+ def longitude(options = {})
70
+ @longitude ||= Math.atan2(@y,@x)
71
+
72
+ options[:unit] == :deg ? Geometry.rad_to_deg(@longitude) : @longitude
73
+ end
74
+
75
+ def geodetic_radius(spheroid = @@spheroid)
76
+ @geodetic_radius ||= spheroid.radius_at_geodetic_latitude(latitude)
77
+ end
78
+
79
+ def antipode
80
+ scale(-1.0)
81
+ end
82
+
83
+ def mean_geodetic_radius(other)
84
+ (self.geodetic_radius + other.geodetic_radius) / 2.0
85
+ end
86
+
87
+ def great_circle_distance(other, options = {})
88
+
89
+ angular_distance = angle(other)
90
+
91
+ if @@spheroid && !options[:unit_vector]
92
+ if options[:use_mean_geodetic_radius]
93
+ return angular_distance * mean_geodetic_radius(other)
94
+ else
95
+ return angular_distance * @@spheroid.mean_radius
96
+ end
97
+ else
98
+ angular_distance
99
+ end
100
+ end
101
+
102
+ # Calculate the distance of self from a great circle defined by two points.
103
+ def great_circle_distance_from_great_circle(point_a,point_b)
104
+
105
+ # The shortest distance from a great circle is the perpendicular distance.
106
+
107
+ # Find the vector which is normal to the plane described by the (curved) line together
108
+ # with the origin.
109
+
110
+ normal_to_line = point_a.cross_normal(point_b).scale(@@spheroid.mean_radius)
111
+
112
+ # The line between self and the normal vector is perpendicular to the line.
113
+ #
114
+ # Next, find the intersection between the two lines which represents the shortest distance
115
+ # from self to the line.
116
+
117
+ intersection = GeoVector.from_great_circle_intersection(point_a, point_b, self, normal_to_line)
118
+
119
+ # There are actually two valid intersections (which are antipodes of one another) and we have
120
+ # found one.
121
+ #
122
+ # Return the smallest distance from self to either intersection
123
+
124
+ [self.great_circle_distance(intersection), self.great_circle_distance(intersection.antipode)].min
125
+ end
126
+
127
+ # Calculate the distance of self from a finite line segment defined by two points
128
+ def great_circle_distance_from_arc(point_a,point_b, options = {})
129
+
130
+ # Distance from a line segment is similar to the distance from the infinte line (above)
131
+ # with a modification.
132
+ #
133
+ # The distance from an infinitely long line calculates the shortest distance to an infintitely
134
+ # long line. This is the perpendicular distance. In the case of the line segment, this perpendicular
135
+ # may or may not fall on the line segment part of the more general infinte line.
136
+ #
137
+ # If it does, we can keep the perpendicular distance. If not, the shortest distance will be
138
+ # to either of the two line segments end. Determine which.
139
+
140
+ normal_to_line = point_a.cross_normal(point_b).scale(@@spheroid.mean_radius)
141
+ intersection = GeoVector.from_great_circle_intersection(point_a, point_b, self, normal_to_line)
142
+
143
+ # The point which intersects the two great circles is actually one of two such unique points. We
144
+ # know both fall on the great circle described by the points but we need to establish whether either
145
+ # fall on our line segment, i.e. between them. If so, we can take the perpendicular distance from
146
+ # the intersection point.
147
+
148
+ line_length = point_a.great_circle_distance(point_b, options)
149
+
150
+ if line_length >= intersection.great_circle_distance(point_a, options) &&
151
+ line_length >= intersection.great_circle_distance(point_b, options)
152
+
153
+ # The intersection falls on the line segment.
154
+ # Calculate the perpendicular distance.
155
+
156
+ return self.great_circle_distance(intersection, options)
157
+
158
+ elsif line_length >= intersection.antipode.great_circle_distance(point_a, options) &&
159
+ line_length >= intersection.antipode.great_circle_distance(point_b, options)
160
+
161
+ # The *other* intersection falls on the line segment.
162
+ # Calculate the perpendicular distance.
163
+
164
+ return self.great_circle_distance(intersection.antipode, options)
165
+
166
+ else
167
+
168
+ # Neither intersection falls on the line segment.
169
+ # Calculate the distance to the closer of the line segment ends.
170
+
171
+ return [ self.great_circle_distance(point_a, options), self.great_circle_distance(point_b, options) ].min
172
+ end
173
+ end
174
+
175
+ # Supports a polyline based on either cartesian or angular coordinates. Specify which using the :basis
176
+ # option
177
+ #
178
+ # :cartesian => cartesian coordinates (x,y)
179
+ #
180
+ # otherwise lat/lng pairs are assumed
181
+ #
182
+ def great_circle_distance_from_polyline(polyline, options = {})
183
+
184
+ constructor = Proc.new do |vertex|
185
+ if options[:basis] == :cartesian
186
+ self.class.new(vertex[0], vertex[1], vertex[2])
187
+ else
188
+ self.class.from_geographic(vertex[0], vertex[1], options)
189
+ end
190
+ end
191
+
192
+ # memoize the last processed point as both array and vector objects
193
+ last_array = polyline.first
194
+ last_vector = constructor.call(last_array)
195
+
196
+ minimum_distance = 999999999999
197
+
198
+ for vertex in polyline[1..-1]
199
+
200
+ next if vertex == last_array
201
+
202
+ start_vector = last_vector
203
+ end_vector = constructor.call(vertex)
204
+
205
+ this_segment_distance = great_circle_distance_from_arc(start_vector, end_vector, options)
206
+
207
+ if(this_segment_distance < minimum_distance)
208
+ minimum_distance = this_segment_distance
209
+ end
210
+
211
+ last_array = vertex
212
+ last_vector = end_vector
213
+ end
214
+
215
+ return minimum_distance
216
+ end
217
+
218
+ # Convert self to a geographic lat/lng pair.
219
+ def to_geographic(options = {})
220
+ [latitude(options), longitude(options)]
221
+ end
222
+
223
+ protected
224
+
225
+ # Return true of point is closer to both points than the points are to each other
226
+ def within_both_radii?(point_a,point_b)
227
+ line_length = point_a.great_circle_distance(point_b)
228
+ self.great_circle_distance(point_a) < line_length && self.great_circle_distance(point_b) < line_length
229
+ end
230
+
231
+ end
232
+
233
+ end
234
+