diff --git a/cfg/shapes.cfg b/cfg/shapes.cfg index c89a6d4..cc5c924 100644 --- a/cfg/shapes.cfg +++ b/cfg/shapes.cfg @@ -2,8 +2,12 @@ items = SP8, AN8 offset_symb1 = 0,0 offset_symb2 = 0,0 +rot_symb1 = 0.0 +rot_symb2 = 0.0 [ILS 2.0 Gefällestrecke] items = AE DS, EE DS offset_symb1 = 0,0 -offset_symb2 = 0,0 \ No newline at end of file +offset_symb2 = 0,0 +rot_symb1 = 90.0 +rot_symb2 = 90.0 \ No newline at end of file diff --git a/lib/plant2dxf.py b/lib/plant2dxf.py index ef70592..e150bb4 100644 --- a/lib/plant2dxf.py +++ b/lib/plant2dxf.py @@ -23,13 +23,15 @@ def get_shape_cfg(teileart, cfg_path): parser.read_file(f) section = teileart if section not in parser: - return [], [] + return [] # Blöcke items = parser.get(section, "items", fallback="").replace('"', '').split(",") blocks = [item.strip() for item in items if item.strip()] - # Offsets (optional) + # Offsets und Rotationen (optional) offset1 = parser.get(section, "offset_symb1", fallback="0,0") offset2 = parser.get(section, "offset_symb2", fallback="0,0") + rot1 = parser.get(section, "rot_symb1", fallback="0.0") + rot2 = parser.get(section, "rot_symb2", fallback="0.0") offsets = [] for off in (offset1, offset2): try: @@ -37,7 +39,22 @@ def get_shape_cfg(teileart, cfg_path): offsets.append((ox, oy)) except Exception: offsets.append((0.0, 0.0)) - return blocks, offsets + rots = [] + for rot in (rot1, rot2): + try: + rots.append(float(rot)) + except Exception: + rots.append(0.0) + # Baue Liste von Dicts + symbols = [] + for i, name in enumerate(blocks): + sym = { + "name": name, + "offset": offsets[i] if i < len(offsets) else (0.0, 0.0), + "rotation": rots[i] if i < len(rots) else 0.0 + } + symbols.append(sym) + return symbols # --------------------------------------------------------- Konstante Parameter ATTR_TAG = "TeileId" # Attributtag im Block @@ -78,6 +95,63 @@ def import_block(block_name: str, from_doc, to_doc) -> None: for ent in src: tgt.add_entity(ent.copy()) +def berechne_hoehe(csv_path): + y_werte = [] + with csv_path.open(newline="", encoding="utf-8") as fh: + reader = csv.DictReader(fh, delimiter=';') + for row in reader: + planquadrat = row.get("Planquadrat", "") + try: + _, y = extract_coords(planquadrat) + y_werte.append(y) + except Exception: + continue + if not y_werte: + raise ValueError("Keine Y-Koordinaten in der CSV gefunden!") + return max(y_werte) + +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, height - y + +def handle_ils_2_0_kreisel(msp, teileid, merkmale, x, y, doc, lib_doc, verbose, symbols): + abstand_m = merkmale.get( + "Abstand (Kreiselachse A - Kreiselachse) in Meter", "20" + ).replace(",", ".") + try: + abstand = float(abstand_m) * 1000 # Meter → mm + except ValueError: + abstand = 10000 # Fallback 10 m + + # Drehung (Winkel in Grad, Standard 0) aus Merkmale + 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 zwischen den beiden Blöcken (bereits transformiert) + halbabstand = abstand / 2 + dx = halbabstand * math.cos(winkel_rad) + dy = halbabstand * math.sin(winkel_rad) + pos1 = (x - dx, y - dy) + pos2 = (x + dx, y + dy) + positions = [pos1, pos2] + for i, sym in enumerate(symbols): + blockname = sym["name"] + offset = sym["offset"] + rotation = sym["rotation"] + if i < len(positions): + pos = (positions[i][0] + offset[0], positions[i][1] + offset[1]) + import_block(blockname, lib_doc, doc) + bref = msp.add_blockref(blockname, pos, dxfattribs={"rotation": rotation}) + bref.add_auto_attribs({ATTR_TAG: teileid}) + if verbose: + print(f"[INFO] Block '{blockname}' (Kreisel) → {teileid} " + f"({pos[0]:.1f}, {pos[1]:.1f}), rot={rotation}") + # Linien zeichnen + draw_kreisel_lines(msp, pos1, pos2) + def draw_kreisel_lines(msp, pos1, pos2): """Zeichnet tangentiale Linien zwischen zwei Kreiselblöcken, unabhängig vom Winkel.""" x1, y1 = pos1 @@ -101,59 +175,6 @@ def draw_kreisel_lines(msp, pos1, pos2): msp.add_line(p1a, p2a) msp.add_line(p1b, p2b) -def berechne_hoehe(csv_path): - y_werte = [] - with csv_path.open(newline="", encoding="utf-8") as fh: - reader = csv.DictReader(fh, delimiter=';') - for row in reader: - planquadrat = row.get("Planquadrat", "") - try: - _, y = extract_coords(planquadrat) - y_werte.append(y) - except Exception: - continue - if not y_werte: - raise ValueError("Keine Y-Koordinaten in der CSV gefunden!") - return max(y_werte) - -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, height - y - -def handle_ils_2_0_kreisel(msp, teileid, merkmale, x, y, doc, lib_doc, verbose, blocks, offsets): - # blocks: [block1, block2], offsets: [(ox1, oy1), (ox2, oy2)] - abstand_m = merkmale.get( - "Abstand (Kreiselachse A - Kreiselachse) in Meter", "20" - ).replace(",", ".") - try: - abstand = float(abstand_m) * 1000 # Meter → mm - except ValueError: - abstand = 10000 # Fallback 10 m - - # Drehung (Winkel in Grad, Standard 0) aus Merkmale - 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 zwischen den beiden Blöcken (bereits transformiert) - halbabstand = abstand / 2 - dx = halbabstand * math.cos(winkel_rad) - dy = halbabstand * math.sin(winkel_rad) - pos1 = (x - dx + offsets[0][0], y - dy + offsets[0][1]) - pos2 = (x + dx + offsets[1][0], y + dy + offsets[1][1]) - positions = [pos1, pos2] - for blockname, pos in zip(blocks, positions): - import_block(blockname, lib_doc, doc) - bref = msp.add_blockref(blockname, pos) - bref.add_auto_attribs({ATTR_TAG: teileid}) - if verbose: - print(f"[INFO] Block '{blockname}' (Kreisel) → {teileid} " - f"({pos[0]:.1f}, {pos[1]:.1f})") - # Linien zeichnen - draw_kreisel_lines(msp, pos1, pos2) - def handle_standard(msp, blocknames, teileid, x, y, lib_doc, doc, verbose): for blockname in blocknames: import_block(blockname, lib_doc, doc) @@ -163,7 +184,7 @@ def handle_standard(msp, blocknames, teileid, x, y, lib_doc, doc, verbose): print(f"[INFO] Block '{blockname}' (Standard) → {teileid} " f"({x:.1f}, {y:.1f})") -def handle_ils_2_0_gefaellestrecke(msp, teileid, merkmale, x, y, doc, lib_doc, verbose, blocks, offsets): +def handle_ils_2_0_gefaellestrecke(msp, teileid, merkmale, x, y, doc, lib_doc, verbose, symbols): # blocks: [block1, block2], offsets: [(ox1, oy1), (ox2, oy2)] # Länge der Strecke (in Meter, Standard 10) laenge_m = merkmale.get("Länge in Meter", "10").replace(",", ".") @@ -190,17 +211,17 @@ def handle_ils_2_0_gefaellestrecke(msp, teileid, merkmale, x, y, doc, lib_doc, v 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(blocks) >= 2 and lib_doc is not None: - block_start = blocks[0] - block_end = blocks[1] - import_block(block_start, lib_doc, doc) - bref1 = msp.add_blockref(block_start, (start[0] + offsets[0][0], start[1] + offsets[0][1])) - bref1.add_auto_attribs({ATTR_TAG: teileid}) - import_block(block_end, lib_doc, doc) - bref2 = msp.add_blockref(block_end, (ende[0] + offsets[1][0], ende[1] + offsets[1][1])) - bref2.add_auto_attribs({ATTR_TAG: teileid}) - if verbose: - print(f"[INFO] Block '{block_start}' an Startpunkt {start} und Block '{block_end}' an Endpunkt {ende} für {teileid}") + 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) + bref = msp.add_blockref(blockname, pos, dxfattribs={"rotation": rotation}) + 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.") @@ -259,9 +280,9 @@ def main(csv_path: Path, lib_path: Path, cfg_path: Path, # Funktions-Dispatch: handle_ (mit _ statt Leerzeichen und Punkten, alles klein) func_name = f'handle_{normalize_func_name(teileart)}' handler = globals().get(func_name) - blocks, offsets = get_shape_cfg(teileart, cfg_path) + symbols = get_shape_cfg(teileart, cfg_path) if handler: - handler(msp, teileid, merkmale, x, y, doc, lib_doc, verbose, blocks, offsets) + handler(msp, teileid, merkmale, x, y, doc, lib_doc, verbose, symbols) else: print(f"[WARN] Keine Routine für TeileArt '{teileart}'. Überspringe '{teileid}'.") continue