如何区分Shapely中的自相交与自接触?

huangapple go评论97阅读模式
英文:

How do I differentiate self-intersection vs. self-touching in shapely?

问题

如何确定多边形是否自交叉(self-touching),类似于蓝色的多边形,但不是自相交(self-intersecting)像红色的那个。

如何区分Shapely中的自相交与自接触?

两幅图中所示多边形的顶点在这里:

from shapely.geometry import Polygon
from shapely.affinity import translate
import pylab

pylab.figure()

points = [[0, 0], [0, 100], [50, 80], [0, 60], [50, 30]]
polygon = Polygon(points)
bounds = polygon.bounds

x, y = polygon.exterior.xy
pylab.plot(x, y, 'b')
pylab.plot(0, 60, 'ko')

points = [[0, 0], [0, 100], [50, 80], [-10, 60], [50, 30]]
polygon = Polygon(points)
polygon = translate(polygon, xoff=70)

x, y = polygon.exterior.xy
pylab.plot(x, y, 'r')

pylab.axis('equal')
pylab.grid(True)
pylab.show()

请注意,我只翻译了代码之外的部分,不包括代码本身。

英文:

How can I determine if a polygon is self-touching(!) like the blue one, but not self-intersecting like the red one

如何区分Shapely中的自相交与自接触?

Vertices for the two polygons shown in the images is here:

from shapely.geometry import Polygon
from shapely.affinity import translate
import pylab

pylab.figure()

points = [[0, 0], [0, 100], [50, 80], [0, 60], [50, 30]]
polygon = Polygon(points)
bounds = polygon.bounds

x,y = polygon.exterior.xy
pylab.plot(x,y, 'b')
pylab.plot(0, 60, 'ko')

points = [[0, 0], [0, 100], [50, 80], [-10, 60], [50, 30]]
polygon = Polygon(points)
polygon = translate(polygon, xoff=70)

x,y = polygon.exterior.xy
pylab.plot(x,y, 'r')

pylab.axis('equal')
pylab.grid(True)
pylab.show()

答案1

得分: 1

重要的一点是,自交叉多边形上的交点与多边形线段相交三次,而在严格自相交多边形上,交点只与多边形线段相交两次。

根据这一点(希望它能很好地概括),我们可以构建一个方法,该方法获取交点,然后检查这个点与多边形线段相交多少次:

  • 严格自相交多边形将只有至少一个交点与多边形相交两次
  • 严格自交触多边形将使所有交点与多边形相交三次。

为了加快计算速度,我们可以使用SRTree(参见此帖)。我还使用多边形获取连续线段和点的方法,可以参考此帖

from shapely.geometry import Polygon, Point, LineString
from shapely.affinity import translate
from shapely import STRtree


def get_polygon_points(polygon: Polygon) -> list[Point]:
    x, y = polygon.exterior.coords.xy
    return [Point(xx, yy) for xx, yy in zip(x, y)]

def get_polygon_segments(polygon: Polygon) -> list[LineString]:
    points = get_polygon_points(polygon)
    return [
        LineString([[p1.x, p1.y], [p2.x, p2.y]]) for p1, p2 in zip(points[:-1], points[1:])
    ]

def get_intersecting_points(polygon: Polygon) -> list[Point]:
    segments = get_polygon_segments(polygon)
    tree = STRtree(segments)
    intersection_points = set()
    for i, seg in enumerate(segments):
        intersecting_segments_idx = tree.query(seg)
        for idx in intersecting_segments_idx:
            # 检查相交的线段不是连续线段的条件
            if idx != i and idx != (i + 1) % len(segments) and idx != (i - 1) % len(segments):
                intersection = seg.intersection(segments[idx])
                intersection_points.add((intersection.x, intersection.y))

    return [Point(*p) for p in intersection_points]


def self_touching_polygon(polygon: Polygon) -> bool:
    intersection_points = get_intersecting_points(polygon)
    segments = get_polygon_segments(polygon)
    tree = STRtree(segments)

    return all(len(tree.query(p)) == 3 for p in intersection_points)

def strictly_self_intersecting_polygon(polygon: Polygon) -> bool:
    return len(get_intersecting_points(polygon)) > 0 and not self_touching_polygon(polygon)

blue_coords = [[0, 0], [0, 100], [50, 80], [0, 60], [50, 30]]
blue_polygon = Polygon(blue_coords)

red_coords = [[0, 0], [0, 100], [50, 80], [-10, 60], [50, 30]]
red_polygon = Polygon(red_coords)

print(self_touching_polygon(red_polygon), strictly_self_intersecting_polygon(red_polygon))
print(self_touching_polygon(blue_polygon), strictly_self_intersecting_polygon(blue_polygon))

英文:

An important remark is that the intersection point on the self touching polygon intersects with polygon segments three times, whereas on the strictly self intersecting polygon, intersection points intersect only twice with the polygon segments.

According to this (hopefully, it generalizes well), we can build a method that get intersection points and then checks how many times this point intersects with the polygon segments:

  • A strictly self intersecting polygon will only have at least one intersection point that intersects twice with the polygon
  • A strictly self touching polygon will have all its intersection points intersecting three times.

To speed up the computation, we can use SRTree (see this post). I also use the polygon to get consecutive segments and points with this post.

from shapely.geometry import Polygon, Point, LineString
from shapely.affinity import translate
from shapely import STRtree


def get_polygon_points(polygon: Polygon) -> list[Point]:
    x, y = polygon.exterior.coords.xy
    return [Point(xx, yy) for xx, yy in zip(x, y)]

def get_polygon_segments(polygon: Polygon) -> list[LineString]:
    points = get_polygon_points(polygon)
    return [
        LineString([[p1.x, p1.y], [p2.x, p2.y]]) for p1, p2 in zip(points[:-1], points[1:])
    ]

def get_intersecting_points(polygon: Polygon) -> list[Point]:
    segments = get_polygon_segments(polygon)
    tree = STRtree(segments)
    intersection_points = set()
    for i, seg in enumerate(segments):
        intersecting_segments_idx = tree.query(seg)
        for idx in intersecting_segments_idx:
            # the condition checks that intersecting segments are not consecutive segments
            if idx != i and idx != (i + 1) % len(segments) and idx != (i - 1) % len(segments):
                intersection = seg.intersection(segments[idx])
                intersection_points.add((intersection.x, intersection.y))

    return [Point(*p) for p in intersection_points]


def self_touching_polygon(polygon: Polygon) -> bool:
    intersection_points = get_intersecting_points(polygon)
    segments = get_polygon_segments(polygon)
    tree = STRtree(segments)

    return all(len(tree.query(p)) == 3 for p in intersection_points)

def strictly_self_intersecting_polygon(polygon: Polygon) -> bool:
    return len(get_intersecting_points(polygon)) > 0 and not self_touching_polygon(polygon)

blue_coords = [[0, 0], [0, 100], [50, 80], [0, 60], [50, 30]]
blue_polygon = Polygon(blue_coords)

red_coords = [[0, 0], [0, 100], [50, 80], [-10, 60], [50, 30]]
red_polygon = Polygon(red_coords)

print(self_touching_polygon(red_polygon), strictly_self_intersecting_polygon(red_polygon))
print(self_touching_polygon(blue_polygon), strictly_self_intersecting_polygon(blue_polygon))

huangapple
  • 本文由 发表于 2023年6月1日 01:36:00
  • 转载请务必保留本文链接:https://go.coder-hub.com/76376033.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定