Files
dxfmakros/lib/omniflo_utils.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

241 lines
8.1 KiB
Python

"""
Gemeinsame Hilfsfunktionen fuer DXF-Makros.
Enthaelt die Reihen-Gruppierung fuer Omniflo-Elemente und Layout-Funktionen
fuer die Uebersichts-DXF Erzeugung.
"""
import json
import os
import ezdxf
from ezdxf import bbox as ezdxf_bbox
from ezdxf.addons.importer import Importer
# ---------------------------------------------------------------------------
# Reihen-Gruppen: (Label, Quell-Key, Filter-Funktion)
# ---------------------------------------------------------------------------
ROW_GROUPS = [
("Boegen 22.5", "boegen",
lambda b: b["KurvenWinkel"] == 22.5),
("Boegen 45", "boegen",
lambda b: b["KurvenWinkel"] == 45),
("Boegen 67.5", "boegen",
lambda b: b["KurvenWinkel"] == 67.5),
("Boegen 90", "boegen",
lambda b: b["KurvenWinkel"] == 90),
("Boegen 180", "boegen",
lambda b: b["KurvenWinkel"] == 180),
("Weichenkoerper Einzel", "weichen",
lambda w: w["WeichenTyp"] == "Einzelweiche" and w["KurvenWinkel"] == 22.5),
("Weichenkoerper Doppel", "weichen",
lambda w: w["WeichenTyp"] == "Doppelweiche" and w["KurvenWinkel"] == 22.5),
("Weichenkoerper Dreiwege", "weichen",
lambda w: w["WeichenTyp"] == "Dreiwegeweiche" and w["KurvenWinkel"] == 22.5),
("Einzelweiche 45", "weichen",
lambda w: w["WeichenTyp"] == "Einzelweiche" and w["KurvenWinkel"] == 45),
("Einzelweiche 90", "weichen",
lambda w: w["WeichenTyp"] == "Einzelweiche" and w["KurvenWinkel"] == 90),
("Einzelweiche Parallel", "weichen",
lambda w: w["WeichenTyp"] == "Einzelweiche" and w["KurvenWinkel"] == 0),
("Doppelweiche 45", "weichen",
lambda w: w["WeichenTyp"] == "Doppelweiche" and w["KurvenWinkel"] == 45),
("Doppelweiche 90", "weichen",
lambda w: w["WeichenTyp"] == "Doppelweiche" and w["KurvenWinkel"] == 90),
("Doppelweiche Parallel", "weichen",
lambda w: w["WeichenTyp"] == "Doppelweiche" and w["KurvenWinkel"] == 0),
("Dreiwegeweiche 45", "weichen",
lambda w: w["WeichenTyp"] == "Dreiwegeweiche" and w["KurvenWinkel"] == 45),
("Dreiwegeweiche 90", "weichen",
lambda w: w["WeichenTyp"] == "Dreiwegeweiche" and w["KurvenWinkel"] == 90),
("Dreiwegeweiche Parallel", "weichen",
lambda w: w["WeichenTyp"] == "Dreiwegeweiche" and w["KurvenWinkel"] == 0),
("Dreifachweiche", "weichen",
lambda w: w["WeichenTyp"] == "Dreifachweiche"),
("Deltaweiche", "weichen",
lambda w: w["WeichenTyp"] == "Deltaweiche"),
("Sternweiche", "weichen",
lambda w: w["WeichenTyp"] == "Sternweiche"),
]
# ---------------------------------------------------------------------------
# Schalter-Filter: (Label, JSON-Datei, Filter-Funktion)
# Gemeinsam genutzt von set_einfuegepkt.py und set_koords.py.
# ---------------------------------------------------------------------------
SWITCH_FILTERS = {
"boegen": (
"Boegen",
"omniflo_boegen.json",
lambda item: True,
),
"weichen45": (
"Weichen 45 Grad",
"omniflo_weichen.json",
lambda item: item["KurvenWinkel"] == 45
and item["WeichenTyp"] not in ("Dreifachweiche", "Deltaweiche"),
),
"weichen90": (
"Weichen 90 Grad",
"omniflo_weichen.json",
lambda item: item["KurvenWinkel"] == 90
and item["WeichenTyp"] not in ("Dreifachweiche", "Deltaweiche"),
),
"weichenkoerper": (
"Weichenkoerper",
"omniflo_weichen.json",
lambda item: item["KurvenWinkel"] == 22.5,
),
"weichen_parallel": (
"Weichen Parallel",
"omniflo_weichen.json",
lambda item: item["KurvenWinkel"] == 0 and item["WeichenTyp"] != "Sternweiche",
),
"sternweiche": (
"Sternweiche",
"omniflo_weichen.json",
lambda item: item["WeichenTyp"] == "Sternweiche",
),
"delta": (
"Deltaweichen",
"omniflo_weichen.json",
lambda item: item["WeichenTyp"] == "Deltaweiche",
),
"dreifachweiche": (
"Dreifachweichen",
"omniflo_weichen.json",
lambda item: item["WeichenTyp"] == "Dreifachweiche",
),
}
PADDING_X = 200
PADDING_Y = 400
TEXT_HEIGHT = 30
CROSS_SIZE = 40
TEXT_MARGIN = 20
ROW_LABEL_WIDTH = 600
def load_omniflo_data(data_dir):
"""Laedt Boegen- und Weichen-JSON und gibt ein Dict {boegen, weichen} zurueck."""
boegen_path = os.path.join(data_dir, "json", "omniflo_boegen.json")
weichen_path = os.path.join(data_dir, "json", "omniflo_weichen.json")
with open(boegen_path, "r", encoding="utf-8") as f:
boegen_data = json.load(f)
with open(weichen_path, "r", encoding="utf-8") as f:
weichen_data = json.load(f)
return {"boegen": boegen_data, "weichen": weichen_data}
def is_bogen(sivasnr, sources):
"""Prueft ob eine Sivasnr ein Bogen ist."""
return any(str(b["Sivasnr"]) == str(sivasnr) for b in sources["boegen"])
def build_row_layout(data_dir, results_dir):
"""
Berechnet das Layout aller Omniflo-Elemente in Reihen.
Returns:
(sources, rows) wobei rows eine Liste von Dicts ist:
[{
'label': str,
'source_key': str,
'elements': [{'sivasnr', 'source', 'extmin', 'extmax',
'width', 'height', 'offset_x', 'offset_y'}],
'cursor_y': float,
'max_height': float,
}]
"""
sources = load_omniflo_data(data_dir)
omniflo_dir = os.path.join(data_dir, "omniflo")
rows = []
cursor_y = 0.0
for label, source_key, filter_func in ROW_GROUPS:
items = [item for item in sources[source_key] if filter_func(item)]
if not items:
continue
row_elements = []
for item in items:
sivasnr = str(item["Sivasnr"])
dxf_path_result = os.path.join(results_dir, f"{sivasnr}.dxf")
dxf_path_orig = os.path.join(omniflo_dir, f"{sivasnr}.dxf")
dxf_path = dxf_path_result if os.path.exists(dxf_path_result) else dxf_path_orig
if not os.path.exists(dxf_path):
continue
source_doc = ezdxf.readfile(dxf_path)
bb = ezdxf_bbox.extents(source_doc.modelspace())
if not bb.has_data:
continue
row_elements.append({
'sivasnr': sivasnr,
'source': source_doc,
'extmin': bb.extmin,
'extmax': bb.extmax,
'width': bb.extmax[0] - bb.extmin[0],
'height': bb.extmax[1] - bb.extmin[1],
})
if not row_elements:
continue
max_height = max(e['height'] for e in row_elements)
cursor_x = 0.0
for elem in row_elements:
elem['offset_x'] = cursor_x - elem['extmin'][0]
elem['offset_y'] = cursor_y - elem['extmin'][1]
cursor_x += elem['width'] + PADDING_X
rows.append({
'label': label,
'source_key': source_key,
'elements': row_elements,
'cursor_y': cursor_y,
'max_height': max_height,
})
cursor_y -= max_height + TEXT_HEIGHT + TEXT_MARGIN * 2 + PADDING_Y
return sources, rows
def import_element_as_block(source_doc, target_doc, block_name):
"""
Importiert ein Quell-Dokument als Block in target_doc.
Wenn das Quell-Dokument eine Blockdefinition mit dem Namen block_name
enthaelt (erzeugt durch set_attributs.py), wird diese direkt importiert.
Andernfalls werden alle Modelspace-Entities als Block importiert.
"""
importer = Importer(source_doc, target_doc)
importer.import_tables()
blk = target_doc.blocks.new(name=block_name)
if block_name in source_doc.blocks:
source_layout = source_doc.blocks[block_name]
else:
source_layout = source_doc.modelspace()
for entity in source_layout:
importer.import_entity(entity, blk)
importer.finalize()
return block_name
def draw_cross(msp, x, y, size, color, layer):
"""Zeichnet ein Kreuz an (x, y)."""
msp.add_line((x - size, y), (x + size, y),
dxfattribs={'layer': layer, 'color': color})
msp.add_line((x, y - size), (x, y + size),
dxfattribs={'layer': layer, 'color': color})