Module: RGeo::ImplHelper::ValidOpHelpers

Included in:
Cartesian::ValidOpHelpers
Defined in:
lib/rgeo/impl_helper/valid_op.rb

Overview

Helper functions for specific validity checks

Class Method Summary collapse

Class Method Details

.check_connected_interiors(poly) ⇒ String

Checks that the interior of the polygon is connected. A disconnected interior can be described by this polygon for example POLYGON((0 0, 10 0, 10 10, 0 10, 0 0), (5 0, 10 5, 5 10, 0 5, 5 0))

Which is a square with a diamond inside of it.



304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
# File 'lib/rgeo/impl_helper/valid_op.rb', line 304

def check_connected_interiors(poly)
  # This is not proper and will flag valid geometries as invalid, but
  # is an ok approximation.
  # Idea is to check if a single hole has multiple points on the
  # exterior ring.
  poly.interior_rings.each do |ring|
    touches = Set.new
    ring.points.each do |pt|
      touches.add(pt) if poly.exterior_ring.contains?(pt)
    end

    return Error::DISCONNECTED_INTERIOR if touches.size > 1
  end

  nil
end

.check_consistent_area(poly) ⇒ String

Checks that the edges in the polygon form a consistent area.

Specifically, checks that there are intersections no between the holes and the shell.

Also checks that there are no duplicate rings.



205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
# File 'lib/rgeo/impl_helper/valid_op.rb', line 205

def check_consistent_area(poly)
  # Holes don't cross exterior check.
  exterior = poly.exterior_ring
  poly.interior_rings.each do |ring|
    return Error::SELF_INTERSECTION if ring.crosses?(exterior)
  end

  # check interiors do not cross
  poly.interior_rings.combination(2).each do |ring1, ring2|
    return Error::SELF_INTERSECTION if ring1.crosses?(ring2)
  end

  # Duplicate rings check
  rings = [exterior] + poly.interior_rings
  return Error::SELF_INTERSECTION if rings.uniq.size != rings.size

  nil
end

.check_consistent_area_mp(mpoly) ⇒ String

Checks that polygons do not intersect in a multipolygon.



326
327
328
329
330
331
# File 'lib/rgeo/impl_helper/valid_op.rb', line 326

def check_consistent_area_mp(mpoly)
  mpoly.geometries.combination(2) do |p1, p2|
    return Error::SELF_INTERSECTION if p1.exterior_ring.crosses?(p2.exterior_ring)
  end
  nil
end

.check_holes_in_shell(poly) ⇒ String

Checks holes are contained inside the exterior of a polygon. Assuming check_consistent_area has already passed on the polygon, a simple point in polygon check can be done on one of the points in each hole to verify (since we know none of them intersect).



261
262
263
264
265
266
267
268
269
270
271
272
# File 'lib/rgeo/impl_helper/valid_op.rb', line 261

def check_holes_in_shell(poly)
  # get hole-less shell as test polygon
  shell = poly.exterior_ring
  shell = shell.factory.polygon(shell)

  poly.interior_rings.each do |interior|
    test_pt = interior.start_point
    return Error::HOLE_OUTSIDE_SHELL unless shell.contains?(test_pt) || poly.exterior_ring.contains?(test_pt)
  end

  nil
end

.check_holes_not_nested(poly) ⇒ String

Checks that holes are not nested within each other.



279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
# File 'lib/rgeo/impl_helper/valid_op.rb', line 279

def check_holes_not_nested(poly)
  # convert holes from linear_rings to polygons
  # Same logic that applies to check_holes_in_shell applies here
  # since we've already passed the consistent area test, we just
  # have to check if one point from each hole is contained in the other.
  holes = poly.interior_rings
  holes = holes.map { |v| v.factory.polygon(v) }
  holes.combination(2).each do |p1, p2|
    if p1.contains?(p2.exterior_ring.start_point) || p2.contains?(p1.exterior_ring.start_point)
      return Error::NESTED_HOLES
    end
  end

  nil
end

.check_invalid_coordinate(point) ⇒ String

Checks that the given point has valid coordinates.



187
188
189
190
191
192
193
# File 'lib/rgeo/impl_helper/valid_op.rb', line 187

def check_invalid_coordinate(point)
  x = point.x
  y = point.y
  return if x.finite? && y.finite? && x.real? && y.real?

  Error::INVALID_COORDINATE
end

.check_no_self_intersecting_rings(poly) ⇒ String

Check that rings do not self intersect in a polygon



239
240
241
242
243
244
245
246
247
248
249
250
251
# File 'lib/rgeo/impl_helper/valid_op.rb', line 239

def check_no_self_intersecting_rings(poly)
  exterior = poly.exterior_ring

  check = check_no_self_intersections(exterior)
  return check unless check.nil?

  poly.interior_rings.each do |ring|
    check = check_no_self_intersections(ring)
    return check unless check.nil?
  end

  nil
end

.check_no_self_intersections(ring) ⇒ String

Checks that the ring does not self-intersect. This is just a simplicity check on the ring.



230
231
232
# File 'lib/rgeo/impl_helper/valid_op.rb', line 230

def check_no_self_intersections(ring)
  return Error::SELF_INTERSECTION unless ring.simple?
end

.check_shells_not_nested(mpoly) ⇒ String

Checks that individual polygons within a multipolygon are not nested.



338
339
340
341
342
343
344
345
346
347
# File 'lib/rgeo/impl_helper/valid_op.rb', line 338

def check_shells_not_nested(mpoly)
  # Since we've passed the consistent area test, we can just check
  # that one point lies in the other.
  mpoly.geometries.combination(2) do |p1, p2|
    if p1.contains?(p2.exterior_ring.start_point) || p2.contains?(p1.exterior_ring.start_point)
      return Error::NESTED_SHELLS
    end
  end
  nil
end