from bisect import bisect_left, bisect_right,insort import unittest from collections import namedtuple import math from shapely.geometry import LineString, Point from itertools import combinations def find_intersections(horizontal_segments, vertical_segments, epsilon=0.0): horizontal_segments.sort(key=lambda h: h[0][1]) # sortiere nach y horizontal_ys = [] horizontal_x_ranges = [] for (x1, y), (x2, _) in horizontal_segments: if x1 > x2: x1, x2 = x2, x1 horizontal_ys.append(y) horizontal_x_ranges.append((x1, x2)) intersections = [] for (x, y1), (_, y2) in vertical_segments: if y1 > y2: y1, y2 = y2, y1 for idx, y in enumerate(horizontal_ys): if y1 - epsilon <= y <= y2 + epsilon: x_start, x_end = horizontal_x_ranges[idx] if x_start - epsilon <= x <= x_end + epsilon: intersections.append((x, y)) return intersections def find_all_intersections(segments): # segments: List of ((x1, y1), (x2, y2)) lines = [LineString([p1, p2]) for p1, p2 in segments] intersections = set() for l1, l2 in combinations(lines, 2): if l1.intersects(l2): point = l1.intersection(l2) if point.geom_type == "Point": intersections.add((point.x, point.y)) elif point.geom_type == "MultiPoint": for p in point.geoms: intersections.add((p.x, p.y)) return list(intersections) def find_touch_cut_close(segments, tol): # segments: Liste aus ((x1, y1), (x2,y2)) mit 1=Anfang, 2=Ende verbindungen = [] for i,(s1_start, s1_end) in enumerate(segments): line1 = LineString([s1_start, s1_end]) for j, ([s2_start, s2_end]) in enumerate(segments): if i >= j: continue line2 = LineString([s2_start,s2_end]) if line1.distance(line2) < tol: verbindungen.append((i,j,line1.intersection(line2))) return verbindungen def find_touch_cut_close_ext(segments,tol): # segments: Liste aus ((x1, y1), (x2,y2)) mit 1=Anfang, 2=Ende verbindungen = [] for i,(s1_start, s1_end) in enumerate(segments): line1 = LineString([s1_start, s1_end]) for j, ([s2_start, s2_end]) in enumerate(segments): if i >= j: continue line2 = LineString([s2_start,s2_end]) #Prüfe Abstand if line1.distance(line2) < tol: schnitt = line1.intersection(line2) #Existiert tatsächlicher SP? if schnitt.is_empty: #Prüfen, welcher Endpunkt von Segment näer an line2 dist_start = Point(s2_start).distance(line1) #Abstand Statpunkt v. Linie1 zu Linie2 dist_end = Point(s2_end).distance(line1) #Abstand Endpunkt v. Linie 1 zu Linie2 if dist_start > dist_end: line2_ext = extend_from_point(s2_start, s2_end) else: line2_ext =extend_from_point(s2_end, s2_start) if dist_start < dist_end: line1_ext = extend_from_point(s1_start, s1_end) else: line1_ext = extend_from_point(s1_end, s1_start) schnitt_ext1 = line2_ext.intersection(line1) schnitt_ext2 = line1_ext.intersection(line2) if not schnitt_ext1.is_empty: verbindungen.append((i,j,schnitt_ext1)) if not schnitt_ext2.is_empty: verbindungen.append((i,j,schnitt_ext2)) else: verbindungen.append((i,j,schnitt)) return verbindungen def extend_from_point(p1, p2, max_extension=0.3): """Verlängert eine Linie von p1 in Richtung p2 um `max_extension`.""" dx = p2[0] - p1[0] dy = p2[1] - p1[1] length = math.hypot(dx, dy) if length == 0: return LineString([p1, p1]) # Kein Verlängern möglich # Normierter Richtungsvektor * (ursprüngliche Länge + Verlängerung) scale = (length + max_extension) / length new_end = (p1[0] + dx * scale, p1[1] + dy * scale) return LineString([p1, new_end]) # class TestSweepMethods(unittest.TestCase): # def test_sweep(self): # horizontal = [((1, 2), (5, 2)), ((3, 4), (7, 4))] # vertical = [((4, 1), (4, 5)), ((6, 3), (6, 6))] # a = find_intersections(horizontal, vertical) # self.assertEqual( [(4,2),(4,4),(6,4)], a) # def test_tol_hor(self): # """Testet die Anzahl der Schnittpunkte bei gegebener Toleranz # schnittpunkt zwischen gegebener horizontalen und zu kurzen vertikalen # """ # horizontal = [((0, 0), (10, 0))] # vertical = [((4, 0.6), (4, 5)), ((6, 0.2), (6, 6)),((8,-0.2),(8, 4))] # b = find_intersections(horizontal, vertical, 0.5) # self.assertEqual( len(b), 2) # self.assertEqual( [(6,0),(8,0)], b) # def test_tol_ver(self): # """Testet die Anzahl der Schnittpunkte bei gegebener Toleranz # schnittpunkt zwischen gegebener vertikalen und zu kurzen horizontalen # """ # horizontal = [((0.2, 0), (9.8, 0))] # vertical = [((0, 0), (0, 10)), ((10, 0), (10, 10))] # b = find_intersections(horizontal, vertical, 0.5) # self.assertEqual( len(b), 2) # self.assertEqual( [(6,0),(8,0)], b) # def test_tol_diag(self): # """Testet die Anzahl der Schnittpunkte bei gegebener Toleranz # Horizontale mit Diagonalen # """ # horizontal = [((0, 0), (5, 0))] # diagonal = [((0, -5), (10, 5))] # b = find_intersections(horizontal, diagonal, 0.5) # self.assertEqual( len(b), 1) # self.assertEqual( [(0,0),(0,0)], b) # class TestScanLineAllgMethods(unittest.TestCase): def test_sweep(self): seks = [((1, 2), (5, 2)), ((3, 4), (7, 4)), ((4, 1), (4, 5)), ((6, 3), (6, 6))] a = find_all_intersections(seks) self.assertEqual( [(4,2),(4,4),(6,4)], a) def test_tol_hor(self): """Testet die Anzahl der Schnittpunkte bei gegebener Toleranz schnittpunkt zwischen gegebener horizontalen und zu kurzen vertikalen """ seks = [((0, 0), (10, 0)), ((4, 0.6), (4, 5)), ((6, 0.2), (6, 6)),((8,-0.2),(8, 4))] b = find_all_intersections(seks, 0.5) self.assertEqual( len(b), 2) self.assertEqual( [(6,0),(8,0)], b) def test_tol_ver(self): """Testet die Anzahl der Schnittpunkte bei gegebener Toleranz schnittpunkt zwischen gegebener vertikalen und zu kurzen horizontalen """ seks = [((0.2, 0), (9.8, 0)), ((0, 0), (0, 10)), ((10, 0), (10, 10))] b = find_all_intersections(seks, 0.5) self.assertEqual( len(b), 2) self.assertEqual( [(6,0),(8,0)], b) def test_tol_diag(self): """Testet die Anzahl der Schnittpunkte bei gegebener Toleranz Horizontale mit Diagonalen """ seks = [((0, 0), (5, 0)), ((0, -5), (10, 5))] b = find_all_intersections(seks, 0.5) self.assertEqual( len(b), 1) self.assertEqual( [(6,0),(8,0)], b) def test_shapely(self): # Beispiel segments = [ ((0, 0), (4, 4)), # Diagonal / ((0, 4), (4, 0)), # Diagonal \ ((2, -1), (2, 5)), # Vertikal ((-1, 2), (5, 2)) # Horizontal ] result = find_all_intersections(segments) print(result) class TestShapely(unittest.TestCase): def test_shapely(self): #Test mit Strecken horizontal, vertikal, diagonal, Schnittpunkt u. Annaeherung segments = [ ((0, 0), (5, 0)), # Horizontale ((0, 0), (0, 5)), # Vertikale mit Schnitt Horizontal in 0,0 ((0.25, 2), (5, 2)), # Horizontale mit 0.25 Abstand zu vertikaler ((0.25, 4), (4, 3)) # Diagonale \ mit 0.25 Abstand zu vertikaler und 1 zu horizontaler ] result = find_touch_cut_close(segments, 0.5) self.assertEqual(len(result),3) def test_shapely_ext(self): #Test mit Strecken horizontal, vertikal, diagonal, Schnittpunkt u. Annaeherung segments = [ ((0, 0), (5, 0)), # Horizontale ((0, 0), (0, 5)), # Vertikale mit Schnitt Horizontal in 0,0 ((0.25, 2), (5, 2)), # Horizontale mit 0.25 Abstand zu vertikaler ((0.25, 4), (4, 3)), # Diagonale \ mit 0.25 Abstand zu vertikaler und 1 zu horizontaler ((5.10, -1), (5.1, 3)) # Vertikale mit 0,25 Abstand zu erster u zweiter Horizontalen ] result = find_touch_cut_close_ext(segments, 0.5) self.assertEqual(len(result),3) if __name__ == '__main__': unittest.main()