class Geospatial::Polygon

Attributes

points[R]

Public Class Methods

[](*points) click to toggle source
# File lib/geospatial/polygon.rb, line 27
def self.[] *points
        self.new(points)
end
dump(polygon) click to toggle source
# File lib/geospatial/polygon.rb, line 37
def self.dump(polygon)
        if polygon
                JSON.dump(polygon.points.map(&:to_a))
        end
end
is_left(p0, p1, p2) click to toggle source
# File lib/geospatial/polygon.rb, line 144
def self.is_left(p0, p1, p2)
        a = p1 - p0
        b = p2 - p0
        
        return (a[0] * b[1]) - (b[0] * a[1])
end
load(data) click to toggle source
# File lib/geospatial/polygon.rb, line 31
def self.load(data)
        if data
                self.new(JSON.parse(data).map{|point| Vector.elements(point)})
        end
end
new(points, bounding_box = nil) click to toggle source
# File lib/geospatial/polygon.rb, line 43
def initialize(points, bounding_box = nil)
        @points = points
        @bounding_box = bounding_box
end

Public Instance Methods

[](index) click to toggle source
# File lib/geospatial/polygon.rb, line 50
def [] index
        a = @points[index.floor]
        b = @points[index.ceil % @points.size]
        
        return a + (b - a) * (index % 1.0)
end
area(radius = 1.0) click to toggle source

@param [Float] radius The radius of the sphere on which to compute the area.

# File lib/geospatial/polygon.rb, line 83
def area(radius = 1.0)
        if @points.size > 2
                area = 0.0
                
                self.edges.each do |p1, p2|
                        r1 = (p2[0] - p1[0]) * D2R
                        r2 = 2 + Math::sin(p1[1] * D2R) + Math::sin(p2[1] * D2R)
                        
                        area += r1 * r2
                end
                
                return (area * radius * radius / 2.0).abs
        else
                return 0.0
        end
end
bounding_box() click to toggle source
# File lib/geospatial/polygon.rb, line 61
def bounding_box
        @bounding_box ||= Box.enclosing_points(@points).freeze
end
edge_intersection(a, b) click to toggle source
# File lib/geospatial/polygon.rb, line 187
def edge_intersection(a, b)
        line = Line.new(a, b)
        
        edges.each_with_index do |(pa, pb), i|
                edge = Line.new(pa, pb)
                
                if line.intersect?(edge)
                        return i
                end
        end
        
        return nil
end
edges() { |point, points| ... } click to toggle source
# File lib/geospatial/polygon.rb, line 72
def edges
        return to_enum(:edges) unless block_given?
        
        size = @points.size
        
        @points.each_with_index do |point, index|
                yield point, @points[(index+1)%size]
        end
end
freeze() click to toggle source
Calls superclass method
# File lib/geospatial/polygon.rb, line 65
def freeze
        @points.freeze
        bounding_box.freeze
        
        super
end
include?(other) click to toggle source
# File lib/geospatial/polygon.rb, line 210
def include?(other)
        other.corners.all?{|corner| self.include_point?(corner)}
end
include_point?(point) click to toggle source
# File lib/geospatial/polygon.rb, line 173
def include_point?(point)
        return false unless bounding_box.include_point?(point)
        
        self.winding_number(point).odd?
end
intersect?(other) click to toggle source
# File lib/geospatial/polygon.rb, line 201
def intersect?(other)
        case other
        when Box
                intersect_with_box?(other)
        when Circle
                intersect_with_circle?(other)
        end
end
intersect_with_box?(other) click to toggle source
# File lib/geospatial/polygon.rb, line 179
def intersect_with_box?(other)
        return true if @points.any?{|point| other.include_point?(point)}
        
        return true if other.corners.any?{|corner| self.include_point?(corner)}
        
        return false
end
simplify() { |last, point, next_point| ... } click to toggle source
# File lib/geospatial/polygon.rb, line 100
def simplify
        simplified_points = @points.first(1)
        
        (1...@points.size).each do |index|
                point = @points[index]
                
                next_point = @points[(index+1) % @points.size]
                
                if yield(simplified_points.last, point, next_point)
                        simplified_points << point
                end
        end
        
        self.class.new(simplified_points, bounding_box)
end
subdivide() { |last, point, next_point| ... } click to toggle source

@example polygon.subdivide do |a, b|

        a = Geospatial::Location.new(*a)
        b = Geospatial::Location.new(*b)
if a.distance_from(b) > maximum_distance
        a.midpoints(b, 2)
end

end

# File lib/geospatial/polygon.rb, line 124
def subdivide
        simplified_points = @points.first(1)
        
        (1..@points.size).each do |index|
                point = @points[index % @points.size]
                next_point = @points[(index+1) % @points.size]
                
                if points = yield(simplified_points.last, point, next_point)
                        simplified_points.concat(points)
                end
                
                # Polygons are represented by a closed sequence of points, but we need to subdivide by the last point at the first point too. However, we don't add the first point a 2nd time.
                if index < @points.size
                        simplified_points << point
                end
        end
        
        self.class.new(simplified_points, bounding_box)
end
to_s() click to toggle source
# File lib/geospatial/polygon.rb, line 57
def to_s
        "#{self.class}#{@points.inspect}"
end
winding_number(p) click to toggle source

Test a 2D point for inclusion in the polygon. @param [Vector] p The point to test. @return [Number] The number of times the polygon winds around the point (0 if outside).

# File lib/geospatial/polygon.rb, line 154
def winding_number(p)
        count = 0
        
        edges.each do |pa, pb|
                if pa[1] <= p[1]
                        if pb[1] >= p[1] and Polygon.is_left(pa, pb, p) > 0
                                count += 1
                        end
                else
                        if pb[1] <= p[1] and Polygon.is_left(pa, pb, p) < 0
                                count -= 1
                        end
                end
                
        end
        
        return count
end