Files
kabellaengen/lib/drawdxf.py
T

253 lines
7.7 KiB
Python
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import argparse
import ezdxf
import json
import os.path
from dataclasses import dataclass, asdict, fields
from dacite import from_dict
from typing import List
from datetime import datetime
from openpyxl import Workbook
import math
from collections import defaultdict
import configparser
@dataclass
class Point:
x: float
y: float
@dataclass
class Polyline:
id: str
coords: List[Point]
length: float
def to_tuple(self):
ret = list()
for p in self.coords:
ret.append( (p.x,p.y) )
return ret
@dataclass
class Polylines:
kabel: List[Polyline]
def add_polyline(msp, points:Polyline, dxf_attribs):
pts = points.to_tuple()
pline = msp.add_lwpolyline(points=pts, dxfattribs=dxf_attribs)
pline.rgb = (255, 128, 0)
def new_dxf(plines, out_path):
""" creates a new dxf file with a polyline inside which is created by the given json file
"""
print("creating dxf ..")
doc = ezdxf.new('R2018', setup=True)
create_cables(plines, doc)
doc.saveas(out_path)
print("done")
def modify_original_dxf(plines, originaldxf):
""" adds new layer to original .dxf-file that contains cables
"""
print("adding cables to original .dxf")
doc = ezdxf.readfile(originaldxf)
create_cables(plines, doc)
doc.saveas(out_path)
print("done")
def copy_layers_into_new(originaldxf, outpath):
""" creates a new dxf file with a racks, sensors, subdists from original file including cable paths
"""
print("copying layers from original .dxf into new .dxf")
quelle = ezdxf.readfile(originaldxf)
ziel = ezdxf.readfile(outpath)
copy_layers_into_dxf_by_filter(quelle, ziel)
create_cables(plines, quelle)
ziel.saveas(out_path)
print("done")
def create_cables(plines, doc):
msp = doc.modelspace()
# # Beispiel für Text und Polyline
# add_text(msp)
# Layer mit Zeitstempel erstellen (z.B. "Layer_2025-05-07_14-30")
timestamp = datetime.now().strftime("Kabel_%Y-%m-%d_%H-%M")
if timestamp not in doc.layers:
doc.layers.add(name=timestamp, color=7)
dxfattribs={"layer": timestamp}
for pl in plines.kabel:
add_polyline(msp, pl, dxfattribs)
def model_from_json(json_file):
with open(json_file, encoding='utf-8') as fh:
data = json.load(fh)
plines = from_dict(
data_class=Polylines,
data=data
)
return plines
def export_excel(json_file, out_path):
# Hier für Excel Export
print("creating excel file ..")
plines = model_from_json(json_file)
write_excel_from_json(plines, out_path)
print("done")
def write_excel_from_json(plines:Polylines, outpath:str):
wb = Workbook()
length_summary = defaultdict(int)
#Worksheet 1 - Kabellängen
ws1 = wb.active
ws1.title = "Kabellängen"
ws1.append(["Kabel-ID", "Länge aus Json (m)", "Gerundete Länge (m)"])
for pl in plines.kabel:
length = pl.length /1000 # Umrechnung von mm in m
rounded_len = math.ceil(length/10)*10
length_summary[rounded_len] +=1
ws1.append([pl.id, length, int(rounded_len)])
# Worksheet 2 - Zusammengefasste Längen
ws2 = wb.create_sheet("Längenübersicht")
ws2.append(["Gerundete Länge (m)", "Anzahl Kabel"])
for rlength in sorted(length_summary):
ws2.append([int(rlength), length_summary[rlength]])
wb.save(outpath)
def check_file_in_work(work_dir, filename):
fexists = True
if not os.path.exists(filename):
mypath = os.path.join(work_dir, filename)
if not os.path.exists(mypath):
fexists = False
else:
mypath = filename
return (mypath, fexists)
def copy_layers_into_dxf_by_filter(dxf_source: ezdxf.document.Drawing, dxf_target:ezdxf.document.Drawing):
msp_source = dxf_source.modelspace()
msp_target = dxf_target.modelspace()
subdist_layers = list(config.items('GetPos-Layer_Distributors'))
rack_layers = list(config.items('GetPos-Layer_Racks'))
equipment_layers = list(config.items('GetPos-Layer_Equipment'))
tunnel_layers = list(config.items('GetPos-Layer_Tunnel'))
layernames = list()
layernames.extend(subdist_layers)
layernames.extend(rack_layers)
layernames.extend(equipment_layers)
layernames.extend(tunnel_layers)
for (layername,v) in layernames:
if layername not in dxf_source.layers:
continue
# Falls der Layer noch nicht im Zieldokument existiert, neu anlegen
if layername not in dxf_target.layers:
quelle_layer = dxf_source.layers.get(layername)
dxf_target.layers.add(
name=layername,
color=quelle_layer.color,
linetype=quelle_layer.dxf.linetype,
lineweight=quelle_layer.dxf.lineweight
)
# Alle Entities auf diesem Layer kopieren
for entity in msp_source.query(f"*[layer=='{layername}']"):
msp_target.add_entity(entity.copy())
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='draws a dxf file with the given cable coordinates', prog='drawdxf')
parser.add_argument('-f', '--filename', action='store', required=True, help='this json file contains all cables and its coordinates which should be drawn. Saved with an unique timestamp', metavar='myfile.json')
parser.add_argument('-d', '--dxf', action='store', help='this dxf drawing will be copied and the new layer with the cables will be added. Original file must be added with --origin', metavar='myfile.dxf')
parser.add_argument('-c', '--copy_layer', action='store', help='copy layers of racks, sensors, distributors into a new .dxf-file. File also contains cable paths. Original file must be added with --origin', metavar='original.dxf')
parser.add_argument('-n', '--new', action='store', help='create a new dxf file only with cables in it. Name is basename and a timestamp')
parser.add_argument('-x', '--excel', action='store', help='create a xlsx file with cables data', metavar='allCables.xls')
parser.add_argument('-o', '--origin', action='store', help='name of original .dxf file used by -d and -a', metavar='original.dxf')
args = parser.parse_args()
config_dir = os.environ.get("PROJECT_CFG")
work_dir = os.fspath(os.environ.get('PROJECT_WORK'))
json_file = args.filename
(json_path, jexists) = check_file_in_work(work_dir, json_file)
if not jexists:
print(f"file {json_file} does not exist")
parser.print_help()
exit()
plines = model_from_json(json_path)
config = configparser.ConfigParser(allow_no_value=True, delimiters=("="))
config.optionxform = lambda option: option # preserve case for letters
config.read(os.path.join(config_dir, "allgemein.cfg"))
dxf_file = args.dxf
if args.dxf or args.copy_layer:
if not args.origin:
parser.print_help()
exit()
else:
(origin_path, dexists) = check_file_in_work(work_dir, args.origin)
if args.dxf:
(dxf_path, dexists) = check_file_in_work(work_dir, dxf_file)
if not dexists:
print(f"file {dxf_file} does not exist")
parser.print_help()
exit()
out_path = dxf_path
res_pos = new_dxf(plines, dxf_path)
if args.copy_layer:
out_path = os.path.join(work_dir, args.copy_layer)
res_pos = new_dxf(plines, out_path)
copy_layers_into_new(origin_path, out_path)
if args.new:
# erzeuge dxf Datei nur mit Kabeln
out_path = os.path.join(work_dir, args.new)
res_pos = new_dxf(plines, out_path)
if args.excel:
excel_path = os.path.join(work_dir, args.excel)
export_excel(json_path, excel_path)