Class: RGeo::Cartesian::Segment

Inherits:
Object
  • Object
show all
Defined in:
lib/rgeo/cartesian/calculations.rb

Overview

Represents a line segment in the plane.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(start, stop) ⇒ Segment

:nodoc:



14
15
16
17
18
19
20
21
22
23
24
# File 'lib/rgeo/cartesian/calculations.rb', line 14

def initialize(start, stop)
  @s = start
  @e = stop
  @sx = @s.x
  @sy = @s.y
  @ex = @e.x
  @ey = @e.y
  @dx = @ex - @sx
  @dy = @ey - @sy
  @lensq = @dx * @dx + @dy * @dy
end

Instance Attribute Details

#dxObject (readonly)

Returns the value of attribute dx.



26
27
28
# File 'lib/rgeo/cartesian/calculations.rb', line 26

def dx
  @dx
end

#dyObject (readonly)

Returns the value of attribute dy.



26
27
28
# File 'lib/rgeo/cartesian/calculations.rb', line 26

def dy
  @dy
end

#eObject (readonly)

Returns the value of attribute e.



26
27
28
# File 'lib/rgeo/cartesian/calculations.rb', line 26

def e
  @e
end

#sObject (readonly)

Returns the value of attribute s.



26
27
28
# File 'lib/rgeo/cartesian/calculations.rb', line 26

def s
  @s
end

Instance Method Details

#contains_point?(point) ⇒ Boolean

Returns:

  • (Boolean)


59
60
61
62
63
64
65
66
# File 'lib/rgeo/cartesian/calculations.rb', line 59

def contains_point?(point)
  if side(point) == 0
    t = tproj(point)
    t && t >= 0.0 && t <= 1.0
  else
    false
  end
end

#degenerate?Boolean

Returns:

  • (Boolean)


37
38
39
# File 'lib/rgeo/cartesian/calculations.rb', line 37

def degenerate?
  @lensq == 0
end

#eql?(other) ⇒ Boolean Also known as: ==

Returns:

  • (Boolean)


32
33
34
# File 'lib/rgeo/cartesian/calculations.rb', line 32

def eql?(other)
  other.is_a?(Segment) && @s == other.s && @e == other.e
end

#intersects_segment?(seg) ⇒ Boolean

Returns:

  • (Boolean)


68
69
70
# File 'lib/rgeo/cartesian/calculations.rb', line 68

def intersects_segment?(seg)
  !segment_intersection(seg).nil?
end

#lengthObject



141
142
143
# File 'lib/rgeo/cartesian/calculations.rb', line 141

def length
  Math.sqrt(@lensq)
end

#segment_intersection(seg) ⇒ RGeo::Feature::Point?

If this and the other segment intersect, this method will return the coordinate at which they intersect, otherwise nil. In the case of a partial overlap (parallel segments), this will return a single point on the overlapping portion.

Parameters:

Returns:



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/rgeo/cartesian/calculations.rb', line 80

def segment_intersection(seg)
  s2 = seg.s
  # Handle degenerate cases
  if seg.degenerate?
    return @s if @lensq == 0 && @s == s2

    return contains_point?(s2) ? s2 : nil
  elsif @lensq == 0
    return seg.contains_point?(@s) ? @s : nil
  end

  # Both segments have nonzero length.
  sx2 = s2.x
  sy2 = s2.y
  dx2 = seg.dx
  dy2 = seg.dy
  denom = @dx * dy2 - @dy * dx2

  if denom == 0
    # Segments are parallel. Make sure they are collinear.
    return nil unless side(s2) == 0

    # return the first point it finds that intersects another line.
    # In many cases, the intersection is actually another line
    # segment, but for now, we will just return a single point.
    return s2 if contains_point?(s2)
    return seg.e if contains_point?(seg.e)
    return @s if seg.contains_point?(@s)
    return @e if seg.contains_point?(@e)
    nil
  else
    # Segments are not parallel. Check the intersection of their
    # containing lines.
    num1 = dx2 * (@sy - sy2) - (dy2 * (@sx - sx2))
    num2 = @dx * (@sy - sy2) - (@dy * (@sx - sx2))
    cross1 = num1 / denom
    cross2 = num2 / denom

    return nil if cross1 < 0.0 || cross1 > 1.0
    if cross2 >= 0.0 && cross2 <= 1.0
      x = @sx + (cross1 * @dx)
      y = @sy + (cross1 * @dy)

      # Check if this segment contains the point.
      # Sometimes round-off errors occur and intersections
      # are recorded as off the line segments.
      #
      # If this is the case, return the closest point from
      # either segment.
      int_pt = @s.factory.point(x, y)

      return int_pt if contains_point?(int_pt)

      # find closest of @s, @e, seg.s, seg.e
      [@e, seg.s, seg.e].reduce(@s) do |closest, pt|
        int_pt.distance(pt) < int_pt.distance(closest) ? pt : closest
      end
    end
  end
end

#side(point) ⇒ Object

Returns a negative value if the point is to the left, a positive value if the point is to the right, or 0 if the point is collinear to the segment.



45
46
47
48
49
# File 'lib/rgeo/cartesian/calculations.rb', line 45

def side(point)
  px = point.x
  py = point.y
  (@sx - px) * (@ey - py) - (@sy - py) * (@ex - px)
end

#to_sObject



28
29
30
# File 'lib/rgeo/cartesian/calculations.rb', line 28

def to_s
  "#{@s} - #{@e}"
end

#tproj(point) ⇒ Object



51
52
53
54
55
56
57
# File 'lib/rgeo/cartesian/calculations.rb', line 51

def tproj(point)
  if @lensq == 0
    nil
  else
    (@dx * (point.x - @sx) + @dy * (point.y - @sy)) / @lensq
  end
end