Files
dxfmakros/lib/export_csv.py
T
m.stangl 1f2ebe5c86 export_csv.py und omniflo_utils.py bereinigt und korrigiert
lib/export_csv.py:
  - CSV-Header vereinfacht: NachbarIds, Planquadrat und rotation-Spalten
    entfernt (nicht im Sivas-Format benoetigt). Anzahl-Spalte auf festen
    Wert 1 gesetzt.
  - format_planquadrat() entfernt (war unused nach Header-Aenderung).
  - process_blocks(): elem_nr startet bei 0 statt -1 (erster Block war
    mit Nummer -1 ausgegeben worden).
  - build_bogen_merkmale(), build_weiche_merkmale(): KurvenWinkel und
    Radius werden jetzt explizit als float() gecastet, um sicherzustellen
    dass JSON-Werte aus dem Katalog als Zahl und nicht als String landen.
  - Felder nachbarids, planquadrat, rotation aus den item-Dicts entfernt.

lib/omniflo_utils.py:
  - import_element_as_block(): prueft jetzt zuerst ob eine Block-
    definition mit dem Zielnamen in der Quelldatei existiert (erzeugt
    durch set_attributs.py). Falls ja, wird diese direkt importiert
    statt des gesamten Modelspace. Das verhindert doppelte Entities wenn
    set_attributs.py die Geometrie bereits in einen Block gefasst hat.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 11:26:41 +02:00

