Weiterer Algorithmus zur Findung von Schnittpunkten und Beinahe-Schnittpunkten integriert. Findet aktuell zwar die Nähe der Punkte korrekt, Linestring mit Schnittpunkt-Koordinaten allerdings noch leer.
This commit is contained in:
+94
-130
@@ -2,6 +2,8 @@ from bisect import bisect_left, bisect_right,insort
|
||||
import unittest
|
||||
from collections import namedtuple
|
||||
import math
|
||||
from shapely.geometry import LineString
|
||||
from itertools import combinations
|
||||
|
||||
|
||||
def find_intersections(horizontal_segments, vertical_segments, epsilon=0.0):
|
||||
@@ -30,105 +32,36 @@ def find_intersections(horizontal_segments, vertical_segments, epsilon=0.0):
|
||||
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))
|
||||
|
||||
Point = namedtuple('Point', ['x', 'y'])
|
||||
Segment = namedtuple('Segment', ['p1', 'p2'])
|
||||
return list(intersections)
|
||||
|
||||
def ccw(a, b, c):
|
||||
return (c.y - a.y) * (b.x - a.x) > (b.y - a.y) * (c.x - a.x)
|
||||
def find_touch_cut_close(segments):
|
||||
# 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])
|
||||
|
||||
def intersect(s1, s2, tol=1e-8):
|
||||
"""Erweiterte Schnittprüfung mit Toleranz."""
|
||||
# Klassische Schnitttest
|
||||
a, b = s1.p1, s1.p2
|
||||
c, d = s2.p1, s2.p2
|
||||
if ccw(a, c, d) != ccw(b, c, d) and ccw(a, b, c) != ccw(a, b, d):
|
||||
return True
|
||||
|
||||
# Zusätzliche Toleranzprüfung: Mindestabstand der Segmente
|
||||
return segment_distance(s1, s2) < tol
|
||||
|
||||
def point_distance(p1, p2):
|
||||
return math.hypot(p1.x - p2.x, p1.y - p2.y)
|
||||
|
||||
def point_segment_distance(p, seg):
|
||||
"""Abstand eines Punkts zu einem Segment."""
|
||||
x, y = p.x, p.y
|
||||
x1, y1 = seg.p1.x, seg.p1.y
|
||||
x2, y2 = seg.p2.x, seg.p2.y
|
||||
|
||||
dx, dy = x2 - x1, y2 - y1
|
||||
if dx == dy == 0:
|
||||
return point_distance(p, seg.p1)
|
||||
|
||||
t = max(0, min(1, ((x - x1) * dx + (y - y1) * dy) / (dx * dx + dy * dy)))
|
||||
nearest = Point(x1 + t * dx, y1 + t * dy)
|
||||
return point_distance(p, nearest)
|
||||
|
||||
def segment_distance(s1, s2):
|
||||
"""Kleinster Abstand zweier Segmente."""
|
||||
return min(
|
||||
point_segment_distance(s1.p1, s2),
|
||||
point_segment_distance(s1.p2, s2),
|
||||
point_segment_distance(s2.p1, s1),
|
||||
point_segment_distance(s2.p2, s1),
|
||||
)
|
||||
|
||||
def segment_cmp_x(segment, x):
|
||||
p1, p2 = segment.p1, segment.p2
|
||||
if p1.x == p2.x:
|
||||
return min(p1.y, p2.y)
|
||||
slope = (p2.y - p1.y) / (p2.x - p1.x)
|
||||
y = p1.y + slope * (x - p1.x)
|
||||
return y
|
||||
|
||||
def schnittpunkttest(segments, tolerance=1e-8):
|
||||
events = []
|
||||
for seg in segments:
|
||||
left = seg.p1 if seg.p1.x < seg.p2.x else seg.p2
|
||||
right = seg.p2 if seg.p1.x < seg.p2.x else seg.p1
|
||||
events.append((left.x, True, seg))
|
||||
events.append((right.x, False, seg))
|
||||
|
||||
events.sort()
|
||||
L = []
|
||||
|
||||
for x, is_left, seg in events:
|
||||
key = lambda s: segment_cmp_x(s, x)
|
||||
idx = bisect_left(L, seg, key=key)
|
||||
|
||||
if is_left:
|
||||
insort(L, seg, key=key)
|
||||
idx = bisect_left(L, seg, key=key)
|
||||
pred = L[idx - 1] if idx > 0 else None
|
||||
succ = L[idx + 1] if idx < len(L) - 1 else None
|
||||
|
||||
if (pred and intersect(pred, seg, tolerance)) or (succ and intersect(succ, seg, tolerance)):
|
||||
#print("ja")
|
||||
return
|
||||
else:
|
||||
idx = bisect_left(L, seg, key=key)
|
||||
if idx < len(L) and L[idx] == seg:
|
||||
pred = L[idx - 1] if idx > 0 else None
|
||||
succ = L[idx + 1] if idx < len(L) - 1 else None
|
||||
L.pop(idx)
|
||||
if pred and succ and intersect(pred, succ, tolerance):
|
||||
#print("ja")
|
||||
return
|
||||
|
||||
#print("nein")
|
||||
|
||||
def sektionen(segmente, tolerance):
|
||||
laenge = len(segmente)
|
||||
lseg = list()
|
||||
for r1 in range(laenge):
|
||||
s1 = segmente[r1]
|
||||
p1 = Point(s1[0][0],s1[0][1])
|
||||
p2 = Point(s1[1][0],s1[1][1])
|
||||
seg1 = Segment( p1, p2 )
|
||||
lseg.append(seg1)
|
||||
schnittpunkttest(lseg, tolerance)
|
||||
if line1.distance(line2) < 0.5: #Toleranz 0,5 Hardcode
|
||||
verbindungen.append((i,j,line1.intersection(line2)))
|
||||
|
||||
return verbindungen
|
||||
|
||||
|
||||
|
||||
@@ -175,53 +108,84 @@ def sektionen(segmente, tolerance):
|
||||
# self.assertEqual( len(b), 1)
|
||||
# self.assertEqual( [(0,0),(0,0)], b)
|
||||
|
||||
class TestScanLineAllgMethods(unittest.TestCase):
|
||||
# def test_sweep(self):
|
||||
# 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))]
|
||||
# seks = [((1, 2), (5, 2)), ((3, 4), (7, 4)), ((4, 1), (4, 5)), ((6, 3), (6, 6))]
|
||||
|
||||
# a = sektionen(seks)
|
||||
# self.assertEqual( [(4,2),(4,4),(6,4)], a)
|
||||
# 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
|
||||
# """
|
||||
# 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))]
|
||||
# seks = [((0, 0), (10, 0)), ((4, 0.6), (4, 5)), ((6, 0.2), (6, 6)),((8,-0.2),(8, 4))]
|
||||
|
||||
# b = sektionen(seks, 0.5)
|
||||
# self.assertEqual( len(b), 2)
|
||||
# self.assertEqual( [(6,0),(8,0)], b)
|
||||
# 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
|
||||
# """
|
||||
# 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))]
|
||||
# seks = [((0.2, 0), (9.8, 0)), ((0, 0), (0, 10)), ((10, 0), (10, 10))]
|
||||
|
||||
# b = sektionen(seks, 0.5)
|
||||
# self.assertEqual( len(b), 2)
|
||||
# self.assertEqual( [(6,0),(8,0)], b)
|
||||
# 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
|
||||
# """
|
||||
# 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))]
|
||||
# seks = [((0, 0), (5, 0)), ((0, -5), (10, 5))]
|
||||
|
||||
# b = sektionen(seks, 0.5)
|
||||
# self.assertEqual( len(b), 1)
|
||||
# self.assertEqual( [(6,0),(8,0)], b)
|
||||
# b = find_all_intersections(seks, 0.5)
|
||||
# self.assertEqual( len(b), 1)
|
||||
# self.assertEqual( [(6,0),(8,0)], b)
|
||||
|
||||
def test_chatgpt(self):
|
||||
s1 = Segment(Point(0, 0), Point(4, 4)) # Diagonale
|
||||
s2 = Segment(Point(0, 4), Point(4, 0)) # Kreuzt s1 → Schnitt
|
||||
s3 = Segment(Point(5, 5), Point(6, 6)) # Weit entfernt, kein Schnitt
|
||||
s4 = Segment(Point(1, 1), Point(1, 3)) # Schneidet s1 → weiterer Schnitt
|
||||
schnittpunkttest([s1, s2, s3, s4], tolerance=1e-8) # Ausgabe: ja
|
||||
# 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_2_Stern(self):
|
||||
# Beispiel !Stern mit gemeinsamen Schnittpunkt in (2,2)
|
||||
segments = [
|
||||
((0, 0), (4, 4)), # Diagonal /
|
||||
((0, 4), (4, 0)), # Diagonal \
|
||||
((2, -1), (2, 5)), # Vertikal
|
||||
((-1, 2), (5, 2)) # Horizontal
|
||||
]
|
||||
|
||||
result = find_touch_cut_close(segments)
|
||||
self.assertEqual(len(result),1)
|
||||
|
||||
def test_shapely_2_Rand(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)
|
||||
self.assertEqual(len(result),3)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user