Class: Terraformer::Polygon

Inherits:
Geometry show all
Defined in:
lib/terraformer/polygon.rb

Constant Summary

Constants inherited from Geometry

Geometry::MULTI_REGEX

Instance Attribute Summary

Attributes inherited from Geometry

#coordinates, #crs

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Geometry

#convex_hull, #each_coordinate, #geographic?, #get, #intersects?, iter_coordinate, #map_coordinate, #mercator?, #to_feature, #to_geographic, #to_hash, #to_mercator

Methods included from Geometry::ClassMethods

#arrays_intersect_arrays?, #coordinates_contain_point?, #edge_intersects_edge?, #line_contains_point?

Methods inherited from Primitive

#bbox, #envelope, #to_json, #type

Constructor Details

#initialize(*args) ⇒ Polygon

Returns a new instance of Polygon.



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/terraformer/polygon.rb', line 5

def initialize *args

  # each arg is a position of the polygon
  if Coordinate === args[0] || (Array === args[0] && Numeric === args[0][0])
    self.coordinates = [Coordinate.from_array(args)]

  elsif Array === args[0]

    # each arg is an array of positions; first is polygon, rest are "holes"
    if Coordinate === args[0][0] ||
       Array === args[0][0] && Numeric === args[0][0][0]
      self.coordinates = Coordinate.from_array args

    # arg is an array of polygon, holes
    elsif Array === args[0][0] && Array === args[0][0][0]
      self.coordinates = Coordinate.from_array *args
    end

  else
    super *args
  end

  # must be an array of arrays of coordinates
  unless Array === coordinates &&
         Array === coordinates[0] &&
         Terraformer::Coordinate === coordinates[0][0]
    raise ArgumentError.new 'invalid coordinates for Terraformer::Polygon'
  end

  if line_strings.map(&:linear_ring?).include? false
    raise ArgumentError.new 'not linear ring'
  end
end

Class Method Details

.polygonally_equal?(a, b) ⇒ Boolean

Returns:

  • (Boolean)

Raises:

  • (ArgumentError)


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
174
175
176
177
# File 'lib/terraformer/polygon.rb', line 144

def self.polygonally_equal? a, b
  if Terraformer::Coordinate === a ||
     Terraformer::Coordinate === b
    return a == b
  end

  raise ArgumentError unless Enumerable === a
  raise ArgumentError unless Enumerable === b

  # polygons must be the same length
  return false if a.length != b.length

  # polygons must have the last element be a duplicate
  # polygon-closing coordinate
  return false if a[0] != a[-1]
  return false if b[0] != b[-1]

  equal = true

  # clone so can pop/rotate
  a = a.clone
  b = b.clone

  # pop to drop the duplicate, polygon-closing, coordinate
  a.pop
  b.pop

  begin
    b.rotate_until_first_equals a[0]
    return a == b
  rescue IndexError
    return false
  end
end

Instance Method Details

#==(obj) ⇒ Object



47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/terraformer/polygon.rb', line 47

def == obj
  super obj do |o|

    equal = true

    # first check outer polygon
    equal = Polygon.polygonally_equal? self.coordinates[0], obj.coordinates[0]

    # then inner polygons (holes)
    #
    if equal
      if self.coordinates.length == obj.coordinates.length and obj.coordinates.length > 1

        self_holes = self.coordinates[1..-1].sort
        obj_holes = obj.coordinates[1..-1].sort

        self_holes.each_with_index do |hole, idx|
          equal = Polygon.polygonally_equal? hole, obj_holes[idx]
          break unless equal
        end
      end
    end

    equal
  end
end

#add_vertex(p) ⇒ Object Also known as: <<



122
123
124
# File 'lib/terraformer/polygon.rb', line 122

def add_vertex p
  insert_vertex -2, p
end

#contains?(obj) ⇒ Boolean

Returns:

  • (Boolean)


83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/terraformer/polygon.rb', line 83

def contains? obj
  obj = Coordinate.new obj if Array === obj and Numeric === obj[0]
  obj = obj.to_point if Coordinate === obj
  contained = false
  case obj
  when Point
    contained = Geometry.coordinates_contain_point? coordinates[0], obj.coordinates
    contained = !holes.any? {|h| h.contains? obj} if contained and has_holes?
  when MultiPoint
    contained = obj.points.all? {|p| contains? p}
  when LineString
    contained = contains?(obj.first_coordinate) && !Geometry.array_intersects_multi_array?(obj.coordinates, coordinates)
  when MultiLineString
    contained = obj.line_strings.all? {|ls| contains? ls}
  when Polygon
    contained = obj.within? self
  when MultiPolygon
    contained = obj.polygons.all? {|p| p.within? self}
  else
    raise ArgumentError.new "unsupported type: #{obj.type rescue obj.class}"
  end
  contained
end

#first_coordinateObject



43
44
45
# File 'lib/terraformer/polygon.rb', line 43

def first_coordinate
  coordinates[0][0]
end

#has_holes?Boolean

Returns:

  • (Boolean)


39
40
41
# File 'lib/terraformer/polygon.rb', line 39

def has_holes?
  coordinates.length > 1
end

#holesObject



78
79
80
81
# File 'lib/terraformer/polygon.rb', line 78

def holes
  return nil unless has_holes?
  coordinates[1..-1].map {|hole| Polygon.new hole}
end

#insert_vertex(idx, p) ⇒ Object

Raises:

  • (ArgumentError)


127
128
129
130
131
# File 'lib/terraformer/polygon.rb', line 127

def insert_vertex idx, p
  p = p.coordinates if Point === p
  raise ArgumentError unless Coordinate === p
  coordinates[0].insert idx, p
end

#line_stringsObject



74
75
76
# File 'lib/terraformer/polygon.rb', line 74

def line_strings
  coordinates.map {|lr| LineString.new lr}
end

#remove_vertex(p) ⇒ Object

Raises:

  • (ArgumentError)


133
134
135
136
137
# File 'lib/terraformer/polygon.rb', line 133

def remove_vertex p
  p = p.coordinates if Point === p
  raise ArgumentError unless Coordinate === p
  coordinates[0].delete p
end

#remove_vertex_at(idx) ⇒ Object



139
140
141
# File 'lib/terraformer/polygon.rb', line 139

def remove_vertex_at idx
  coordinates[0].delete_at idx
end

#within?(obj) ⇒ Boolean

Returns:

  • (Boolean)


107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/terraformer/polygon.rb', line 107

def within? obj
  case obj
  when Polygon
    if self == obj
      true
    elsif obj.contains? first_coordinate
      !Geometry.arrays_intersect_arrays?(coordinates, obj.coordinates)
    end
  when MultiPolygon
    obj.polygons.any? {|p| p.contains? self}
  else
    raise ArgumentError.new "unsupported type: #{obj.type rescue obj.class}"
  end
end