Class: Geometry::GeoVector

Inherits:
Vector
  • Object
show all
Defined in:
lib/geometry/vector/geo_vector.rb

Direct Known Subclasses

EarthVector

Constant Summary collapse

@@spheroid =
nil

Instance Attribute Summary collapse

Attributes inherited from Vector

#x, #y, #z

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Vector

#==, #add, #angle, #cross, #cross_length, #cross_normal, #distance, #distance_from_line, #distance_from_line_segment, #distance_from_polyline, #divide, #dot, from_polar, #heading, #initialize, #inspect, #magnitude, #multiply, #normalize, #orthogonal?, #parallel?, #scale, #subtract

Constructor Details

This class inherits a constructor from Geometry::Vector

Instance Attribute Details

#geodetic_radius(spheroid = @@spheroid) ⇒ Object

Returns the value of attribute geodetic_radius.



52
53
54
# File 'lib/geometry/vector/geo_vector.rb', line 52

def geodetic_radius
  @geodetic_radius
end

#latitude(options = {}) ⇒ Object

Returns the value of attribute latitude.



53
54
55
# File 'lib/geometry/vector/geo_vector.rb', line 53

def latitude
  @latitude
end

#longitude(options = {}) ⇒ Object

Returns the value of attribute longitude.



54
55
56
# File 'lib/geometry/vector/geo_vector.rb', line 54

def longitude
  @longitude
end

Class Method Details

.from_geographic(lat, lng, options = {}) ⇒ Object

Return a 3-dimensional cartesian vector representing the given latitude and longitude.



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/geometry/vector/geo_vector.rb', line 21

def self.from_geographic(lat, lng, options = {})

  spheroid = @@spheroid || options[:spheroid]
  
  unless options[:unit] == :radians
    lat = Geometry.deg_to_rad(lat)
    lng = Geometry.deg_to_rad(lng)
  end

  # Convert the geodetic latitude to geocentric if required
  geocentric_latitude = options[:geocentric] ? lat : spheroid.geodetic_to_geocentric_latitude(lat, :unit => :radians)
  
  # Find the projection of the point on the equatorial plane.
  equatorial_plane_projection = Math.cos(geocentric_latitude)

  x = Math.cos(lng) * equatorial_plane_projection
  y = Math.sin(lng) * equatorial_plane_projection
  z = Math.sin(geocentric_latitude)

  unit_vector = self.new(x,y,z)

  if options[:unit_vector]
    unit_vector
  else
    raise ArgumentError, "No spheroid defined" unless spheroid

    geodetic_radius = spheroid.radius_at_geodetic_latitude(lat, :unit => :radians)
    unit_vector.scale(geodetic_radius)
  end
end

.from_great_circle_intersection(vector_1, vector_2, vector_3, vector_4) ⇒ Object



7
8
9
10
11
12
13
14
15
16
17
18
# File 'lib/geometry/vector/geo_vector.rb', line 7

def self.from_great_circle_intersection(vector_1,vector_2,vector_3,vector_4)
  normal_to_great_circle_1 = vector_1.cross_normal(vector_2)
  normal_to_great_circle_2 = vector_3.cross_normal(vector_4)

  unit_vector = normal_to_great_circle_1.cross_normal(normal_to_great_circle_2)

  if @@spheroid
    unit_vector.scale(@@spheroid.mean_radius)
  else
    unit_vector
  end
end

Instance Method Details

#antipodeObject



79
80
81
# File 'lib/geometry/vector/geo_vector.rb', line 79

def antipode
  scale(-1.0)
end

#great_circle_distance(other, options = {}) ⇒ Object



87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/geometry/vector/geo_vector.rb', line 87

def great_circle_distance(other, options = {})
  
  angular_distance = angle(other) 

  if @@spheroid && !options[:unit_vector]
    if options[:use_mean_geodetic_radius]
      return angular_distance * mean_geodetic_radius(other) 
    else
      return angular_distance * @@spheroid.mean_radius
    end
  else
    angular_distance
  end
end

#great_circle_distance_from_arc(point_a, point_b, options = {}) ⇒ Object

Calculate the distance of self from a finite line segment defined by two points



128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/geometry/vector/geo_vector.rb', line 128

