diff --git a/lib/export_sivas.py b/lib/export_sivas.py index 5c57321..e8048f1 100644 --- a/lib/export_sivas.py +++ b/lib/export_sivas.py @@ -103,6 +103,113 @@ def format_hoehe_m(hoehe_mm_str): return "" +def build_gefaellestrecke_details(block): + """Merkmale-Dict fuer eine einzelne ILS Gefaellestrecke (GF_N-Block).""" + attribs = block.get("attribs", {}) + + def mm_to_m(key): + try: + return str(round(float(attribs.get(key, "0")) / 1000.0, 2)) + except (ValueError, TypeError): + return "0" + + return { + "IDs": [get_id(block)], + "Bezeichnung": attribs.get("Bezeichnung", block.get("block_name", "")), + "Hoehe oben": mm_to_m("HOEHE_VON_mm"), + "Hoehe unten": mm_to_m("HOEHE_BIS_mm"), + "Laenge in Meter": attribs.get("L_GF_m", ""), + "Winkel": attribs.get("GF_WINKEL", ""), + "Anzahl der Separatoren": attribs.get("ANZAHL_SEPARATOR", "0"), + "Anzahl der Scanner": attribs.get("ANZAHL_SCANNER", "0"), + "Typ": attribs.get("TYP", "Gefaellestrecke"), + } + + +def gefaellestrecke_signature(block): + """ + Schluessel zur Gruppierung gleichartiger Gefaellestrecken: Bloecke mit + identischen Massen (Hoehe, Laenge, Winkel, Separatoren, Scanner, Typ) + landen in derselben Sivas-Zeile - nur IDs/Bezeichnung unterscheiden sich. + """ + attribs = block.get("attribs", {}) + return ( + attribs.get("HOEHE_VON_mm", "0"), + attribs.get("HOEHE_BIS_mm", "0"), + attribs.get("L_GF_m", ""), + attribs.get("GF_WINKEL", ""), + attribs.get("ANZAHL_SEPARATOR", "0"), + attribs.get("ANZAHL_SCANNER", "0"), + attribs.get("TYP", "Gefaellestrecke"), + ) + + +def parse_float_list(csv_str): + """Kommagetrennten Attribut-String (z.B. L_GF_m) in Liste von floats.""" + out = [] + for part in str(csv_str or "").split(","): + part = part.strip() + if not part: + continue + try: + out.append(float(part)) + except ValueError: + pass + return out + + +def parse_str_list(csv_str): + """Kommagetrennten Attribut-String (z.B. MOTORSEITE) in Liste von Strings.""" + return [p.strip() for p in str(csv_str or "").split(",") if p.strip()] + + +def build_variofoerderer_details(block): + """ + Merkmale-Dict fuer eine VF_N-Streckengruppe (VarioFoerderer, ggf. + mehrsegmentig aus Gefaelle- und angetriebenen Foerderer-Teilen). + """ + attribs = block.get("attribs", {}) + + def to_int(key): + try: + return int(attribs.get(key, "0") or "0") + except (ValueError, TypeError): + return 0 + + return { + "IDs": [get_id(block)], + "Anzahl Foerdergruppen": to_int("ANZAHL_VF"), + "Anzahl Gefaellegruppen": to_int("ANZAHL_GF"), + "Hoehe in m": attribs.get("MONTAGEHOEHE_m", "0"), + "GruppenteileGefaelle": { + "AnzahlGeraden": to_int("ANZAHL_GF"), + "Laenge in Meter": parse_float_list(attribs.get("L_GF_m", "")), + "Winkel": parse_float_list(attribs.get("GF_WINKEL", "")), + "AnzahlKurvenLinks90": to_int("GF_Bogen_L_90"), + "AnzahlKurvenLinks60": to_int("GF_Bogen_L_60"), + "AnzahlKurvenLinks30": to_int("GF_Bogen_L_30"), + "AnzahlKurvenRechts90": to_int("GF_Bogen_R_90"), + "AnzahlKurvenRechts60": to_int("GF_Bogen_R_60"), + "AnzahlKurvenRechts30": to_int("GF_Bogen_R_30"), + }, + "GruppenteileAngetrieben": { + "MotorSeite": parse_str_list(attribs.get("MOTORSEITE", "")), + "AnzahlGeraden": to_int("ANZAHL_VF"), + "Laenge in Meter": parse_float_list(attribs.get("L_VF_m", "")), + "Winkel": parse_float_list(attribs.get("VF_WINKEL", "")), + "Foerderrichtung": parse_str_list(attribs.get("ANTRIEBFAHRTRICHTUNG", "")), + "AnzahlKurvenAussen90": to_int("VF_Bogen_A_90"), + "AnzahlKurvenAussen60": to_int("VF_Bogen_A_60"), + "AnzahlKurvenAussen30": to_int("VF_Bogen_A_30"), + "AnzahlKurvenInnen90": to_int("VF_Bogen_I_90"), + "AnzahlKurvenInnen60": to_int("VF_Bogen_I_60"), + "AnzahlKurvenInnen30": to_int("VF_Bogen_I_30"), + }, + "Anzahl der Separatoren": to_int("ANZAHL_SEPARATOR"), + "Anzahl der Scanner": to_int("ANZAHL_SCANNER"), + } + + def build_kreisel_details(block): """Merkmale-Dict fuer einen ILS Kreisel.""" attribs = block.get("attribs", {}) @@ -164,8 +271,9 @@ def process_blocks(blocks, lookup): gerade_groups = {} kreisel_list = [] # Liste von blocks eckrad_ids = [] # IDs aller Eckrad-Bloecke - strecke_ids = [] # IDs aller Strecke-/Variofoerderer (VF_*, Vario/Staustrecke) - gefaelle_ids = [] # IDs aller Gefaellestrecken (GF_*) + strecke_ids = [] # IDs loser Strecke-Elemente (Vario/Staustrecke ausserhalb VF_N) + vario_list = [] # Liste von VarioFoerderer-Compound-Bloecken (VF_N), je einer Zeile + gefaelle_list = [] # Liste von Gefaellestrecke-Bloecken (GF_*), je einer Zeile counters = { "anzahl_boegen": 0, @@ -287,10 +395,12 @@ def process_blocks(blocks, lookup): continue # --- VarioFoerderer Compound-Block (VF_N) --- + # Bekommt immer eine eigene Detail-Zeile (vario_list) statt der + # generischen IDs-Gruppierung - siehe build_variofoerderer_details. if matches_any(bname, BLOCKPATTERNS.get("pattern_variofoerderer", [])): attribs = block.get("attribs", {}) counters["anzahl_variofoerderer"] += 1 - strecke_ids.append(get_id(block)) + vario_list.append(block) try: counters["laengen_variofoerderer"] += float(attribs.get("L_VF_m", "0") or "0") except (ValueError, TypeError): @@ -312,7 +422,7 @@ def process_blocks(blocks, lookup): if matches_any(bname, BLOCKPATTERNS.get("pattern_gefaellestrecke", [])): attribs = block.get("attribs", {}) counters["anzahl_gefaellestrecken"] += 1 - gefaelle_ids.append(get_id(block)) + gefaelle_list.append(block) try: counters["laengen_gefaellestrecken"] += float(attribs.get("L_GF_m", "0") or "0") except (ValueError, TypeError): @@ -412,7 +522,10 @@ def process_blocks(blocks, lookup): }) nr += 1 - if counters["anzahl_variofoerderer"] > 0: + # Lose Strecke-Elemente (Vario/Staustrecke ausserhalb eines VF_N-Blocks) + # als eine gruppierte Zeile; VF_N-Compound-Bloecke bekommen unten je eine + # eigene Detail-Zeile. + if strecke_ids: sivasnr, teileart = ILS_SIVASNR["strecke"] items.append({ "nr": nr, @@ -420,25 +533,56 @@ def process_blocks(blocks, lookup): "sivasnr": sivasnr, "sivasnr_quoted": True, "dispgruppe": 20, - "anzahl": counters["anzahl_variofoerderer"], + "anzahl": len(strecke_ids), "laenge_mm": "", "hoehe_m": "", "details": {"IDs": strecke_ids}, }) nr += 1 - if counters["anzahl_gefaellestrecken"] > 0: - sivasnr, teileart = ILS_SIVASNR["gefaellestrecke"] + # VarioFoerderer-Compound-Bloecke (VF_N) - je Block eine eigene Zeile mit + # vollstaendigen Gefaelle-/Angetrieben-Teilgruppen (immer TYP "Streckengruppe"). + for block in vario_list: + sivasnr, teileart = ILS_SIVASNR["strecke"] items.append({ "nr": nr, "teileart": teileart, "sivasnr": sivasnr, "sivasnr_quoted": True, "dispgruppe": 20, - "anzahl": counters["anzahl_gefaellestrecken"], + "anzahl": 1, "laenge_mm": "", "hoehe_m": "", - "details": {"IDs": gefaelle_ids}, + "details": build_variofoerderer_details(block), + }) + nr += 1 + + # Gleichartige Gefaellestrecken (identische Masse) zu einer Zeile + # zusammenfassen - nur IDs/Bezeichnung bleiben je Block unterschiedlich. + gefaelle_groups = {} + for block in gefaelle_list: + sig = gefaellestrecke_signature(block) + if sig not in gefaelle_groups: + gefaelle_groups[sig] = {"ids": [], "bezeichnungen": [], "block": block} + gefaelle_groups[sig]["ids"].append(get_id(block)) + gefaelle_groups[sig]["bezeichnungen"].append( + block.get("attribs", {}).get("Bezeichnung", block.get("block_name", ""))) + + for group in gefaelle_groups.values(): + sivasnr, teileart = ILS_SIVASNR["gefaellestrecke"] + details = build_gefaellestrecke_details(group["block"]) + details["IDs"] = group["ids"] + details["Bezeichnung"] = group["bezeichnungen"] + items.append({ + "nr": nr, + "teileart": teileart, + "sivasnr": sivasnr, + "sivasnr_quoted": True, + "dispgruppe": 20, + "anzahl": len(group["ids"]), + "laenge_mm": "", + "hoehe_m": "", + "details": details, }) nr += 1