剔除自相交线上的环
import numpy as np from functools import partial from shapely.geometry import Point, LineString from shapely.ops import substring, linemerge from shapely.ops import unary_union from itertools import combinations class removeRingOnLineSting(): # 去除自相交线上的小环 # 基本思路 1 定位自相交点 2 自相交点分割线 3 分割线合并 根据长度筛选最长的两个 # todo1 环上环 def __init__(self, ls, torelance=30): self.ls = ls self.torelance = torelance self.cp = self.crossPoint() def crossPoint(self): # 定位自相交点 N = int(self.ls.length / self.torelance) sp = np.linspace(0, 1, N)[1:-1] pfun = partial(substring, geom=self.ls, normalized=1) cp = set() for p in sp: t1 = pfun(start_dist=0, end_dist=p) tp = pfun(start_dist=p, end_dist=p) t2 = pfun(start_dist=p, end_dist=1) if not t1.touches(t2): mp = [_ for _ in t1.intersection(t2).geoms if _ != tp] cp.update(mp) tx = lambda x: round(x, 3) cp = [(tx(p.x), tx(p.y)) for p in cp] cp = set([Point(_) for _ in cp]) return cp def removeRing(self): spliter = [p.buffer(self.torelance) for p in self.cp] spliter = unary_union(spliter) mls = self.ls.symmetric_difference(spliter) # 自相交点缓冲裁剪线 mls = [_ for _ in mls.geoms if _.geom_type == 'LineString'] # 过滤 mls = self.spliterFilter(mls) # 过滤环 及 自相交点附近的线 rls = self.mergeLinesWithGap(mls) # 合并剩余的线 return rls def spliterFilter(self, geoms): # 把起点和终点都在spliter附近的部分过滤掉 # 思想是计算起点和终点之间的距离 pfun = lambda x: substring(x, start_dist=0, end_dist=0, normalized=1) ptun = lambda x: substring(x, start_dist=1, end_dist=1, normalized=1) flag = [pfun(_).distance(ptun(_)) >= self.torelance * 2 for _ in geoms] geoms = [g for g, _ in zip(geoms, flag) if _] return geoms def mergeLinesWithGap(self, lines): pfun = lambda x: substring(x, start_dist=0, end_dist=0, normalized=1) ptun = lambda x: substring(x, start_dist=1, end_dist=1, normalized=1) fps = ((i, pfun(_)) for i, _ in enumerate(lines)) tps = ((j, ptun(_)) for j, _ in enumerate(lines)) ftp = list(fps) + list(tps) tt = combinations(ftp, 2) para = ((f, t) for (i, f), (j, t) in tt if i != j) para = ((f, t) for f, t in para if f.distance(t) <= self.torelance * 2) gap_line = [LineString(_) for _ in para] gap_line.extend(lines) return linemerge(gap_line) if __name__ == '__main__': pnts = [(-7,0), (1,0), (1,1), (0,1), (0,-2), (-1,-2),(-1,-1),(7,-1)] ls = LineString(pnts) algo = removeRingOnLineSting(ls, torelance=0.2) rls = algo.removeRing() # LINESTRING (-7 0, -0.2 0, 0 -0.2, 0 -0.8, 0.2 -1, 7 -1) print(rls.wkt)