def great_circle_distance_from_arc(point_a,point_b, options = {})

  # Distance from a line segment is similar to the distance from the infinte line (above)
  # with a modification.
  #
  # The distance from an infinitely long line calculates the shortest distance to an infintitely
  # long line. This is the perpendicular distance. In the case of the line segment, this perpendicular
  # may or may not fall on the line segment part of the more general infinte line.
  # 
  # If it does, we can keep the perpendicular distance. If not, the shortest distance will be
  # to either of the two line segments end. Determine which.

  normal_to_line = point_a.cross_normal(point_b).scale(@@spheroid.mean_radius)
  intersection   = GeoVector.from_great_circle_intersection(point_a, point_b, self, normal_to_line)

  # The point which intersects the two great circles is actually one of two such unique points. We
  # know both fall on the great circle described by the points but we need to establish whether either
  # fall on our line segment, i.e. between them. If so, we can take the perpendicular distance from 
  # the intersection point.

  line_length = point_a.great_circle_distance(point_b, options)

  if line_length >= intersection.great_circle_distance(point_a, options) && 
     line_length >= intersection.great_circle_distance(point_b, options)

    # The intersection falls on the line segment.
    # Calculate the perpendicular distance.
    
    return self.great_circle_distance(intersection, options)

  elsif line_length >= intersection.antipode.great_circle_distance(point_a, options) && 
        line_length >= intersection.antipode.great_circle_distance(point_b, options)

    # The *other* intersection falls on the line segment.
    # Calculate the perpendicular distance.

    return self.great_circle_distance(intersection.antipode, options)

  else

    # Neither intersection falls on the line segment.
    # Calculate the distance to the closer of the line segment ends.

    return [ self.great_circle_distance(point_a, options), self.great_circle_distance(point_b, options) ].min
  end
end

#great_circle_distance_from_great_circle(point_a, point_b) ⇒ Object

Calculate the distance of self from a great circle defined by two points.



103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/geometry/vector/geo_vector.rb', line 103

def great_circle_distance_from_great_circle(point_a,point_b)

  # The shortest distance from a great circle is the perpendicular distance. 

  # Find the vector which is normal to the plane described by the (curved) line together
  # with the origin.

  normal_to_line = point_a.cross_normal(point_b).scale(@@spheroid.mean_radius)

  # The line between self and the normal vector is perpendicular to the line.
  #
  # Next, find the intersection between the two lines which represents the shortest distance
  # from self to the line.

  intersection = GeoVector.from_great_circle_intersection(point_a, point_b, self, normal_to_line)

  # There are actually two valid intersections (which are antipodes of one another) and we have
  # found one. 
  #
  # Return the smallest distance from self to either intersection

  [self.great_circle_distance(intersection), self.great_circle_distance(intersection.antipode)].min      
end

#great_circle_distance_from_polyline(polyline, options = {}) ⇒ Object

Supports a polyline based on either cartesian or angular coordinates. Specify which using the :basis option

:cartesian => cartesian coordinates (x,y)

otherwise lat/lng pairs are assumed



182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
# File 'lib/geometry/vector/geo_vector.rb', line 182

def great_circle_distance_from_polyline(polyline, options = {})

  constructor = Proc.new do |vertex|
    if options[:basis] == :cartesian
      self.class.new(vertex[0], vertex[1], vertex[2])
    else
      self.class.from_geographic(vertex[0], vertex[1], options)          
    end
  end
  
  # memoize the last processed point as both array and vector objects
  last_array  = polyline.first
  last_vector = constructor.call(last_array)

  minimum_distance = 999999999999

  for vertex in polyline[1..-1]  

    next if vertex == last_array  

    start_vector = last_vector
    end_vector   = constructor.call(vertex)

    this_segment_distance = great_circle_distance_from_arc(start_vector, end_vector, options)

    if(this_segment_distance < minimum_distance)
      minimum_distance = this_segment_distance
    end

    last_array  = vertex
    last_vector = end_vector
  end

  return minimum_distance
end

#mean_geodetic_radius(other) ⇒ Object



83
84
85
# File 'lib/geometry/vector/geo_vector.rb', line 83

def mean_geodetic_radius(other)
  (self.geodetic_radius + other.geodetic_radius) / 2.0
end

#to_geographic(options = {}) ⇒ Object

Convert self to a geographic lat/lng pair.



219
220
221
# File 'lib/geometry/vector/geo_vector.rb', line 219

def to_geographic(options = {})
  [latitude(options), longitude(options)]
end