Files
dxfmakros/lib/export_csv.py
T

271 lines
8.7 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 build_bogen_merkmale(block, eintrag):
return {
"Länge in Meter": "0",
"Kurvenwinkel": eintrag.get("KurvenWinkel", 0),
"Radius": eintrag.get("Radius", 0),
"Höhe": "2000",
"Drehung": block.get("rotation", 0.0),
"SivasNummer": str(eintrag.get("Sivasnr", ""))
}
def build_weiche_merkmale(block, eintrag):
wt = eintrag.get("WeichenTyp", "Einzelweiche")
return {
"Länge in Meter links": None,
"Länge in Meter rechts": None,
"Länge in Meter gerade": None,
"Weichentyp": wt,
"Richtung": str(eintrag.get("KurvenRichtung", "")),
"Weichenwinkel": eintrag.get("KurvenWinkel", 0),
"Höhe": "2000",
"Drehung": block.get("rotation", 0.0),
"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"}
# ---------------------------------------------------------------------------
# Planquadrat aus Block-Koordinaten
# ---------------------------------------------------------------------------
def format_planquadrat(block):
x = block.get("x", 0.0)
y = block.get("y", 0.0)
return f"X:{x:.2f} Y:{y:.2f}"
# ---------------------------------------------------------------------------
# Bloecke verarbeiten (einfache Liste, keine Summierung)
# ---------------------------------------------------------------------------
def process_blocks(blocks, lookup):
items = []
elem_nr = -1
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,
"nachbarids": "",
"bezeichnung": f"OFBogen :{bogen_count}",
"planquadrat": format_planquadrat(block),
"rotation": block.get("rotation", 0.0),
"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,
"nachbarids": "",
"bezeichnung": f"OFWeiche :{weiche_count[wt]}",
"planquadrat": format_planquadrat(block),
"rotation": block.get("rotation", 0.0),
"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(),
"nachbarids": "",
"bezeichnung": f"OFGerade :{gerade_count}",
"planquadrat": format_planquadrat(block),
"rotation": block.get("rotation", 0.0),
"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(),
"nachbarids": "",
"bezeichnung": f"Kreisel :{kreisel_count}",
"planquadrat": format_planquadrat(block),
"rotation": block.get("rotation", 0.0),
"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["nachbarids"]}'
f';"{item["bezeichnung"]}"'
f';"{item["planquadrat"]}"'
f';'
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;NachbarIds;Bezeichnung;Planquadrat;rotation;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()