Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Shapely .contains() method but including boundaries?

Tags:

python

shapely

I'm working with Shapely polygons and I need a way to delete all smaller polygons contained within a bigger polygon. I tried using the .contains() method which Shapely provides, but the method doesn't return True if a given smaller polygon isn't completely inside of the bigger, "parent" polygon.

Basically, I want a method like .contains() but that it returns True in case the inner polygon shares boundaries with the outer polygon like on the example image below.

here.

Here are the polygons from the picture presented in wkt format:

The green one:

POLYGON Z ((14.4265764858233823 45.3396418051734784 0.0000000000000000, 14.4267228266679606 45.3395430970275015 0.0000000000000000, 14.4266753563381904 45.3394727193694536 0.0000000000000000, 14.4265290154936121 45.3395714275154376 0.0000000000000000, 14.4265764858233823 45.3396418051734784 0.0000000000000000))`  

The red one:

POLYGON Z ((14.4265450394689161 45.3395951840357725 0.0000000000000000, 14.4265695507109317 45.3395786509942837 0.0000000000000000, 14.4265802185605700 45.3395944667317679 0.0000000000000000, 14.4265982245953417 45.3395823215079616 0.0000000000000000, 14.4265715327703994 45.3395427492501426 0.0000000000000000, 14.4265290154936121 45.3395714275154376 0.0000000000000000, 14.4265450394689161 45.3395951840357725 0.0000000000000000))

I also tried using the .intersects() method but it returns True for polygons outside of a given polygon which have some common boundaries, which I do not want.

I hope you understand what I need and I'm thankful if someone knows a solution to this.

like image 927
kfilipcic Avatar asked Jul 06 '19 10:07

kfilipcic


1 Answers

Normally the contains method should work when testing if one polygon is inside another and they have common borders. For example, if you would take the following simple example, it would work as expected:

from shapely.geometry import Polygon

a = Polygon([(0, 0), (2, 0), (2, 2), (0, 2)])
b = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
a.contains(b)
# True

enter image description here

But quite often what happens is that due to precision errors the inner polygon comes out just a tiny bit from the outer and the test fails.

Here, for example, I plotted your polygons and zoomed in on the upper left intersection point:

import matplotlib.pyplot as plt

plt.plot(*green.exterior.xy, c='g')
plt.plot(*red.exterior.xy, c='r')

You can see that lines don't lie on each other perfectly:

enter image description here

There are several ways to deal with this problem. The first one, for example, was proposed in How to deal with rounding errors in Shapely and in some Shapely issues on GitHub:

  1. Shrink the smaller polygon or extend the bigger one a bit:

    big.contains(small.buffer(-1e-14))
    # True
    big.buffer(1e-14).contains(small)
    # True
    
  2. Check that area of the smaller polygon that lies outside of the bigger one is close to zero:

    small.difference(big).area < 1e-14
    # True
    
  3. Check if distances of each vertex of a smaller polygon to the bigger polygon are close to zero:

    from shapely.geometry import Point
    
    vertices = map(Point, small.exterior.coords)
    distances = map(big.distance, vertices)
    all(distance < 1e-14 for distance in distances) 
    # True
    

Probably there are more ways to perform the test, but I think these will suffice.

like image 193
Georgy Avatar answered Sep 30 '22 05:09

Georgy