261 lines
8.1 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
export_csv.py - Erzeugt eine einfache Item-Liste als CSV (ohne Summierung).
Aufruf:
python export_csv.py <export_raw.json> <data_dir> <output.csv>
CSV-Format:
Elementnummer;TeileArt;TeileId;NachbarIds;Bezeichnung;Planquadrat;rotation;Merkmale
"""
import json
import sys
import os
import uuid
# ---------------------------------------------------------------------------
# Gemeinsame Hilfsfunktionen (auch in export_sivas.py verwendet)
# ---------------------------------------------------------------------------
def load_json(path):
with open(path, "r", encoding="utf-8") as f:
return json.load(f)
def build_lookup(boegen, weichen):
"""Erzeugt ein Dict: SivasNr (als String) -> (typ, eintrag)"""
lookup = {}
for b in boegen:
lookup[str(b["Sivasnr"])] = ("bogen", b)
for w in weichen:
lookup[str(w["Sivasnr"])] = ("weiche", w)
return lookup
def generate_shape_id():
return "shape_" + str(uuid.uuid4())
# ---------------------------------------------------------------------------
# Merkmale-Builder
# ---------------------------------------------------------------------------
def get_hoehe(block):
"""Liest HOEHE aus Block-Attributen. Fallback: Z-Koordinate."""
attribs = block.get("attribs", {})
return attribs.get("HOEHE") or str(int(block.get("z", 0) or 0)) or "2000"
def get_drehung(block):
"""Liest DREHUNG aus Block-Attributen. Fallback: CAD-Rotation."""
attribs = block.get("attribs", {})
drehung_raw = attribs.get("DREHUNG")
if drehung_raw is not None:
try:
return float(drehung_raw)
except (ValueError, TypeError):
pass
return block.get("rotation", 0.0)
def build_bogen_merkmale(block, eintrag):
return {
"Kurvenwinkel": float(eintrag.get("KurvenWinkel", 0)),
"Radius": float(eintrag.get("Radius", 0)),
"Höhe": get_hoehe(block),
"Drehung": get_drehung(block),
"SivasNummer": str(eintrag.get("Sivasnr", ""))
}
def build_weiche_merkmale(block, eintrag):
wt = eintrag.get("WeichenTyp", "Einzelweiche")
return {
"Weichentyp": wt,
"Richtung": str(eintrag.get("KurvenRichtung", "")),
"Weichenwinkel": float(eintrag.get("KurvenWinkel", 0)),
"Höhe": get_hoehe(block),
"Drehung": get_drehung(block),
"Antrieb Kurve": eintrag.get("SivasnrTEF") is not None,
"SivasNummer": str(eintrag.get("Sivasnr", ""))
}
def build_gerade_merkmale(block):
return {
"Anzahl der Separatoren": "0",
"Länge in Meter": "2",
"Winkel": "0",
"Anzahl der Scanner": 0,
"Höhe oben": "2000",
"Höhe unten": "2000",
"Drehung": block.get("rotation", 0.0),
"SivasNummer": ""
}
def build_kreisel_merkmale(block):
attribs = block.get("attribs", {})
abstand_mm = attribs.get("ABSTAND", "2300")
try:
abstand_m = str(round(float(abstand_mm) / 1000.0, 2))
except (ValueError, TypeError):
abstand_m = "2.3"
hoehe_mm = attribs.get("HOEHE", "0")
try:
hoehe_m = str(round(float(hoehe_mm) / 1000.0, 2))
except (ValueError, TypeError):
hoehe_m = "0"
return {
"Abstand (Kreiselachse A - Kreiselachse) in Meter": abstand_m,
"Anzahl der Separatoren": attribs.get("N_SEPARATOREN", "2"),
"Kreiselart": attribs.get("KREISELART", "STANDARD"),
"Anzahl der Scanner": attribs.get("N_SCANNER", "0"),
"Anzahl der Rampen": attribs.get("N_RAMPEN", "0"),
"Höhe in m": hoehe_m,
"Drehrichtung": attribs.get("DREHRICHTUNG", "UZS"),
"Drehung": block.get("rotation", 0.0),
"Name": attribs.get("NAME", ""),
}
# ---------------------------------------------------------------------------
# Bekannte Blocknamen
# ---------------------------------------------------------------------------
SKIP_BLOCKS = {"K1", "K2", "K3", "K4", "KS_EIN", "KS_AUS"}
# ---------------------------------------------------------------------------
# Bloecke verarbeiten (einfache Liste, keine Summierung)
# ---------------------------------------------------------------------------
def process_blocks(blocks, lookup):
items = []
elem_nr = 0
bogen_count = 0
weiche_count = {}
gerade_count = 0
kreisel_count = 0
for block in blocks:
bname = block.get("block_name", "")
if bname in SKIP_BLOCKS:
continue
# Omniflo Bogen oder Weiche
if bname in lookup:
typ, eintrag = lookup[bname]
elem_nr += 1
shape_id = generate_shape_id()
if typ == "bogen":
bogen_count += 1
items.append({
"nr": elem_nr,
"teileart": "Omniflo Kurve",
"teileid": shape_id,
"bezeichnung": f"OFBogen :{bogen_count}",
"merkmale": build_bogen_merkmale(block, eintrag),
})
elif typ == "weiche":
wt = eintrag.get("WeichenTyp", "Einzelweiche")
weiche_count[wt] = weiche_count.get(wt, 0) + 1
items.append({
"nr": elem_nr,
"teileart": "Omniflo Weiche",
"teileid": shape_id,
"bezeichnung": f"OFWeiche :{weiche_count[wt]}",
"merkmale": build_weiche_merkmale(block, eintrag),
})
continue
# Omniflo Gerade (AP110)
if "AP110" in bname.upper() or "AP_110" in bname.upper():
elem_nr += 1
gerade_count += 1
items.append({
"nr": elem_nr,
"teileart": "Omniflo Gerade",
"teileid": generate_shape_id(),
"bezeichnung": f"OFGerade :{gerade_count}",
"merkmale": build_gerade_merkmale(block),
})
continue
# ILS Kreisel (Compound-Bloecke mit Praefix KR_)
if bname.startswith("KR_"):
elem_nr += 1
kreisel_count += 1
items.append({
"nr": elem_nr,
"teileart": "ILS 2.0 Kreisel",
"teileid": generate_shape_id(),
"bezeichnung": f"Kreisel :{kreisel_count}",
"merkmale": build_kreisel_merkmale(block),
})
continue
return items
# ---------------------------------------------------------------------------
# CSV-Formatierung
# ---------------------------------------------------------------------------
def format_csv_line(item):
merkmale_json = json.dumps(item["merkmale"], ensure_ascii=False)
return (
f'{item["nr"]}'
f';"{item["teileart"]}"'
f';"{item["teileid"]}"'
f';"{item["bezeichnung"]}"'
f';1'
f';{merkmale_json}'
)
# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------
def main():
if len(sys.argv) < 4:
print("Aufruf: python export_csv.py <export_raw.json> <data_dir> <output.csv>")
sys.exit(1)
raw_json_path = sys.argv[1]
data_dir = sys.argv[2]
output_csv = sys.argv[3]
blocks = load_json(raw_json_path)
boegen_path = os.path.join(data_dir, "json", "omniflo_boegen.json")
weichen_path = os.path.join(data_dir, "json", "omniflo_weichen.json")
boegen = load_json(boegen_path) if os.path.exists(boegen_path) else []
weichen = load_json(weichen_path) if os.path.exists(weichen_path) else []
lookup = build_lookup(boegen, weichen)
print(f"[export_csv] {len(blocks)} Bloecke geladen, "
f"{len(boegen)} Boegen, {len(weichen)} Weichen im Katalog.")
items = process_blocks(blocks, lookup)
header = "Elementnummer;TeileArt;TeileId;Bezeichnung;Anzahl;Merkmale"
with open(output_csv, "w", encoding="utf-8") as f:
f.write(header + "\n")
for item in items:
f.write(format_csv_line(item) + "\n")
print(f"[export_csv] CSV geschrieben: {output_csv} ({len(items)} Zeilen)")
if __name__ == "__main__":
main()