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.geometry import LineString, Point
|
||||||
from shapely.ops import nearest_points
|
from shapely.ops import nearest_points
|
||||||
import unittest
|
import unittest
|
||||||
|
from collections import defaultdict
|
||||||
|
import bisect
|
||||||
|
|
||||||
class NodeIDs():
|
class NodeIDs():
|
||||||
def __init__(self, points=[]):
|
def __init__(self, points=[]):
|
||||||
@@ -39,12 +41,109 @@ class NodeIDs():
|
|||||||
ret.append(c)
|
ret.append(c)
|
||||||
return ret
|
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:
|
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"
|
return json.dumps(d, indent=2 if pretty else None, default=str) #ensure_ascii false für darstellung von "ue"
|
||||||
|
|
||||||
def rack_segmentation(racks):
|
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 = []
|
rack_segments = []
|
||||||
for rack_id, nodes in racks.items():
|
for rack_id, nodes in racks.items():
|
||||||
# Sortiere Node_1, Node_2, ...
|
# Sortiere Node_1, Node_2, ...
|
||||||
@@ -58,8 +157,8 @@ def rack_segmentation(racks):
|
|||||||
|
|
||||||
return(rack_segments)
|
return(rack_segments)
|
||||||
|
|
||||||
def rack_endpoints(rack_segments):
|
def find_rack_endpoints(rack_segments):
|
||||||
''' Endpunkte der Racks werden in Points konvertiert'''
|
''' Endpunkte der Racks-Segmente werden in Points konvertiert und in Liste gesammelt'''
|
||||||
segment_endpoints = []
|
segment_endpoints = []
|
||||||
for rack_id, idx, line in rack_segments:
|
for rack_id, idx, line in rack_segments:
|
||||||
for pt in [line.coords[0], line.coords[1]]:
|
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
|
break
|
||||||
radius += tol_step
|
radius += tol_step
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# === 3. Verbindungen suchen ===
|
# === 3. Verbindungen suchen ===
|
||||||
def search_connections(rack_segments, segment_endpoints, tol, tol_step):
|
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 = []
|
verbindungen = []
|
||||||
endpoint_pinned = []
|
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}")
|
#print(f"✅ Exakter Schnittpunkt {inter} zwischen {rack_id1}_{idx1} und {rack_id2}_{idx2}")
|
||||||
verbindungen.append((rack_id1, idx1, rack_id2, idx2, inter))
|
verbindungen.append((rack_id1, idx1, rack_id2, idx2, inter))
|
||||||
|
|
||||||
|
|
||||||
# === B: Näherungsweise Verbindung durch Toleranz-Kreise ===
|
# === B: Näherungsweise Verbindung durch Toleranz-Kreise ===
|
||||||
''' Entlanglaufen der Racks und Scan nach Endpunkten im Toleranzbereich'''
|
''' Entlanglaufen der Racks und Scan nach Endpunkten im Toleranzbereich'''
|
||||||
for rack_id, idx, line in rack_segments:
|
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
|
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()
|
d_rack_conn_points = dict()
|
||||||
|
|
||||||
for conn_to_rack, conn_to_idx, conn_from_rack, conn_from_idx, conn_point in verbindungen:
|
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}"
|
key = f"{conn_to_rack}-{conn_to_idx} + {conn_from_rack}-{conn_from_idx}"
|
||||||
d_rack_conn_points[key] = [conn_point]
|
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]
|
return [d_racks_segments, d_rack_conn_points]
|
||||||
|
|
||||||
|
|
||||||
@@ -205,7 +326,7 @@ class TestLinesweep(unittest.TestCase):
|
|||||||
|
|
||||||
# === 2. Alle Endpunkte sammeln ===
|
# === 2. Alle Endpunkte sammeln ===
|
||||||
''' Alle Endpunkte aller Racks als Point gespeichert, um shapely funktionen verwenden zu können'''
|
''' 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)
|
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)
|
log_res = to_json(res_rack_seg)
|
||||||
self.assertEqual(d_racks_segments, 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__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
Reference in New Issue
Block a user