Neue Funktionen und neue Klassen zur Rack Definition und Sortierung der Punkte auf dem Rack

This commit is contained in:
2025-05-14 10:58:53 +02:00
parent 559a4690c8
commit 1341142477
+150 -6
View File
@@ -2,6 +2,8 @@ import json
from shapely.geometry import LineString, Point
from shapely.ops import nearest_points
import unittest
from collections import defaultdict
import bisect
class NodeIDs():
def __init__(self, points=[]):
@@ -39,12 +41,109 @@ class NodeIDs():
ret.append(c)
return ret
class RackIDs():
def __init__(self, racks=dict()):
self._point2rack = dict()
self._rack2begend = dict()
self.add_racks(racks)
def add_rack(self, beg:Point, end:Point, name): #Hier wird Rack nur mit Anfang und Ende hinzugefügt -> wie macht man Zwischenpunkte?
if beg in self._point2rack:
self._point2rack[beg].append(name)
else:
self._point2rack[beg] = [name]
if end in self._point2rack:
self._point2rack[end].append(name)
else:
self._point2rack[end] = [name]
self._rack2begend[name] = [beg, end] #Anfangs und Endpunkte zu Rack Namen merken
def add_racks(self, racks:dict):
for name,v in racks.items():
if len(v) != 2:
raise AttributeError
self.add_rack(v[0], v[1], name)
def add_point_to_rack(self, point:Point, name:str):
pass
def get_racks_from_point(self, point:Point) -> list[str]:
return self._point2rack[point]
def get_points_from_rack(self, name:str) -> list[Point]:
''' Gibt zu Namen von Rack zugehörige Punkte aus und sortiert Punkte'''
ret = list()
pin = PointIndex2D()
for p, l_racks in self._point2rack.items():
if name in l_racks:
ret.append(p)
pin.add_points(ret)
ret_sorted = list()
[pa, pe] = self._rack2begend[name]
if self.rack_is_horizontal(name):
ret_sorted = pin.get_sorted_by_x()
else:
ret_sorted = pin.get_sorted_by_y()
return ret_sorted
def rack_is_horizontal(self, name):
[pa, pe] = self._rack2begend[name]
if pa.y == pe.y:
return True
else:
return False
class PointIndex2D:
def __init__(self):
self._points_by_x = [] # [(x, y)]
self._points_by_y = [] # [(y, x)]
def add_point(self, x, y):
bisect.insort(self._points_by_x, (x, y))
bisect.insort(self._points_by_y, (y, x))
def add_points(self, points:list[Point]):
for p in points:
self.add_point(p.x, p.y)
def query_box(self, x1, x2, y1, y2):
# Suche nach x-Grenzen
ix1 = bisect.bisect_left(self._points_by_x, (x1, -float('inf')))
ix2 = bisect.bisect_right(self._points_by_x, (x2, float('inf')))
candidates = self._points_by_x[ix1:ix2]
# Filtere nach y
ret = list()
for (x,y) in candidates:
if y1 <= y <= y2:
ret.append(Point(x,y))
return ret
def get_sorted_by_x(self):
# Sortiere nach x
ret = list()
for (x,y) in self._points_by_x:
ret.append(Point(x,y))
return ret
def get_sorted_by_y(self):
# Sortiere nach y
ret = list()
for (y,x) in self._points_by_y:
ret.append(Point(x,y))
return ret
def to_json(d, pretty: bool = True) -> str:
return json.dumps(d, indent=2 if pretty else None, default=str) #ensure_ascii false für darstellung von "ue"
def rack_segmentation(racks):
''' Racks werden zu LineString konvertiert. Racks bestehend aus Polylinine werden in einzelne Segmente zerlegt.'''
''' Racks werden zu LineString konvertiert. Racks bestehend aus Polylinine werden in einzelne Segmente zerlegt und in Liste gesammelt.'''
rack_segments = []
for rack_id, nodes in racks.items():
# Sortiere Node_1, Node_2, ...
@@ -58,8 +157,8 @@ def rack_segmentation(racks):
return(rack_segments)
def rack_endpoints(rack_segments):
''' Endpunkte der Racks werden in Points konvertiert'''
def find_rack_endpoints(rack_segments):
''' Endpunkte der Racks-Segmente werden in Points konvertiert und in Liste gesammelt'''
segment_endpoints = []
for rack_id, idx, line in rack_segments:
for pt in [line.coords[0], line.coords[1]]:
@@ -97,8 +196,13 @@ def increase_circle(tol, tol_step, line, pt, rack_id, idx, other_rack_id, other_
break
radius += tol_step
# === 3. Verbindungen suchen ===
def search_connections(rack_segments, segment_endpoints, tol, tol_step):
''' Aus Rack Segmenten und Endpunkten der Racks wird unter Berücksichtigung von Toleranz naheliegende Endpunkte gefunden.
Zuerst echte Schnittpunkte und im Anschluss via Kreissuche neheliegende Punkte und deren gepinnte Berührpunkte
'''
verbindungen = []
endpoint_pinned = []
@@ -116,7 +220,6 @@ def search_connections(rack_segments, segment_endpoints, tol, tol_step):
#print(f"✅ Exakter Schnittpunkt {inter} zwischen {rack_id1}_{idx1} und {rack_id2}_{idx2}")
verbindungen.append((rack_id1, idx1, rack_id2, idx2, inter))
# === B: Näherungsweise Verbindung durch Toleranz-Kreise ===
''' Entlanglaufen der Racks und Scan nach Endpunkten im Toleranzbereich'''
for rack_id, idx, line in rack_segments:
@@ -174,13 +277,31 @@ def search_connections(rack_segments, segment_endpoints, tol, tol_step):
d_racks_segments[key] = coords # aktualisieren
#Dict erstellen, dass alle Punkte die an einem Rack anschließen
#Dict erstellen, dass alle Punkte die an einem Rack anschließen speichert
d_rack_conn_points = dict()
for conn_to_rack, conn_to_idx, conn_from_rack, conn_from_idx, conn_point in verbindungen:
key = f"{conn_to_rack}-{conn_to_idx} + {conn_from_rack}-{conn_from_idx}"
d_rack_conn_points[key] = [conn_point]
d_rack_to_points = dict() #neues Dict für Rack_id - Idx: Alle Punkte auf dem Rack
for key, coords in d_racks_segments.items(): # Erst Anfangs und Endpunkt aus d_racks_segments holen
# coords = [start_point end_point]
d_rack_to_points[key] = coords.copy()
for key, point in d_rack_conn_points.items(): # Dann aus d_rack_conn_points alle verbindungspunkte holen und dazu speichern
to_rack = key.split(" + ")[0]
if to_rack in d_rack_to_points:
d_rack_to_points[to_rack].extend(point)
for key in d_rack_to_points:
unique_points = list({(pt.x, pt.y): pt for pt in d_rack_to_points[key]}.values())
d_rack_to_points[key] = unique_points
return [d_racks_segments, d_rack_conn_points]
@@ -205,7 +326,7 @@ class TestLinesweep(unittest.TestCase):
# === 2. Alle Endpunkte sammeln ===
''' Alle Endpunkte aller Racks als Point gespeichert, um shapely funktionen verwenden zu können'''
segment_endpoints = rack_endpoints(rack_segments)
segment_endpoints = find_rack_endpoints(rack_segments)
d_racks_segments, d_rack_conn_points = search_connections(rack_segments, segment_endpoints, tol, tol_step)
@@ -221,6 +342,29 @@ class TestLinesweep(unittest.TestCase):
log_res = to_json(res_rack_seg)
self.assertEqual(d_racks_segments, res_rack_seg)
def test_ids_to_point(self):
allids = NodeIDs(nodes)
for k,v in d_racks_segments.items():
allids.add_points(v)
for k,v in d_rack_conn_points:
allids.add_point(v)
def test_ids_to_point(self):
res_rack_seg = {'Rack_1-0': [Point(1, 0), Point(5, 6)],
'Rack_2-0': [Point(1, 8), Point(1, 0)],
'Rack_2-1': [Point(0, 10), Point(5, 10)]}
allids = RackIDs(res_rack_seg)
self.assertEqual(allids.get_racks_from_point(Point(1, 0)), ["Rack_1-0", "Rack_2-0"])
self.assertEqual(allids.get_racks_from_point(Point(5, 6)), ["Rack_1-0"])
self.assertEqual(allids.get_points_from_rack("Rack_2-0"), [Point(1, 0), Point(1, 8)])
if __name__ == '__main__':
unittest.main()