pythongeometryshapely

Which Shapely predicate should be used to distinquish between these LinearRings


In my project there are two use-cases. The red and blue shapes are not overlapping in the first case, but they do in the second.

case 1
case #1

case 2
case #2

In my test code I tried various predicates from Shapely but could not find anyway to tell them apart.

from shapely import LinearRing

# first use-case
blue_sq = LinearRing([(1, 1), (1, 2), (2, 2), (2, 1), (1, 1)])
red_one = LinearRing([(0, 0), (0, 3), (3, 3), (3, 2), (1, 2), (1, 0), (0, 0)])

print(blue_sq.overlaps(red_one))
print(blue_sq.intersects(red_one))
print(blue_sq.crosses(red_one))
print(blue_sq.contains(red_one))
print(blue_sq.touches(red_one))
print(blue_sq.within(red_one))

# second use-case
blue_ln = LinearRing([(2, 1), (2, 2), (7, 2), (7, 1), (2, 1)])
red_two = LinearRing([(1, 0), (1, 1), (2, 1), (2, 2), (3, 2), (3, 0), (1, 0)])

print(blue_ln.overlaps(red_two))
print(blue_ln.intersects(red_two))
print(blue_ln.crosses(red_two))
print(blue_ln.contains(red_two))
print(blue_ln.touches(red_two))
print(blue_ln.within(red_two))

The results are summarised in the table below.

Shapely predicate case 1 case 2
overlaps True True
intersects True True
crosses False False
contains False False
touches False False
within False False

Both cases appear to intersect and overlap, but neither case crosses, contains, touches or lies within the other. I feel sure that I am missing something obvious :-)


Solution

  • A LinearRing is still just a line... so considering that both cases are indeed very similar and there are no differences in the results of the predicates.

    A difference appears when you consider the coordinates as the outer border of a surface. To use the lines like that you need to use them to create Polygons in shapely. If you do that, you get the expected different results.

    Code sample:

    from shapely import Polygon
    
    # first use-case
    blue_sq = Polygon([(1, 1), (1, 2), (2, 2), (2, 1), (1, 1)])
    red_one = Polygon([(0, 0), (0, 3), (3, 3), (3, 2), (1, 2), (1, 0), (0, 0)])
    
    print(f"{blue_sq.overlaps(red_one)=}")
    print(f"{blue_sq.intersects(red_one)=}")
    print(f"{blue_sq.crosses(red_one)=}")
    print(f"{blue_sq.contains(red_one)=}")
    print(f"{blue_sq.touches(red_one)=}")
    print(f"{blue_sq.within(red_one)=}")
    
    # second use-case
    blue_ln = Polygon([(2, 1), (2, 2), (7, 2), (7, 1), (2, 1)])
    red_two = Polygon([(1, 0), (1, 1), (2, 1), (2, 2), (3, 2), (3, 0), (1, 0)])
    
    print(f"{blue_ln.overlaps(red_two)=}")
    print(f"{blue_ln.intersects(red_two)=}")
    print(f"{blue_ln.crosses(red_two)=}")
    print(f"{blue_ln.contains(red_two)=}")
    print(f"{blue_ln.touches(red_two)=}")
    print(f"{blue_ln.within(red_two)=}")
    

    Output:

    blue_sq.overlaps(red_one)=False
    blue_sq.intersects(red_one)=True
    blue_sq.crosses(red_one)=False
    blue_sq.contains(red_one)=False
    blue_sq.touches(red_one)=True
    blue_sq.within(red_one)=False
    blue_ln.overlaps(red_two)=True
    blue_ln.intersects(red_two)=True
    blue_ln.crosses(red_two)=False
    blue_ln.contains(red_two)=False
    blue_ln.touches(red_two)=False
    blue_ln.within(red_two)=False