Neue Funktionen und neue Klassen zur Rack Definition und Sortierung der Punkte auf dem Rack
This commit is contained in:
+150
-6
@@ -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()
|
||||
Reference in New Issue
Block a user