diff --git a/lib/plant2dxf.py b/lib/plant2dxf.py index f116fd9..b408260 100644 --- a/lib/plant2dxf.py +++ b/lib/plant2dxf.py @@ -11,6 +11,9 @@ import re import argparse import configparser import ezdxf +from ezdxf import units +from ezdxf.entities import Line +from ezdxf.math import Matrix44 from pathlib import Path import math from utils import check_environment_var, setup_logger @@ -157,7 +160,7 @@ def transform_coords(x: float, y: float, height: float) -> tuple[float, float]: """Transformiert Bildschirmkoordinaten (0,0 oben links) ins DXF-KoSy (0,0 unten links).""" return x, y -def handle_ils_2_0_kreisel(msp, teileid, merkmale, x, y, doc, lib_doc, verbose, symbols): +def handle_ils_2_0_kreisel(msp, teileid, merkmale, x, y, doc, lib_doc, verbose, symbols, richtung,config): abstand_m = merkmale.get( "Abstand (Kreiselachse A - Kreiselachse) in Meter", "20" ).replace(",", ".") @@ -278,77 +281,110 @@ def handle_standard(msp, blocknames, teileid, x, y, lib_doc, doc, verbose): f"({x:.1f}, {y:.1f})") -def handle_ils_2_0_gefaellestrecke(msp, teileid, merkmale, x, y, doc, lib_doc, verbose, symbols): +def handle_ils_2_0_gefaellestrecke(msp, teileid, merkmale, x, y, doc, lib_doc, verbose, symbols, richtung,config): # blocks: [block1, block2], offsets: [(ox1, oy1), (ox2, oy2)] # Länge der Strecke (in Meter, Standard 10) + asoffset = float(config.get("ILS 2.0 Gefällestrecke", "asoffset")) + esoffset = float(config.get("ILS 2.0 Gefällestrecke", "esoffset")) + upper_hight = float(merkmale.get("Höhe oben")) *1000 + lower_hight = float(merkmale.get("Höhe unten")) * 1000 + hight = (upper_hight + lower_hight)/2 + + laenge_m = merkmale.get("Länge in Meter", "10").replace(",", ".") try: laenge = float(laenge_m) * 1000 # Meter → mm except ValueError: laenge = 10000 # Fallback 10 m + blockname = f"Ils_2.0_Gefaellestrecke_{laenge}_{hight}" + # if blockname not in doc.blocks: + # block = doc.blocks.new(name=blockname, base_point=(0,0,0)) + # halbe_laenge = laenge / 2 + + # dy = halbe_laenge * math.cos(0) + # start = (x , y + dy - asoffset,upper_hight) + # ende = (x , y - dy + esoffset,lower_hight) + # line = Line.new(dxfattribs={"start": start, "end": ende}) + # copy= line.copy() + + # copy.translate(-x,-y,-hight) + # block.add_entity(copy) + # a = "200000241_AS-Element_90_rechts" + # import_block(a,lib_doc,doc) + # b = "200000146_ES-Element_90_rechts" + # import_block(b,lib_doc,doc) + # block.add_blockref(a,(start[0]-x ,start[1]+asoffset -y,start[2] -hight)) + # block.add_blockref(b, (ende[0]-x ,ende[1]-esoffset-y ,ende[2] -hight)) - # Drehung (Winkel in Grad, Standard 0) - try: - winkel = float(merkmale.get("Drehung", 0)) - except (ValueError, TypeError): - winkel = 0.0 - winkel_rad = math.radians(winkel) - - # Die Koordinaten (x, y) sind die Mitte der Strecke - halbe_laenge = laenge / 2 - dx = halbe_laenge * math.sin(winkel_rad) - dy = halbe_laenge * math.cos(winkel_rad) - start = (x + dx, y + dy,float(merkmale.get("Höhe oben") ) *1000) - ende = (x - dx, y - dy,float(merkmale.get("Höhe unten")) * 1000) - msp.add_line(start, ende) + # if blockname not in doc.blocks: + # block = doc.blocks.new(name=blockname, base_point=(0,0,0)) + # halbe_laenge = laenge / 2 + + # dy = halbe_laenge * math.cos(0) + # start = (x , y + dy - asoffset,upper_hight) + # ende = (x , y - dy + esoffset,lower_hight) + # line = Line.new(dxfattribs={"start": start, "end": ende}) + # copy= line.copy() + + # copy.translate(-x,-y,-hight) + # block.add_entity(copy) + # a = "200000241_AS-Element_90_rechts" + # import_block(a,lib_doc,doc) + # b = "400102632_ES-Element_90_links" + # import_block(b,lib_doc,doc) + # block.add_blockref(a,(start[0]-x ,start[1]+asoffset -y,start[2])) + # block.add_blockref(b, (ende[0]-x ,ende[1]- esoffset-y,ende[2]),dxfattribs={"rotation": 180}) + + # if blockname not in doc.blocks: + # block = doc.blocks.new(name=blockname, base_point=(0,0,0)) + # halbe_laenge = laenge / 2 + + # dy = halbe_laenge * math.cos(0) + # start = (x , y + dy - esoffset,upper_hight) + # ende = (x , y - dy + asoffset,lower_hight) + # line = Line.new(dxfattribs={"start": start, "end": ende}) + # copy= line.copy() + + # copy.translate(-x ,-y,-hight) + # block.add_entity(copy) + # a = "200000217_AS-Element_90_links" + # import_block(a,lib_doc,doc) + # b = "400102632_ES-Element_90_links" + # import_block(b,lib_doc,doc) + + # block.add_blockref(a,(start[0]-x ,start[1]+esoffset -y,start[2]),dxfattribs={"rotation": 180}) + + # block.add_blockref(b, (ende[0]-x ,ende[1]-asoffset -y,ende[2]),dxfattribs={"rotation": 180}) + + if blockname not in doc.blocks: + block = doc.blocks.new(name=blockname, base_point=(0,0,0)) + halbe_laenge = laenge / 2 + + dy = halbe_laenge * math.cos(0) + start = (x , y + dy - esoffset,upper_hight) + ende = (x , y - dy + asoffset,lower_hight) + line = Line.new(dxfattribs={"start": start, "end": ende}) + copy= line.copy() + + copy.translate(-x ,-y,-hight) + block.add_entity(copy) + a = "200000217_AS-Element_90_links" + import_block(a,lib_doc,doc) + b = "200000146_ES-Element_90_rechts" + import_block(b,lib_doc,doc) + + block.add_blockref(a,(start[0]-x ,start[1]+asoffset -y,start[2]),dxfattribs={"rotation": 180}) + + block.add_blockref(b, (ende[0]-x ,ende[1]-esoffset -y,ende[2]),) + + + msp.add_blockref(blockname,(x,y,hight),dxfattribs={"rotation": merkmale.get("Drehung")}) if verbose: print(f"[INFO] Gefällestrecke → {teileid} Linie von ({start[0]:.1f}, {start[1]:.1f}) nach ({ende[0]:.1f}, {ende[1]:.1f})") - # Blöcke am Anfang und Ende der Strecke aus der CFG platzieren - if len(symbols) >= 2 and lib_doc is not None: - for i, sym in enumerate(symbols[:2]): - blockname = sym["name"] - offset = sym["offset"] - rotation = sym["rotation"] - pos = (start[0] + offset[0], start[1] + offset[1] - ) if i == 0 else (ende[0] + offset[0], ende[1] + offset[1]) - import_block(blockname, lib_doc, doc) - blockref_layer = get_layer(doc, lib_doc, blockname) - bref = msp.add_blockref(blockname, pos, dxfattribs={ - "rotation": rotation,"layer": blockref_layer}) - bref.add_auto_attribs({ATTR_TAG: teileid}) - if verbose: - print( - f"[INFO] Block '{blockname}' an {'Startpunkt' if i == 0 else 'Endpunkt'} {pos} für {teileid}, rot={rotation}") - elif lib_doc is None: - print("[WARN] lib_doc nicht verfügbar, Blöcke werden nicht eingefügt.") - -def handle_ils_2_0_kurve_angetrieben(msp, teileid, merkmale, x, y, doc, lib_doc, verbose, symbols): - center =x,y - winkel_deg = float(merkmale.get("Kurvenwinkel")) - richtung = merkmale.get("Kurvenrichtung") - - - if richtung.lower() == "links": - end_angle = 180 - else: - start_angle= 0 - # Endwinkel je nach Richtung - if richtung.lower() == "links": - start_angle = end_angle- winkel_deg - elif richtung.lower() == "rechts": - end_angle = start_angle + winkel_deg - else: - raise ValueError("Unbekannte Kurvenrichtung: Muss 'links' oder 'rechts' sein") - msp.add_arc( - center=center, - radius=400, - start_angle=start_angle, - end_angle=end_angle - ) -def handle_omniflo(msp, teileid, merkmale, x, y, doc, lib_doc, verbose, symbols): +def handle_omniflo(msp, teileid, merkmale, x, y, doc, lib_doc, verbose, symbols, richtung,config): """ Für Omniflo Gerade: zeichnet eine Linie (Mitte = Koordinate, Länge und Winkel aus Merkmale). Für alle anderen Omniflo-Typen: Block mit SivasNummer an den Koordinaten. @@ -465,15 +501,58 @@ def get_libfile_cfg(teileart, cfg_path): return parser.get(section, "libfile", fallback=None) return None + +def get_rotations_of_gefaellestrecke(csv_path:Path) -> dict: + gefaehlleStrecken = dict() + kreisel =dict() + richtung = dict() + anweisungen = 0 + """Gib für jede gefällestrecke zurück welche Drehrichtung die benachbarten Kreisel haben """ + with csv_path.open(newline="", encoding="utf-8") as fh: + reader = csv.DictReader(fh, delimiter=';') + for row in reader: + bezeichner = row["TeileArt"].strip() + if bezeichner == "ILS 2.0 Gefällestrecke": + Id = row["TeileId"].strip() + NachbarIds = row["NachbarIds"].strip() + gefaehlleStrecken.append({"Ids": Id, "NachbarIds": NachbarIds}) + if bezeichner == "ILS 2.0 Kreisel": + Id = row["TeileId"].strip() + merkmale = parse_merkmale(row.get("Merkmale", "")) + drehung = merkmale.get("Drehung") + kreisel.append({"Id":Id, "drehung":drehung}) + + bezeichner = row["Bezeichnung"].strip() + + for gefaelleStrecke in gefaehlleStrecken: + for kreis in kreisel: + if kreis.get("Id") in gefaelleStrecke.get("NachbarIds") and anweisungen == 0: + richtung.append({"Id": gefaelleStrecke.get("Ids"), "Drehung": kreis.get("drehung") }) + anweisungen = 1 + elif kreis.get("Id") in gefaelleStrecke.get("NachbarIds") and anweisungen == 1: + richtung.update({"Drehung2" : kreis.get("drehung")}) + + return richtung + + + + # --------------------------------------------------------- Hauptfunktion def main(csv_path: Path, lib_path: Path, cfg_path: Path, output_path: Path, verbose=False, logger=None): # Bibliothek nur laden, wenn Datei existiert check_dxflibrary_path(lib_path, verbose, logger) - + parser = configparser.ConfigParser() + try: + with open(cfg_path, encoding='utf-8') as f: + parser.read_file(f) + except Exception as e: + msg = f"Fehler beim Lesen der Config-Datei {cfg_path}: {e}" # Neue Ziel­zeichnung (DXF R2018) + config =parser doc = ezdxf.new(dxfversion="R2018") + doc.units = units.M doc.header['$INSUNITS'] = 4 # Millimeter msp = doc.modelspace() @@ -491,6 +570,10 @@ def main(csv_path: Path, lib_path: Path, cfg_path: Path, blocklib_dir = data_dir / "block_libraries" lib_docs = dict() + richtung = get_rotations_of_gefaellestrecke(csv_path) + # gibt zu jeder ShapeId einer Gefällestrecke zurück, ob sich der jeweilige Kreisel im UZ oder GUZ dreht + # rot_of_gf["shape_3ae53a7b-efb8-f66b-eadc-20b99f949ef1"] = ('UZ', 'GUZ') + # Verarbeitung der Blöcke with csv_path.open(newline="", encoding="utf-8") as fh: reader = csv.DictReader(fh, delimiter=';') @@ -502,6 +585,7 @@ def main(csv_path: Path, lib_path: Path, cfg_path: Path, merkmale = parse_merkmale(row.get("Merkmale", "")) merkmale["bezeichner"] = bezeichner + try: x_screen, y_screen = extract_coords(planquadrat) x, y = transform_coords(x_screen, y_screen, height) @@ -543,7 +627,7 @@ def main(csv_path: Path, lib_path: Path, cfg_path: Path, if func_name.startswith('handle_omniflo'): handler = globals().get('handle_omniflo') if handler: - handler(msp, teileid, merkmale, x, y, doc, lib_doc, verbose, symbols) + handler(msp, teileid, merkmale, x, y, doc, lib_doc, verbose, symbols, richtung, config) else: msg = f"[WARN] Keine Routine für TeileArt '{teileart}'. Überspringe '{teileid}'." if logger: @@ -613,3 +697,4 @@ if __name__ == "__main__": main(csv_path, default_lib_path, cfg_path, output_path, verbose=args.verbose, logger=logger) logger.info("=== plant2dxf Verarbeitung abgeschlossen ===") +