Alle TEFWEICHEN sind eingepflegt.

This commit is contained in:
2025-09-18 14:55:11 +02:00
parent ad39d3335e
commit 0407b2d044
459 changed files with 15010 additions and 7260 deletions
@@ -385,11 +385,11 @@ if __name__ == '__main__':
in_dir = args.inputdir
else:
if args.bogen:
in_dir = os.environ.get('RD_CONF_BOGEN')
in_dir = os.environ.get('RD_CONF_OFBOGEN')
elif args.tefbogen:
in_dir = os.environ.get('RD_CONF_TEFBOGEN')
elif args.weichen:
in_dir = os.environ.get('RD_CONF_WEICHEN')
in_dir = os.environ.get('RD_CONF_OFWEICHEN')
elif args.tefweichen:
in_dir = os.environ.get('RD_CONF_TEFWEICHEN')
@@ -126,7 +126,7 @@ def convert_svg_to_xml(svg_path, output_dir):
def update_txt_file(txt_path, xml_rel_path):
"""Update path in TXT file"""
try:
with open(txt_path, "r", encoding="UTF-8") as f:
with open(txt_path, "r", encoding="UTF-8-sig") as f:
data = json.load(f)
if "srcSVG" not in data:
@@ -241,7 +241,7 @@ def process_json_file(input_filename, output_filename=None):
if __name__ == "__main__":
try:
# Get JSON_PATH from environment
json_path = os.environ.get("JSON_PATH","JSON")
json_path = os.environ.get("JSON_PATH_TEFBOGEN","JSON")
# Default filenames
input_filename = "1_TEF_Boegen_input.json"
@@ -145,7 +145,7 @@ def process_files(json_file_path, txt_files_dir):
# Example usage
if __name__ == "__main__":
json_path = os.environ.get("JSON_PATH", "JSON")
json_path = os.environ.get("JSON_PATH_TEFBOGEN", "JSON")
json_file_path = os.path.join(json_path, "1_TEF_Boegen_output.json")
txt_files_dir = os.environ.get("PROPS_PATH", "props")
process_files(json_file_path, txt_files_dir)
@@ -107,9 +107,9 @@ def main():
Main processing function
"""
# Configure paths
json_path=os.environ.get("JSON_PATH","JSON")
json_path=os.environ.get("JSON_PATH_TEFBOGEN","JSON")
json_file_path = os.path.join(json_path,"1_TEF_Boegen_input.json")
svg_folder_path = os.environ.get("XML_PATH","svg")
svg_folder_path = os.environ.get("SVG_PATH","svg")
try:
@@ -2,9 +2,9 @@
set TEFBogen_PATH=%~dp0
set XML_PATH=C:\Program Files\RuleDesigner\RDConfigurator Fusion\WebApi\Editor2D\SSG\shapes\svg
set SVG_PATH=C:\Program Files\RuleDesigner\RDConfigurator Fusion\WebApi\Editor2D\SSG\shapes\svg
set JSON_PATH=%TEFBogen_PATH%JSON
set JSON_PATH_TEFBOGEN=%TEFBogen_PATH%JSON
python 4_TEFBogen_SVG_XML_Modifier_Script.py
+1 -1
View File
@@ -3,7 +3,7 @@
set TEFBogen_PATH=%~dp0
set PROPS_PATH=C:\Program Files\RuleDesigner\RDConfigurator Fusion\WebApi\Editor2D\SSG\shapes\props
set JSON_PATH=%TEFBogen_PATH%JSON
set JSON_PATH_TEFBOGEN=%TEFBogen_PATH%JSON
python 1_process_TEFBogen_json_1.py
python 2_update_props_TEF_Boegen_from_json_1.py
@@ -0,0 +1,136 @@
import json
import os
import re
def process_json_file(input_file, output_file):
os.makedirs(os.path.dirname(output_file), exist_ok=True)
if not os.path.exists(input_file):
raise FileNotFoundError(f"输入文件不存在: {input_file}")
with open(input_file, 'r', encoding='utf-8') as f:
data = json.load(f)
DIRECTION_RULES = {
"CP1": 180,
"CPR1": 180,
"CPL1": 180,
"CP2": lambda kw: round(360 - kw, 1),
"CPR2": lambda kw: round(360 - kw, 1),
"CPL2": lambda kw: round(360 - kw, 1),
"CP3": lambda kw: round(kw, 1),
"CPR3": lambda kw: round(kw, 1),
"CPL3": lambda kw: round(kw, 1),
"CP4": 0
}
# Process each item in the JSON data
for item in data:
if item.get("SivasnrTEF") is not None:
# Step 1: Calculate pixel dimensions
width_mm = item["Objekt_width_mm"]
height_mm = item["Objekt_height_mm"]
# Calculate initial pixel values
item["Objekt_width_px"] = round(width_mm * 3.7795275, 4)
item["Objekt_height_px"] = round(height_mm * 3.7795275, 4)
# Determine which dimension is larger and calculate scaling factor
if width_mm >= height_mm:
scale = 1000 / width_mm
item["calculated_SVG_width_px"] = 1000.0
item["calculated_SVG_height_px"] = round(height_mm * scale, 6)
scale_RD_H = round(1000 / item["calculated_SVG_height_px"], 6) if item["calculated_SVG_height_px"] > 0 else 1
scale_RD_W = 1
else:
scale = 1000 / height_mm
item["calculated_SVG_width_px"] = round(width_mm * scale, 6)
item["calculated_SVG_height_px"] = 1000.0
scale_RD_W = round(1000 / item["calculated_SVG_width_px"], 6) if item["calculated_SVG_width_px"] > 0 else 1
scale_RD_H = 1
item["scale_factor"] = round(scale, 6)
item["scale_factor_RD_Width"] = round(scale_RD_W, 6)
item["scale_factor_RD_Height"] = round(scale_RD_H, 6)
# Process connection points -
connection_points = []
kurven_winkel = item["KurvenWinkel"]
cp_fields = {}
for key in item.keys():
if key.endswith('_x_mm'):
base_key = key[:-5]
y_key = base_key + '_y_mm'
if y_key in item:
cp_fields[base_key] = (key, y_key)
for base_key, (x_key, y_key) in cp_fields.items():
try:
if base_key.startswith('OFWeiche_'):
cp_type = base_key.replace('OFWeiche_', '')
link_class = "Omniflo"
elif base_key.startswith('TEFWeiche_'):
cp_type = base_key.replace('TEFWeiche_', '')
link_class = "TEFOmniflo"
else:
continue
cp_id = cp_type.lower()
direction_rule = DIRECTION_RULES.get(cp_type)
if direction_rule is None:
print(f"警告: 未找到连接点 {cp_type} 的方向规则,使用默认值0")
direction = 0
elif callable(direction_rule):
direction = direction_rule(kurven_winkel)
else:
direction = direction_rule
x_px = round(item[x_key] * scale * scale_RD_W, 3)
y_px = round(item[y_key] * scale * scale_RD_H, 3)
connection_points.append({
"id": cp_id,
"x": x_px,
"y": y_px,
"direction": direction,
"linkClass": link_class
})
print(f"cp: {cp_id}: x={x_px}, y={y_px}, direction={direction}, linkClass={link_class}")
except Exception as e:
print(f"cp: {base_key} error: {e}")
continue
connection_points.sort(key=lambda x: x["id"])
item["connectionPoints"] = connection_points
print(f"Processed {item['Sivasnr']}: find: {len(connection_points)} CPs")
# Save the processed data
with open(output_file, 'w', encoding='utf-8') as f:
json.dump(data, f, indent=2, ensure_ascii=False)
if __name__ == "__main__":
json_path = os.environ.get("JSON_PATH_TEFWEICHE", "JSON")
input_filename = os.path.join(json_path, "omniflo_TEF_weichen.json")
output_filename = os.path.join(json_path, "omniflo_TEF_weichen_output.json")
try:
process_json_file(input_filename, output_filename)
print(f"completed, saved as: {output_filename}")
except Exception as e:
print(f"error: {str(e)}")
@@ -0,0 +1,172 @@
''' Script Analysis
This Python script processes JSON and TXT files to update dimensions and connection points in SVG-related data, but only for entries where SivasnrTEF is null. Here's the main logic:
Input Handling:
Reads a JSON file containing reference data
Processes all TXT files in a specified directory
Data Processing:
Creates a mapping between Sivasnr (from filenames) and JSON data (only for entries with SivasnrTEF = null)
For each matching TXT file:
Updates width and height based on JSON data (converting mm to px)
Updates connection points (x, y, direction) from JSON data
Preserves the original file structure while updating specific values
Reporting:
Prints detailed change reports to console
Skips files without matching JSON data or where SivasnrTEF is not null '''
import json
import os
import glob
def process_files(json_file_path, txt_files_dir):
if not os.path.exists(json_file_path):
print(f"Error: JSON file does not exist at {json_file_path}")
exit(1)
if not os.path.exists(txt_files_dir):
print(f"Error: Directory does not exist at {txt_files_dir}")
exit(1)
# Read JSON file
with open(json_file_path, 'r', encoding='utf-8') as f:
json_data = json.load(f)
# Create Sivasnr to JSON data mapping ONLY for items with SivasnrTEF is not null
sivasnr_mapping = {str(item["Sivasnr"]): item for item in json_data if item["SivasnrTEF"] is not None}
txt_sivasnrs = set()
for txt_file_path in glob.glob(os.path.join(txt_files_dir, '*.txt')):
filename = os.path.basename(txt_file_path)
sivasnr = os.path.splitext(filename)[0]
txt_sivasnrs.add(sivasnr)
unmatched_json_items = []
for item in json_data:
if item["SivasnrTEF"] is not None and str(item["Sivasnr"]) not in txt_sivasnrs:
unmatched_json_items.append(item)
print(f"JSON Items: {len(json_data)}")
print(f"SivasnrTEF is not null : {len(sivasnr_mapping)}")
print(f"SivasnrTEF is null {len([item for item in json_data if item['SivasnrTEF'] is None])}")
print(f"SivasnrTEF record exists but no corresponding text file found in folder: {len(unmatched_json_items)}")
print("="*50)
if unmatched_json_items:
print("\n SivasnrTEF record exists but no corresponding text file found in folder:")
print("-" * 50)
for item in unmatched_json_items:
print(f"Sivasnr: {item['Sivasnr']}")
print(f"ProfilTyp: {item.get('ProfilTyp', 'N/A')}")
print(f"WeichenTyp: {item.get('WeichenTyp', 'N/A')}")
print(f"SivasnrTEF: {item.get('SivasnrTEF', 'N/A')}")
print("-" * 30)
# Initialize counters
total_files = 0
processed_files = 0
matched_files = 0
# Process all TXT files
for txt_file_path in glob.glob(os.path.join(txt_files_dir, '*.txt')):
total_files += 1
filename = os.path.basename(txt_file_path)
sivasnr = os.path.splitext(filename)[0]
# Check if corresponding JSON data exists and SivasnrTEF is not null
if sivasnr in sivasnr_mapping:
matched_files += 1
json_item = sivasnr_mapping[sivasnr]
try:
# Read TXT file content
with open(txt_file_path, 'r', encoding='utf-8-sig') as f:
txt_content = json.load(f)
# Update width and height
txt_content["width"] = round(json_item["Objekt_width_px"], 4)
txt_content["height"] = round(json_item["Objekt_height_px"], 4)
txt_cp_count = len(txt_content["connectionPoints"])
json_cp_count = len(json_item["connectionPoints"])
if txt_cp_count != json_cp_count:
print(f"Warning: Connection point count mismatch in {filename} (TXT: {txt_cp_count}, JSON: {json_cp_count})")
new_connection_points = []
for json_cp in json_item["connectionPoints"]:
new_cp = {
"id": json_cp["id"],
"x": json_cp["x"],
"y": json_cp["y"],
"z": 0.0,
"xMin": "",
"xMax": "",
"xStep": "",
"yMin": "",
"yMax": "",
"yStep": "",
"zMin": "",
"zMax": "",
"zStep": "",
"direction": json_cp["direction"],
"linkClass": json_cp["linkClass"],
"label": "",
"isDimension": True
}
new_connection_points.append(new_cp)
print(f" Added {json_cp['id']}: x={json_cp['x']}, y={json_cp['y']}, direction={json_cp['direction']}, linkClass={json_cp['linkClass']}")
txt_content["connectionPoints"] = new_connection_points
# Write back to TXT file
with open(txt_file_path, 'w', encoding='utf-8') as f:
json.dump(txt_content, f, indent=2, ensure_ascii=False)
print(f"Successfully processed file: {filename} (替换了 {json_cp_count} 个连接点)")
processed_files += 1
except Exception as e:
print(f"Error processing file {filename}: {str(e)}")
else:
matching_in_json = any(str(item["Sivasnr"]) == sivasnr for item in json_data)
''' if matching_in_json:
print(f"Skipped file: {filename} (SivasnrTEF is null)")
else:
print(f"Skipped file: {filename} (not found in JSON)") '''
# Print final statistics
print("\n" + "="*50)
print("Processing Complete - Summary:")
print(f"JSON文件总项数: {len(json_data)}")
print(f"JSON中 SivasnrTEF 不为 null 的项数: {len(sivasnr_mapping)}")
print(f"未匹配的JSON项数: {len(unmatched_json_items)}")
print(f"TXT目录中找到的文件数: {total_files}")
print(f"在JSON中找到匹配的TXT文件数: {matched_files}")
print(f"成功处理的文件数: {processed_files}")
if matched_files > 0:
success_rate = (processed_files / matched_files) * 100
print(f"处理成功率: {success_rate:.2f}%")
# 再次显示未匹配的JSON项统计
if unmatched_json_items:
print(f"\n注意: 有 {len(unmatched_json_items)} 个JSON项 (SivasnrTEF不为null) 在TXT目录中没有对应的文件")
print("这些项可能需要手动检查或创建对应的TXT文件")
print("="*50)
if __name__ == "__main__":
json_path = os.environ.get("JSON_PATH_TEFWEICH", "JSON")
json_file_path = os.path.join(json_path, "omniflo_TEF_weichen_output.json")
txt_files_dir = r"C:\Program Files\RuleDesigner\RDConfigurator Fusion\WebApi\Editor2D\SSG\shapes\props"
process_files(json_file_path, txt_files_dir)
@@ -0,0 +1,385 @@
import os
import re
import xml.etree.ElementTree as ET
from xml.dom import minidom
import argparse
def analyze_and_normalize_path(d, path_type, path_id=""):
"""Strictly normalize path direction and generate detailed report"""
original_d = d
report = []
normalized = []
changed = False
# First check if path contains any arc commands
if 'A' in d or 'a' in d:
report.append(" Path contains arc commands - leaving unchanged")
return original_d, report, False
# Ensure path starts with M command
if not d.strip().startswith('M'):
d = 'M' + d[1:] if d.startswith('L') else 'M ' + d
report.append(" Fix: Added M command at path start")
changed = True
# Parse path commands
commands = []
current_cmd = None
for token in re.split('([A-Za-z])', d):
if not token:
continue
if token in 'MLAZHVCSQTa-z':
current_cmd = token
else:
if current_cmd:
commands.append((current_cmd, token.strip()))
prev_x, prev_y = None, None
for cmd, params in commands:
params = [float(p) for p in re.split('[, ]+', params.strip()) if p]
if cmd == 'M':
if len(params) >= 2:
x, y = params[0], params[1]
normalized.append(f"M {x} {y}")
prev_x, prev_y = x, y
continue
if path_type == 'Line segment' and cmd == 'L':
if len(params) >= 2 and prev_x is not None:
x, y = params[0], params[1]
need_swap = x < prev_x or (x == prev_x and y < prev_y)
if need_swap:
new_segment = [f"M {x} {y}", f"L {prev_x} {prev_y}"]
report.append(f" Need to swap start/end → New path: {' '.join(new_segment)}")
normalized = new_segment
changed = True
else:
normalized.append(f"L {x} {y}")
report.append(" Path direction already correct, no changes made")
prev_x, prev_y = x, y
continue
if path_type == 'Arc' and cmd == 'A':
if len(params) >= 7 and prev_x is not None:
rx, ry, xrot, large, sweep, x, y = params[0], params[1], params[2], int(params[3]), int(params[4]), params[5], params[6]
# Force left-to-right clockwise
need_swap = x < prev_x
new_sweep = 1
if need_swap:
new_segment = [f"M {x} {y}", f"A {rx} {ry} {xrot} {large} {new_sweep} {prev_x} {prev_y}"]
report.append(f" Need to swap start/end → New path: {' '.join(new_segment)}")
normalized = new_segment
changed = True
else:
if sweep != new_sweep:
new_segment = [f"A {rx} {ry} {xrot} {large} {new_sweep} {x} {y}"]
report.append(f" Need to adjust sweep to 1 → New path: {' '.join(new_segment)}")
normalized.extend(new_segment)
changed = True
else:
normalized.append(f"A {rx} {ry} {xrot} {large} {sweep} {x} {y}")
report.append(" Arc direction already correct, no changes made")
prev_x, prev_y = x, y
continue
normalized.append(f"{cmd} {' '.join(map(str, params))}")
new_d = ' '.join(normalized)
report_header = f"[{path_id}-Analysis] Type: {path_type}"
full_report = [report_header, f"Original path: {original_d}"] + report
if changed:
full_report.append(f"Modified path: {new_d}")
else:
full_report.append("Path not modified")
return new_d, '\n'.join(full_report)
def analyze_and_normalize_path(d, path_type, path_id=""):
"""Strictly normalize path direction and generate detailed report"""
original_d = d
report = []
normalized = []
changed = False
# First check if path contains any arc commands
if 'A' in d or 'a' in d:
report_header = f"[{path_id}-Analysis] Type: {path_type} (contains arcs)"
full_report = [report_header,
f"Original path: {original_d}",
" Path contains arc commands - leaving unchanged",
"Path not modified"]
return original_d, '\n'.join(full_report)
# Ensure path starts with M command
if not d.strip().startswith('M'):
d = 'M' + d[1:] if d.startswith('L') else 'M ' + d
report.append(" Fix: Added M command at path start")
changed = True
# Parse path commands
commands = []
current_cmd = None
for token in re.split('([A-Za-z])', d):
if not token:
continue
if token in 'MLAZHVCSQTa-z':
current_cmd = token
else:
if current_cmd:
commands.append((current_cmd, token.strip()))
prev_x, prev_y = None, None
for cmd, params in commands:
params = [float(p) for p in re.split('[, ]+', params.strip()) if p]
if cmd == 'M':
if len(params) >= 2:
x, y = params[0], params[1]
normalized.append(f"M {x} {y}")
prev_x, prev_y = x, y
continue
if path_type == 'Line segment' and cmd == 'L':
if len(params) >= 2 and prev_x is not None:
x, y = params[0], params[1]
need_swap = x < prev_x or (x == prev_x and y < prev_y)
if need_swap:
new_segment = [f"M {x} {y}", f"L {prev_x} {prev_y}"]
report.append(f" Need to swap start/end → New path: {' '.join(new_segment)}")
normalized = new_segment
changed = True
else:
normalized.append(f"L {x} {y}")
report.append(" Path direction already correct, no changes made")
prev_x, prev_y = x, y
continue
normalized.append(f"{cmd} {' '.join(map(str, params))}")
new_d = ' '.join(normalized)
report_header = f"[{path_id}-Analysis] Type: {path_type}"
full_report = [report_header, f"Original path: {original_d}"] + report
if changed:
full_report.append(f"Modified path: {new_d}")
else:
full_report.append("Path not modified")
return new_d, '\n'.join(full_report)
def optimize_svg(input_path, output_path):
"""Process single SVG file with all optimizations"""
try:
# Register namespace
ET.register_namespace('', 'http://www.w3.org/2000/svg')
# Parse SVG file
tree = ET.parse(input_path)
root = tree.getroot()
print(f"\nProcessing file: {os.path.basename(input_path)}")
print("="*60)
# Create parent map
parent_map = {c: p for p in tree.iter() for c in p}
# 1. Remove xlink namespace
for attr in list(root.attrib):
if 'xlink' in attr:
del root.attrib[attr]
# 2. Set standard dimensions
root.set('width', '1e3')
root.set('height', '1e3')
root.set('viewBox', '0 0 1e3 1e3')
# 3. Remove all clipPath definitions and references
defs = root.find('{http://www.w3.org/2000/svg}defs')
if defs is not None:
clip_paths = defs.findall('{http://www.w3.org/2000/svg}clipPath')
for cp in clip_paths:
defs.remove(cp)
if len(defs) == 0:
root.remove(defs)
# 4. Remove clip-path attributes
for elem in tree.iter():
if 'clip-path' in elem.attrib:
del elem.attrib['clip-path']
# 5. Force square line caps
for g in root.findall('.//{http://www.w3.org/2000/svg}g'):
g.set('stroke-linecap', 'butt')
# 6. Remove dashed line styles
for elem in tree.iter():
if 'stroke-dasharray' in elem.attrib:
del elem.attrib['stroke-dasharray']
# 7. Completely remove empty groups
removed_groups = True
while removed_groups:
removed_groups = False
for g in root.findall('.//{http://www.w3.org/2000/svg}g'):
is_empty = (
len(g) == 0 and
not (g.text or '').strip() and
not (g.tail or '').strip() and
all(not k.startswith('{') for k in g.attrib))
if is_empty:
parent = parent_map.get(g)
if parent is not None:
parent.remove(g)
removed_groups = True
parent_map = {c: p for p in tree.iter() for c in p}
# 8. Convert polylines to paths
for polyline in root.findall('.//{http://www.w3.org/2000/svg}polyline'):
points = polyline.get('points', '').strip()
if points:
coords = [p for p in re.split(r'[\s,]', points) if p]
path_data = []
for i in range(0, len(coords), 2):
if i == 0:
path_data.append(f"M {coords[i]} {coords[i+1]}")
else:
path_data.append(f"L {coords[i]} {coords[i+1]}")
path = ET.Element('{http://www.w3.org/2000/svg}path')
new_d, analysis_report = analyze_and_normalize_path(
' '.join(path_data),
'Line segment',
"Polyline conversion"
)
path.set('d', new_d)
print(f"\n[Polyline conversion analysis]\n{analysis_report}")
for attr, value in polyline.attrib.items():
if attr not in ('points', 'stroke-dasharray'):
path.set(attr, value)
parent = parent_map.get(polyline)
if parent is not None:
parent.remove(polyline)
parent.append(path)
# 9. Normalize path directions (final step)
group_num = 0
for g in root.findall('.//{http://www.w3.org/2000/svg}g'):
group_num += 1
path_num = 0
group_report = []
for path in g.findall('.//{http://www.w3.org/2000/svg}path'):
path_num += 1
if 'd' not in path.attrib:
continue
# Auto-detect path type
path_type = 'Arc' if 'A' in path.get('d') else 'Line segment'
path_id = f"Group{group_num}-Path{path_num}"
new_d, analysis_report = analyze_and_normalize_path(
path.get('d'),
path_type,
path_id
)
if new_d != path.get('d'):
path.set('d', new_d)
group_report.append(analysis_report)
# Print all path reports for current group
if group_report:
print(f"\n=== Group {group_num} Path Analysis ===")
print('\n\n'.join(group_report))
# 10. Update colors and line width
for elem in root.findall('.//*[@stroke]'):
stroke = elem.get('stroke', '').lower()
if stroke == '#0ff' or stroke == 'rgb(0,255,0)':
elem.set('stroke', '#1bff38')
elif stroke == '#000' or stroke == 'rgb(255,0,0)':
elem.set('stroke', '#FF0000')
elem.set('stroke-width', f'{1}px')
# Generate final XML
rough_string = ET.tostring(root, encoding='utf-8', xml_declaration=True)
rough_string = rough_string.replace(b'standalone="no"', b'')
# Format output (remove empty lines)
reparsed = minidom.parseString(rough_string)
pretty_svg = reparsed.toprettyxml(indent=' ', encoding='utf-8')
pretty_svg = b'\n'.join(
line for line in pretty_svg.splitlines()
if line.strip()
).replace(b'<?xml version="1.0" ?>', b'<?xml version="1.0" encoding="utf-8"?>')
with open(output_path, 'wb') as f:
f.write(pretty_svg)
print("\n" + "="*60)
return True
except Exception as e:
print(f"\nProcessing error: {str(e)}")
print("="*60)
return False
def batch_process_svgs(input_dir, output_dir):
if not os.path.exists(output_dir):
os.makedirs(output_dir)
success_count = 0
failure_count = 0
for filename in os.listdir(input_dir):
if os.path.isdir(os.path.join(input_dir, filename)):
continue
if filename.lower().endswith('.svg') or '_new.svg' in filename.lower():
input_path = os.path.join(input_dir, filename)
output_filename = filename.replace('_new.svg', '_optimized.svg')
output_path = os.path.join(output_dir, output_filename)
if optimize_svg(input_path, output_path):
success_count += 1
print(f"✓ Processed successfully: {filename}{output_filename}")
else:
failure_count += 1
print(f"✗ Processing failed: {filename}")
print("\n" + "="*60 + "\n")
print("\nProcessing summary:")
print(f"Successfully processed: {success_count} files")
print(f"Failed to process: {failure_count} files")
if __name__ == '__main__':
input_dir = os.environ.get('RD_CONF_TEFWEICHEN')
output_dir = os.environ.get('RD_CONF_WORK')
if not input_dir or not output_dir:
print("Error: Environment variable RD_CONF_WORK is not set")
exit(1)
if not os.path.exists(input_dir):
print(f"Error: Input directory '{input_dir}' does not exist")
exit(1)
print(f"Starting SVG file processing, working directory: {input_dir}")
print("=" * 50)
batch_process_svgs(input_dir, output_dir)
print("Processing completed!")
@@ -0,0 +1,276 @@
import os
from lxml import etree
import re
from math import sqrt, isclose
from collections import defaultdict
# Constants
WEICHEN_PROFILE_WIDTH = 42.000
L_R_TARGET_LENGTH = 55.7237
DELTA_TARGET_LENGTH = 53.5437
MATCH_TOLERANCE = 0.1 # Matching tolerance 0.1mm
TARGET_STROKE_COLOR = "#1bff38" # 新增:目标stroke颜色
def process_svg_files(directory):
print(f"🔍 Scanning directory: {directory}")
print("-" * 60)
result_stats = {
'total_files': 0,
'L_R_files': {'with_pairs': [], 'no_pairs': [], 'excess_pairs': []},
'Delta_files': {'with_triples': [], 'no_triples': [], 'excess_triples': []}
}
for filename in os.listdir(directory):
if not filename.endswith('.svg'):
continue
filepath = os.path.join(directory, filename)
result_stats['total_files'] += 1
if '_L_' in filename or '_R_' in filename:
print(f"\n📄 Processing L/R file: {filename}")
process_lr_file(filepath, filename, result_stats)
elif '_DELTA_' in filename:
print(f"\n📄 Processing DeltaWeiche file: {filename}")
process_delta_file(filepath, filename, result_stats)
# Print final statistics
print("\n" + "="*60)
print("📊 Final processing statistics:")
print(f"Total files processed: {result_stats['total_files']}")
print("\nL/R type files results:")
print(f"✅ Files with matching path pairs: {len(result_stats['L_R_files']['with_pairs'])}")
print(f" {result_stats['L_R_files']['with_pairs']}")
print(f"⚠️ Files with no matching paths: {len(result_stats['L_R_files']['no_pairs'])}")
print(f" {result_stats['L_R_files']['no_pairs']}")
print(f"❌ Files with >2 matching paths: {len(result_stats['L_R_files']['excess_pairs'])}")
print(f" {result_stats['L_R_files']['excess_pairs']}")
print("\nDeltaWeiche type files results:")
print(f"✅ Files with matching path triples: {len(result_stats['Delta_files']['with_triples'])}")
print(f" {result_stats['Delta_files']['with_triples']}")
print(f"⚠️ Files with no matching paths: {len(result_stats['Delta_files']['no_triples'])}")
print(f" {result_stats['Delta_files']['no_triples']}")
print(f"❌ Files with ≠3 matching paths: {len(result_stats['Delta_files']['excess_triples'])}")
print(f" {result_stats['Delta_files']['excess_triples']}")
print("\n✨ Processing complete!")
def process_lr_file(filepath, filename, stats):
try:
tree = etree.parse(filepath)
root = tree.getroot()
# Find all straight line paths within target stroke color groups
straight_lines = find_straight_lines_in_colored_groups(root)
print(f" 📊 Found {len(straight_lines)} straight paths in stroke='{TARGET_STROKE_COLOR}' groups")
if not straight_lines:
print(f" ❌ No paths found in stroke='{TARGET_STROKE_COLOR}' groups")
stats['L_R_files']['no_pairs'].append(filename)
return
# Print all straight paths
print("\n 🔍 All straight path details:")
for line in straight_lines:
print(f" Path{line['index']}: ({line['p1'][0]:.2f},{line['p1'][1]:.2f})→"
f"({line['p2'][0]:.2f},{line['p2'][1]:.2f}) length={line['length']:.4f}mm")
# Group by length
length_groups = group_lines_by_length(straight_lines)
# Only print groups with exactly 2 paths
print("\n 🔍 Same-length path groups (2 paths):")
perfect_pairs = [group for group in length_groups.values() if len(group) == 2]
for group in perfect_pairs:
print(f" ┌ Length group ({group[0]['length']:.4f}mm, 2 paths)")
for line in group:
print(f" │ Path{line['index']}: ({line['p1'][0]:.2f},{line['p1'][1]:.2f})→"
f"({line['p2'][0]:.2f},{line['p2'][1]:.2f})")
print("" + "" * 40)
if len(perfect_pairs) == 1:
pair = perfect_pairs[0]
original_length = pair[0]['length']
scale_factor = WEICHEN_PROFILE_WIDTH / original_length
print(f"\n 🔄 Scaling calculation (based on length {original_length:.4f}mm):")
print(f" Scale factor: {scale_factor:.4f}")
# Print scaled lengths
print("\n 🔍 Scaled path lengths:")
for line in straight_lines:
scaled_len = line['length'] * scale_factor
print(f" Path{line['index']}: {line['length']:.4f}mm → {scaled_len:.4f}mm")
# Find path matching target length (within tolerance)
target_path = find_target_path(straight_lines, scale_factor, L_R_TARGET_LENGTH)
if target_path:
target_path['element'].set('style', 'stroke:none;fill:none;')
tree.write(filepath, encoding='utf-8', xml_declaration=True)
print(f"\n ✅ Hid path{target_path['index']} matching target length {L_R_TARGET_LENGTH:.4f}mm (±{MATCH_TOLERANCE}mm)")
stats['L_R_files']['with_pairs'].append(filename)
else:
print(f"\n ❌ No path found matching {L_R_TARGET_LENGTH:.4f}mm (±{MATCH_TOLERANCE}mm)")
stats['L_R_files']['no_pairs'].append(filename)
elif len(perfect_pairs) > 1:
print(f"\n ❗ Found multiple same-length path groups: {[len(g) for g in length_groups.values()]}")
stats['L_R_files']['excess_pairs'].append(filename)
else:
print("\n ❌ No same-length path pairs found")
stats['L_R_files']['no_pairs'].append(filename)
except Exception as e:
print(f" ❌ Processing failed: {str(e)}")
def process_delta_file(filepath, filename, stats):
try:
tree = etree.parse(filepath)
root = tree.getroot()
# Find all straight line paths within target stroke color groups
straight_lines = find_straight_lines_in_colored_groups(root)
print(f" 📊 Found {len(straight_lines)} straight paths in stroke='{TARGET_STROKE_COLOR}' groups")
if not straight_lines:
print(f" ❌ No paths found in stroke='{TARGET_STROKE_COLOR}' groups")
stats['Delta_files']['no_triples'].append(filename)
return
# Print all straight paths
print("\n 🔍 All straight path details:")
for line in straight_lines:
print(f" Path{line['index']}: ({line['p1'][0]:.2f},{line['p1'][1]:.2f})→"
f"({line['p2'][0]:.2f},{line['p2'][1]:.2f}) length={line['length']:.4f}mm")
# Group by length
length_groups = group_lines_by_length(straight_lines)
# Only print groups with exactly 3 paths
print("\n 🔍 Same-length path groups (3 paths):")
perfect_triples = [group for group in length_groups.values() if len(group) == 3]
for group in perfect_triples:
print(f" ┌ Length group ({group[0]['length']:.4f}mm, 3 paths)")
for line in group:
print(f" │ Path{line['index']}: ({line['p1'][0]:.2f},{line['p1'][1]:.2f})→"
f"({line['p2'][0]:.2f},{line['p2'][1]:.2f})")
print("" + "" * 40)
if len(perfect_triples) == 1:
triple = perfect_triples[0]
original_length = triple[0]['length']
scale_factor = WEICHEN_PROFILE_WIDTH / original_length
print(f"\n 🔄 Scaling calculation (based on length {original_length:.4f}mm):")
print(f" Scale factor: {scale_factor:.4f}")
# Print scaled lengths
print("\n 🔍 Scaled path lengths:")
for line in straight_lines:
scaled_len = line['length'] * scale_factor
print(f" Path{line['index']}: {line['length']:.4f}mm → {scaled_len:.4f}mm")
# Find all paths matching target length (there might be multiple)
target_paths = [line for line in straight_lines
if abs(line['length'] * scale_factor - DELTA_TARGET_LENGTH) < MATCH_TOLERANCE]
if target_paths:
for target in target_paths:
target['element'].set('style', 'stroke:none;fill:none;')
tree.write(filepath, encoding='utf-8', xml_declaration=True)
print(f"\n ✅ Hid {len(target_paths)} paths matching target length {DELTA_TARGET_LENGTH:.4f}mm (±{MATCH_TOLERANCE}mm):")
for target in target_paths:
print(f" - Path{target['index']}")
stats['Delta_files']['with_triples'].append(filename)
else:
print(f"\n ❌ No path found matching {DELTA_TARGET_LENGTH:.4f}mm (±{MATCH_TOLERANCE}mm)")
stats['Delta_files']['no_triples'].append(filename)
elif len(perfect_triples) > 1:
print(f"\n ❗ Found multiple same-length path groups: {[len(g) for g in length_groups.values()]}")
stats['Delta_files']['excess_triples'].append(filename)
else:
print("\n ❌ No same-length path groups (3 paths) found")
stats['Delta_files']['no_triples'].append(filename)
except Exception as e:
print(f" ❌ Processing failed: {str(e)}")
def find_straight_lines_in_colored_groups(root):
"""Find all straight line paths within groups that have stroke="#1bff38" """
lines = []
# 首先找到所有stroke="#1bff38"的group元素
colored_groups = root.xpath('.//svg:g[contains(@stroke, "#1bff38")]',
namespaces={'svg': 'http://www.w3.org/2000/svg'})
# 如果没有找到指定颜色的group,返回空列表
if not colored_groups:
return lines
print(f" 🎨 Found {len(colored_groups)} groups with stroke='{TARGET_STROKE_COLOR}'")
# 在每个符合条件的group中查找路径
path_index = 1
for group in colored_groups:
# 在当前group中查找所有path元素
paths = group.xpath('.//svg:path', namespaces={'svg': 'http://www.w3.org/2000/svg'})
for path in paths:
d = path.get('d', '').strip()
if not d:
continue
if is_straight_line(d):
length, (p1, p2) = calculate_line_length(d)
if length > 0:
lines.append({
'index': path_index,
'element': path,
'length': round(length, 4),
'p1': (round(p1[0], 2), round(p1[1], 2)),
'p2': (round(p2[0], 2), round(p2[1], 2))
})
path_index += 1
return lines
def group_lines_by_length(lines):
"""Group straight paths by length"""
groups = defaultdict(list)
for line in lines:
groups[line['length']].append(line)
return groups
def find_target_path(lines, scale_factor, target_length):
"""Find path that matches target length after scaling (within tolerance)"""
for line in lines:
scaled_length = line['length'] * scale_factor
if abs(scaled_length - target_length) < MATCH_TOLERANCE:
return line
return None
def is_straight_line(d):
"""Check if path is strictly a straight line"""
commands = [cmd[0].upper() for cmd in re.findall('([A-Za-z])', d)]
return len(commands) == 2 and commands[0] == 'M' and commands[1] == 'L'
def calculate_line_length(d):
"""Calculate line length and return endpoints"""
points = []
for cmd, params in re.findall('([A-Za-z])([^A-Za-z]*)', d):
if cmd.upper() in ('M', 'L'):
coords = [float(p) for p in re.findall('[-+]?\d*\.\d+|[-+]?\d+', params)]
points.append((coords[0], coords[1]))
if len(points) != 2:
return 0, ((0,0), (0,0))
length = sqrt((points[1][0]-points[0][0])**2 + (points[1][1]-points[0][1])**2)
return round(length, 4), (points[0], points[1])
if __name__ == '__main__':
# Set SVG files directory path
# svg_directory = r'C:\Users\y.wang\Documents\SSG-Ruledesigner-Konfigurator\SVGs\Omniflo\work'
svg_directory =os.environ.get('RD_CONF_WORK')
process_svg_files(svg_directory)
@@ -0,0 +1,160 @@
import os
import re
import xml.etree.ElementTree as ET
from typing import Tuple, List
def parse_svg_path(d: str) -> List[Tuple[float, float]]:
"""Extract all coordinates from SVG path data (including curve control points)"""
points = []
commands = re.findall(
r'([MmLlCcQqAaHhVvZz])([^MmLlCcQqAaHhVvZz]*)',
d.strip().replace(',', ' ')
)
current_pos = (0.0, 0.0)
for cmd, args in commands:
args = list(map(float, re.findall(r'[-+]?\d*\.?\d+', args)))
if cmd in ('M', 'm', 'L', 'l'): # Move/line commands
for i in range(0, len(args), 2):
x, y = args[i], args[i+1]
if cmd.islower(): # Relative coordinates
x += current_pos[0]
y += current_pos[1]
points.append((x, y))
current_pos = (x, y)
elif cmd in ('C', 'c'): # Cubic Bezier curves
for i in range(0, len(args), 6):
x1, y1, x2, y2, x, y = args[i:i+6]
if cmd.islower():
x1 += current_pos[0]; y1 += current_pos[1]
x2 += current_pos[0]; y2 += current_pos[1]
x += current_pos[0]; y += current_pos[1]
points.extend([(x1, y1), (x2, y2), (x, y)])
current_pos = (x, y)
elif cmd in ('A', 'a'): # Arc commands (start/end points only)
for i in range(0, len(args), 7):
x, y = args[i+5], args[i+6]
if cmd.islower():
x += current_pos[0]
y += current_pos[1]
points.append((x, y))
current_pos = (x, y)
return points
def calculate_bounding_box(svg_file: str) -> Tuple[float, float, float, float]:
"""Calculate true bounding box of all paths in SVG"""
tree = ET.parse(svg_file)
root = tree.getroot()
all_points = []
for path in root.findall('.//{http://www.w3.org/2000/svg}path'):
d = path.get('d', '')
all_points.extend(parse_svg_path(d))
if not all_points:
return (0, 0, 0, 0)
x_min = min(p[0] for p in all_points)
x_max = max(p[0] for p in all_points)
y_min = min(p[1] for p in all_points)
y_max = max(p[1] for p in all_points)
return (x_min, y_min, x_max, y_max)
def normalize_stroke_widths2(root, scale):
"""Normalize stroke widths to ensure consistent appearance after scaling"""
for elem in root.iter():
if 'stroke-width' in elem.attrib:
# Remove existing stroke-width
del elem.attrib['stroke-width']
# Apply scaled stroke width (e.g., 1px → 1/scale)
elem.set('stroke-width', f'{1/scale:.6f}px')
def apply_non_scaling_stroke(root):
for elem in root.iter():
if 'stroke' in elem.attrib and elem.attrib['stroke'] != 'none':
# set stroke-width=1no unit
elem.set('stroke-width', '1')
# set vector-effect
elem.set('vector-effect', 'non-scaling-stroke')
def scale_svg_file(input_path: str, output_path: str):
"""Scale SVG file with consistent stroke widths"""
print(f"\nProcessing: {os.path.basename(input_path)}")
tree = ET.parse(input_path)
root = tree.getroot()
# Calculate original bounding box
x_min, y_min, x_max, y_max = calculate_bounding_box(input_path)
width = x_max - x_min
height = y_max - y_min
print(f"Original bounding box: ({x_min:.3f}, {y_min:.3f}) to ({x_max:.3f}, {y_max:.3f})")
print(f"Original dimensions: {width:.3f} (w) × {height:.3f} (h)")
# Determine scaling base (larger dimension → 1000px)
if width > height:
scale = 1000.0 / width
new_width = 1000.0
new_height = round(height * scale,3)
print(f"Scaling base: Width (larger dimension)")
else:
scale = 1000.0 / height
new_height = 1000.0
new_width = round(width * scale,3)
print(f"Scaling base: Height (larger dimension)")
print(f"Scale factor: {scale:.6f}")
print(f"New dimensions: {new_width:.3f}px × {new_height:.3f}px")
print(f"ViewBox: 0 0 {new_width:.3f} {new_height:.3f}")
normalize_stroke_widths2(root, scale)
#apply_non_scaling_stroke(root)
#
# Update SVG attributes
root.set('viewBox', f'0 0 {new_width:.3f} {new_height:.3f}')
root.set('width', f'{new_width:.3f}')
root.set('height', f'{new_height:.3f}')
# Apply translation and scaling
for g in root.findall('{http://www.w3.org/2000/svg}g'):
transform = g.get('transform', '')
new_transform = f'translate({-x_min*scale},{-y_min*scale}) scale({scale})'
if transform:
new_transform = f'{transform} {new_transform}'
g.set('transform', new_transform)
# Save modified SVG
tree.write(output_path, encoding='utf-8', xml_declaration=True)
print(f"Finished processing: {os.path.basename(input_path)}")
def batch_process_svg(input_dir: str, output_dir: str):
"""Batch process SVG files in directory (non-recursive)"""
if not os.path.exists(output_dir):
os.makedirs(output_dir)
svg_files = [
f for f in os.listdir(input_dir)
if f.lower().endswith('.svg') and os.path.isfile(os.path.join(input_dir, f))
]
if not svg_files:
print("No SVG files found in the input directory!")
return
print(f"\nFound {len(svg_files)} SVG files to process")
for filename in svg_files:
input_path = os.path.join(input_dir, filename)
output_path = os.path.join(output_dir, filename)
scale_svg_file(input_path, output_path)
print("\nAll files processed successfully!")
if __name__ == '__main__':
# input_dir = r'C:\Users\y.wang\Documents\SSG-Ruledesigner-Konfigurator\SVGs\Omniflo\work'
# output_dir =r"C:\Users\y.wang\Documents\SSG-Ruledesigner-Konfigurator\SVGs\Omniflo\TEFWeichen\output"
input_dir = os.environ.get('RD_CONF_WORK')
output_dir = os.environ.get('RD_CONF_WORK')
batch_process_svg(input_dir, output_dir)
@@ -0,0 +1,162 @@
import re
import os
import xml.etree.ElementTree as ET
from typing import List, Dict, Tuple
def process_svg_file(input_path: str, output_path: str) -> bool:
"""Process SVG file while preserving original dimensions and viewBox"""
try:
# Parse SVG file
tree = ET.parse(input_path)
root = tree.getroot()
# Store original dimensions and viewBox
original_attrs = {
'width': root.attrib.get('width', '100%'),
'height': root.attrib.get('height', '100%'),
'viewBox': root.attrib.get('viewBox', '')
}
# Remove namespace prefixes
for elem in root.iter():
if '}' in elem.tag:
elem.tag = elem.tag.split('}', 1)[1]
for attr in list(elem.attrib):
if '}' in attr:
new_attr = attr.split('}', 1)[1]
elem.attrib[new_attr] = elem.attrib[attr]
del elem.attrib[attr]
# Process all groups with transforms
for g in root.findall('g'):
if 'transform' in g.attrib:
# Parse transform and convert to matrix
transform = g.attrib['transform']
matrix_str = convert_transform_to_matrix(transform)
# Update group attributes
g.attrib['transform'] = matrix_str
# g.attrib['stroke'] = '#ffe31b' # Force stroke color
g.attrib['fill'] = 'none'
g.attrib['stroke-linecap'] = 'butt'
g.attrib['vector-effect'] = 'non-scaling-stroke'
# Format paths within group
for path in g.findall('path'):
path.attrib['d'] = simplify_path_data(path.attrib.get('d', ''))
path.tail = '\n ' # Maintain consistent indentation
# Set root attributes while preserving original dimensions
root.attrib.update({
'version': '1.1',
'xmlns': 'http://www.w3.org/2000/svg',
'width': original_attrs['width'],
'height': original_attrs['height'],
'viewBox': original_attrs['viewBox'],
'fill-rule': 'evenodd',
'stroke-linecap': 'round',
'stroke-linejoin': 'round',
'space': 'preserve'
})
# Format XML with proper indentation
indent(root)
# Generate XML string
xml_str = ET.tostring(root, encoding='unicode')
xml_str = '<?xml version="1.0" encoding="UTF-8"?>\n' + xml_str
# Clean up formatting
xml_str = re.sub(r'\n\s*\n', '\n', xml_str)
xml_str = re.sub(r'>\s+<', '>\n<', xml_str)
# Write to file
with open(output_path, 'w', encoding='utf-8') as f:
f.write(xml_str)
print(f"Processed: {os.path.basename(input_path)}")
return True
except Exception as e:
print(f"Failed {os.path.basename(input_path)}: {str(e)}")
return False
def convert_transform_to_matrix(transform: str) -> str:
"""Convert transform string to matrix notation with 4 decimal places"""
tx = ty = 0.0
translate_match = re.search(r'translate\(([^,]+),\s*([^)]+)\)', transform)
if translate_match:
tx = float(translate_match.group(1))
ty = float(translate_match.group(2))
scale = 1.0
scale_match = re.search(r'scale\(([^)]+)\)', transform)
if scale_match:
scale = float(scale_match.group(1))
return f"matrix({scale:.4f} 0 0 {scale:.4f} {tx:.2f} {ty:.2f})"
def simplify_path_data(d: str) -> str:
"""Simplify path data while maintaining relative commands"""
d = re.sub(r'\s+', ' ', d.strip())
def format_number(num_str: str) -> str:
try:
num = float(num_str)
if abs(num) < 0.001 and num != 0:
return f"{num:.0e}".replace('e-0', 'e-')
if abs(num - 1000) < 0.1:
return '1e3'
if abs(num - round(num, 3)) < 0.0001:
return f"{round(num, 3):g}"
return num_str
except ValueError:
return num_str
parts = []
for part in re.split(r'([ ,])', d):
if re.match(r'^[-+]?\d*\.?\d+$', part):
parts.append(format_number(part))
else:
parts.append(part)
return ''.join(parts).replace(' ,', ',')
def indent(elem: ET.Element, level: int = 0):
"""Properly indent XML elements"""
indent_str = "\n" + level * " "
if len(elem):
if not elem.text or not elem.text.strip():
elem.text = indent_str + " "
if not elem.tail or not elem.tail.strip():
elem.tail = indent_str
for child in elem:
indent(child, level + 1)
if not elem.tail or not elem.tail.strip():
elem.tail = indent_str
else:
if level and (not elem.tail or not elem.tail.strip()):
elem.tail = indent_str
def batch_process_svgs(input_dir: str, output_dir: str):
"""Process all SVG files in a directory"""
if not os.path.exists(output_dir):
os.makedirs(output_dir)
success_count = 0
for filename in sorted(os.listdir(input_dir)):
if filename.lower().endswith('.svg'):
input_path = os.path.join(input_dir, filename)
output_path = os.path.join(output_dir, filename)
if process_svg_file(input_path, output_path):
success_count += 1
print(f"\nCompleted: {success_count} files processed")
if __name__ == '__main__':
# input_dir = r'C:\Users\y.wang\Documents\SSG-Ruledesigner-Konfigurator\SVGs\Omniflo\TEFWeichen\output'
# output_dir =r"C:\Users\y.wang\Documents\SSG-Ruledesigner-Konfigurator\SVGs\Omniflo\TEFWeichen\output"
input_dir = os.environ.get('RD_CONF_WORK')
output_dir = os.environ.get('RD_CONF_OUTPUT_TEFWEICHEN')
# Example usage:
batch_process_svgs(input_dir, output_dir)
@@ -0,0 +1,195 @@
import os
import re
import json
from xml.etree import ElementTree as ET
from xml.dom import minidom
def extract_sivasnr(filename):
"""Extract content before _WEICHE from filename"""
# 匹配模式:提取 _WEICHE 之前的所有内容
match = re.search(r'^(.+?)_WEICHE', filename)
return match.group(1) if match else None
def should_skip_stroke_width(element_attrib):
"""Check if element should skip stroke-width setting"""
# Check style attribute
if 'style' in element_attrib:
style = element_attrib['style'].lower()
if 'stroke:none' in style and 'fill:none' in style:
return True
# Check separate stroke and fill attributes
stroke = element_attrib.get('stroke', '').lower()
fill = element_attrib.get('fill', '').lower()
return stroke == 'none' and fill == 'none'
def create_xml_structure(svg_root):
"""Create target XML structure preserving all elements"""
# Create base structure
new_root = ET.Element("svg", xmlns="http://www.w3.org/2000/svg")
g_layer = ET.SubElement(new_root, "g", transform="rotate(0)")
# Add inner SVG container
inner_svg = ET.SubElement(g_layer, "svg", {
"viewBox": svg_root.attrib.get("viewBox", "0 0 1000 1000"),
"preserveAspectRatio": "none",
"position": "absolute",
"overflow": "visible"
})
# Transfer all content
for elem in svg_root:
if elem.tag.endswith("}g"):
# Process group with namespace
new_g = ET.SubElement(inner_svg, "g", attrib={
k: v for k, v in elem.attrib.items()
if not k.startswith("xmlns")
})
# Process all elements within group
for child in elem:
child_tag = child.tag.split("}")[-1] # Remove namespace
child_attrib = {}
# Preserve all original attributes (except namespace)
for k, v in child.attrib.items():
if not k.startswith("xmlns"):
child_attrib[k] = v
# Only add stroke-width if not stroke:none;fill:none
if not should_skip_stroke_width(child.attrib):
child_attrib["stroke-width"] = "1px"
# Create element with preserved attributes
ET.SubElement(new_g, child_tag, attrib=child_attrib)
else:
# Process standalone elements (path, circle, etc.)
elem_tag = elem.tag.split("}")[-1] # Remove namespace
elem_attrib = {}
# Preserve all original attributes (except namespace)
for k, v in elem.attrib.items():
if not k.startswith("xmlns"):
elem_attrib[k] = v
# Only add stroke-width if not stroke:none;fill:none
if not should_skip_stroke_width(elem.attrib):
elem_attrib["stroke-width"] = "1px"
ET.SubElement(inner_svg, elem_tag, attrib=elem_attrib)
return new_root
def format_xml(element):
"""Generate formatted XML string"""
# Generate XML with declaration
xml_str = ET.tostring(element, encoding="UTF-8", xml_declaration=True)
# Pretty format with 2-space indent
dom = minidom.parseString(xml_str)
pretty_xml = dom.toprettyxml(indent=" ", encoding="UTF-8").decode("UTF-8")
# Remove extra empty lines (preserve structure)
lines = []
for line in pretty_xml.split("\n"):
if line.strip() or line.lstrip().startswith("</"):
lines.append(line)
return "\n".join(lines)
def convert_svg_to_xml(svg_path, output_dir):
"""Convert SVG to target XML format"""
sivasnr = extract_sivasnr(os.path.basename(svg_path))
if not sivasnr:
raise ValueError(f"Invalid filename format: {os.path.basename(svg_path)}")
# Parse original SVG
try:
tree = ET.parse(svg_path)
svg_root = tree.getroot()
except Exception as e:
raise ValueError(f"SVG parsing error: {str(e)}")
# Build new structure
new_xml = create_xml_structure(svg_root)
formatted_xml = format_xml(new_xml)
# Ensure output directory exists
os.makedirs(output_dir, exist_ok=True)
output_path = os.path.join(output_dir, f"{sivasnr}.xml")
# Write file (UTF-8 encoding)
with open(output_path, "w", encoding="UTF-8") as f:
f.write(formatted_xml)
return f"SSG/shapes/svg/{sivasnr}.xml"
def update_txt_file(txt_path, xml_rel_path):
"""Update path in TXT file"""
try:
with open(txt_path, "r", encoding="UTF-8-sig") as f:
data = json.load(f)
if "srcSVG" not in data:
raise ValueError("Missing srcSVG field")
data["srcSVG"] = xml_rel_path
with open(txt_path, "w", encoding="UTF-8") as f:
json.dump(data, f, indent=4, ensure_ascii=False)
return True
except Exception as e:
print(f"Update failed {os.path.basename(txt_path)}: {str(e)}")
return False
def process_files(svg_dir, txt_dir, output_dir):
"""Batch process files"""
if not all(map(os.path.exists, [svg_dir, txt_dir])):
raise FileNotFoundError("Input directory not found")
results = {"success": 0, "failed": 0}
for svg_file in os.listdir(svg_dir):
if not svg_file.endswith(".svg"):
continue
svg_path = os.path.join(svg_dir, svg_file)
sivasnr = extract_sivasnr(svg_file)
if not sivasnr:
print(f"Skipping invalid file: {svg_file}")
results["failed"] += 1
continue
try:
# Convert file
xml_rel_path = convert_svg_to_xml(svg_path, output_dir)
# Update TXT
txt_path = os.path.join(txt_dir, f"{sivasnr}.txt")
if not os.path.exists(txt_path):
raise FileNotFoundError(f"Corresponding TXT file not found: {sivasnr}.txt")
if update_txt_file(txt_path, xml_rel_path):
print(f"Success: {svg_file}{sivasnr}.xml")
results["success"] += 1
else:
results["failed"] += 1
except Exception as e:
print(f"Processing failed {svg_file}: {str(e)}")
results["failed"] += 1
# Output report
print(f"\nProcessing complete: {results['success']} succeeded, {results['failed']} failed")
if __name__ == "__main__":
# Configuration - modify these paths as needed
SVG_INPUT_FOLDER = os.environ.get('RD_CONF_OUTPUT_TEFWEICHEN') # Folder containing SVG files
SVG_OUTPUT_FOLDER = os.environ.get("SVG_PATH","svg")
PRORPS_FOLDER = os.environ.get("PROPS_PATH", "props") # Folder containing txt files
# Start processing
process_files(SVG_INPUT_FOLDER, PRORPS_FOLDER, SVG_OUTPUT_FOLDER)
print("\nProcessing complete.")
@@ -0,0 +1,196 @@
"""
SVG XML Batch Modifier Script
This script:
1. Reads a JSON array of items
2. For each item, extracts the "Sivasnr" value
3. Locates and modifies the corresponding XML file
4. Processes only red color groups (#FF0000)
5. Provides comprehensive reporting
"""
import json
import os
import xml.etree.ElementTree as ET
from xml.etree.ElementTree import Element, SubElement, tostring
def process_xml_file(xml_path):
"""
Process a single XML file focusing on red color groups only
Returns modification statistics
"""
try:
tree = ET.parse(xml_path)
root = tree.getroot()
# Register and handle SVG namespace
ET.register_namespace('', 'http://www.w3.org/2000/svg')
namespaces = {'svg': 'http://www.w3.org/2000/svg'}
stats = {
'ff0000_groups': 0, # Count of #FF0000 groups processed
'other_red_groups': 0, # Count of other red-style groups processed
'elements_modified': 0, # Total elements modified
'modified': False # Whether file was modified
}
# Process all group elements
for g in root.findall('.//svg:g', namespaces):
stroke = g.get('stroke', '').upper() # Normalize to uppercase for comparison
# Process #FF0000 red groups
if stroke == "#FF0000":
stats['ff0000_groups'] += 1
# Remove group attributes: stroke, stroke-width, stroke-linejoin
for attr in ['stroke', 'stroke-width', 'stroke-linejoin']:
if attr in g.attrib:
del g.attrib[attr]
stats['modified'] = True
# Modify path children: remove stroke-width, set new style
for child in g.findall('.//svg:path', namespaces):
if 'stroke-width' in child.attrib:
del child.attrib['stroke-width']
stats['modified'] = True
# Set new style for red stroke with 2px width
child.set('style', 'stroke:#FF0000;stroke-width:2px')
stats['elements_modified'] += 1
stats['modified'] = True
# Process other groups with red color in style attributes
else:
# Process elements with style="stroke:#FF0000;"
red_elements_found = False
for child in g.findall('.//svg:*[@style]', namespaces):
style = child.get('style', '')
if 'stroke:#FF0000;' in style:
red_elements_found = True
# Remove stroke-width attribute if exists
if 'stroke-width' in child.attrib:
del child.attrib['stroke-width']
stats['modified'] = True
# Update the style attribute with standardized format
child.set('style', 'stroke:#FF0000;stroke-width:2px;')
stats['elements_modified'] += 1
stats['modified'] = True
if red_elements_found:
stats['other_red_groups'] += 1
# Save changes if modifications were made
if stats['modified']:
xml_str = ET.tostring(root, encoding='unicode')
xml_str = xml_str.replace('><', '>\n<').replace('</svg>', '\n</svg>')
with open(xml_path, 'w', encoding='utf-8') as f:
f.write(xml_str)
return stats
except Exception as e:
print(f" Error processing file: {str(e)}")
return None
def main():
"""
Main processing function
"""
# Configure paths from environment variables
json_path = os.environ.get("JSON_PATH_TEFWEICHE", "JSON")
json_file_path = os.path.join(json_path, "omniflo_TEF_weichen_output.json")
svg_folder_path = os.environ.get("SVG_PATH", "svg")
# Statistics for final report
total_stats = {
'files_processed': 0,
'files_modified': 0,
'total_ff0000_groups': 0,
'total_other_red_groups': 0,
'total_elements_modified': 0
}
try:
# Read and parse JSON file
with open(json_file_path, 'r', encoding='utf-8') as f:
data = json.load(f)
# Verify we have a list of items
if not isinstance(data, list):
print("Error: JSON data should be an array of items")
return
print(f"\nFound {len(data)} items in JSON file")
print("Starting XML file processing...")
print("=" * 60)
# Process each item in the JSON array
for index, item in enumerate(data, 1):
if not isinstance(item, dict):
print(f"\nItem {index}: Not a dictionary, skipping")
continue
sivasnr = item.get("Sivasnr")
if not sivasnr:
print(f"\nItem {index}: Missing 'Sivasnr' value, skipping")
continue
print(f"\nProcessing item {index}: Sivasnr = {sivasnr}")
# Build XML filename and path
xml_filename = f"{sivasnr}.xml"
xml_path = os.path.join(svg_folder_path, xml_filename)
if not os.path.exists(xml_path):
print(f" XML file not found: {xml_path}")
continue
print(f" Found XML file: {xml_path}")
total_stats['files_processed'] += 1
# Process the XML file
stats = process_xml_file(xml_path)
if stats is None:
print(" Processing failed")
elif stats['modified']:
print(" ✓ File successfully modified")
total_stats['files_modified'] += 1
total_stats['total_ff0000_groups'] += stats['ff0000_groups']
total_stats['total_other_red_groups'] += stats['other_red_groups']
total_stats['total_elements_modified'] += stats['elements_modified']
# Print detailed statistics for this file
if stats['ff0000_groups'] > 0:
print(f" - #FF0000 groups processed: {stats['ff0000_groups']}")
if stats['other_red_groups'] > 0:
print(f" - Other red style groups: {stats['other_red_groups']}")
print(f" - Elements modified: {stats['elements_modified']}")
else:
print(" - No modifications needed (no red groups found)")
# Generate final comprehensive report
print("\n" + "=" * 60)
print("PROCESSING SUMMARY REPORT")
print("=" * 60)
print(f"Total files processed: {total_stats['files_processed']}")
print(f"Total files modified: {total_stats['files_modified']}")
print(f"Total #FF0000 groups processed: {total_stats['total_ff0000_groups']}")
print(f"Total other red style groups: {total_stats['total_other_red_groups']}")
print(f"Total elements modified: {total_stats['total_elements_modified']}")
print("=" * 60)
except FileNotFoundError:
print("\nError: JSON file not found at specified path")
except json.JSONDecodeError:
print("\nError: Invalid JSON format in input file")
except Exception as e:
print(f"\nUnexpected error: {str(e)}")
if __name__ == "__main__":
print("SVG XML Batch Modifier - Starting execution")
print("Focus: Processing only red color groups (#FF0000)")
main()
@@ -0,0 +1,725 @@
[
{
"Sivasnr": "834372002+0_BG090090",
"ProfilTyp": "WEICHE S 45°-L-350/700, KPL. MIT P mit TEF Innen",
"WeichenTyp": "Einzelweiche",
"KurvenWinkel": 45,
"Schaltungstyp": "P",
"OFWeiche_center_line_width_mm": 350.129,
"OFWeiche_center_line_height_mm": 700.123,
"Objekt_width_mm": 496.396,
"Objekt_height_mm": 858.309,
"TEFWeiche_center_line_width_mm": 320.7103,
"TEFWeiche_center_line_height_mm": 722.5864,
"OFWeiche_CP1_x_mm": 442.8527,
"OFWeiche_CP1_y_mm": 715.0010,
"OFWeiche_CP2_x_mm": 92.8502,
"OFWeiche_CP2_y_mm": 14.8776,
"OFWeiche_CP4_x_mm": 442.8527,
"OFWeiche_CP4_y_mm": 355.0010,
"TEFWeiche_CPL1_x_mm": 342.8427,
"TEFWeiche_CPL1_y_mm": 858.3085,
"TEFWeiche_CPL2_x_mm": 22.1324,
"TEFWeiche_CPL2_y_mm": 85.7221,
"KurvenRichtung": 1,
"SivasnrTEF": "0_B10090+0_B10090"
},
{
"Sivasnr": "834372005+0_BG090090",
"ProfilTyp": "WEICHE S 45°-R-350/700, KPL. MIT P mit TEF Innen",
"WeichenTyp": "Einzelweiche",
"KurvenWinkel": 45,
"Schaltungstyp": "P",
"OFWeiche_center_line_width_mm": 350.129,
"OFWeiche_center_line_height_mm": 700.123,
"Objekt_width_mm": 496.396,
"Objekt_height_mm": 858.309,
"TEFWeiche_center_line_width_mm": 320.7103,
"TEFWeiche_center_line_height_mm": 722.5864,
"OFWeiche_CP1_x_mm": 53.5437,
"OFWeiche_CP1_y_mm": 715.0010,
"OFWeiche_CP3_x_mm": 403.6727,
"OFWeiche_CP3_y_mm": 14.8776,
"OFWeiche_CP4_x_mm": 53.5437,
"OFWeiche_CP4_y_mm": 355.0010,
"TEFWeiche_CPR1_x_mm": 153.5537,
"TEFWeiche_CPR1_y_mm": 858.3085,
"TEFWeiche_CPR2_x_mm": 474.264,
"TEFWeiche_CPR2_y_mm": 85.7221,
"KurvenRichtung": 2,
"SivasnrTEF": "0_B10090+0_B10090"
},
{
"Sivasnr": "834372008+0_BG190090",
"ProfilTyp": "WEICHE S 45°-L-400/750, KPL. MIT P mit TEF Ihnen",
"WeichenTyp": "Einzelweiche",
"KurvenWinkel": 45,
"Schaltungstyp": "P",
"OFWeiche_center_line_width_mm": 400,
"OFWeiche_center_line_height_mm": 750,
"Objekt_width_mm": 492.626,
"Objekt_height_mm": 903.949,
"TEFWeiche_center_line_width_mm": 316.9403,
"TEFWeiche_center_line_height_mm": 764.5864,
"OFWeiche_CP1_x_mm": 439.0827,
"OFWeiche_CP1_y_mm": 764.8776,
"OFWeiche_CP2_x_mm": 39.0827,
"OFWeiche_CP2_y_mm": 14.8776,
"OFWeiche_CP4_x_mm": 439.8627,
"OFWeiche_CP4_y_mm": 404.8776,
"TEFWeiche_CPL1_x_mm": 339.0727,
"TEFWeiche_CPL1_y_mm": 903.9492,
"TEFWeiche_CPL2_x_mm": 22.1324,
"TEFWeiche_CPL2_y_mm": 139.3628,
"KurvenRichtung": 1,
"SivasnrTEF": "0_B10090+0_B10090"
},
{
"Sivasnr": "834372011+0_BG190090",
"ProfilTyp": "WEICHE S 45°-R-400/750, KPL. MIT P mit TEF Ihnen",
"WeichenTyp": "Einzelweiche",
"KurvenWinkel": 45,
"Schaltungstyp": "P",
"OFWeiche_center_line_width_mm": 400,
"OFWeiche_center_line_height_mm": 750,
"Objekt_width_mm": 492.626,
"Objekt_height_mm": 903.949,
"TEFWeiche_center_line_width_mm": 316.9403,
"TEFWeiche_center_line_height_mm": 764.5864,
"OFWeiche_CP1_x_mm": 53.5437,
"OFWeiche_CP1_y_mm": 764.8776,
"OFWeiche_CP3_x_mm": 453.5437,
"OFWeiche_CP3_y_mm": 14.8776,
"OFWeiche_CP4_x_mm": 53.5437,
"OFWeiche_CP4_y_mm": 404.8776,
"TEFWeiche_CPR1_x_mm": 153.5537,
"TEFWeiche_CPR1_y_mm": 903.9492,
"TEFWeiche_CPR2_x_mm": 470.4940,
"TEFWeiche_CPR2_y_mm": 139.3628,
"KurvenRichtung": 2,
"SivasnrTEF": "0_B10090+0_B10090"
},
{
"Sivasnr": "834372028+0_BG080090",
"ProfilTyp": "WEICHE S 90°-L-700/700, KPL. MIT P mit TEF Innen",
"WeichenTyp": "Einzelweiche",
"KurvenWinkel": 90,
"Schaltungstyp": "P",
"OFWeiche_center_line_width_mm": 700.374,
"OFWeiche_center_line_height_mm": 700.185,
"Objekt_width_mm": 753.917,
"Objekt_height_mm": 859.292,
"TEFWeiche_center_line_width_mm": 599.685,
"TEFWeiche_center_line_height_mm": 738.242,
"OFWeiche_CP1_x_mm": 700.374,
"OFWeiche_CP1_y_mm": 721.225,
"OFWeiche_CP2_x_mm": 0,
"OFWeiche_CP2_y_mm": 21.040,
"OFWeiche_CP4_x_mm": 700.374,
"OFWeiche_CP4_y_mm": 361.225,
"TEFWeiche_CPL1_x_mm": 600.364,
"TEFWeiche_CPL1_y_mm": 859.292,
"TEFWeiche_CPL2_x_mm": 0,
"TEFWeiche_CPL2_y_mm": 121.050,
"KurvenRichtung": 1,
"SivasnrTEF": "0_B10080+0_B10090"
},
{
"Sivasnr": "834372031+0_BG080090",
"ProfilTyp": "WEICHE S 90°-R-700/700, KPL. MIT P mit TEF Innen",
"WeichenTyp": "Einzelweiche",
"KurvenWinkel": 90,
"Schaltungstyp": "P",
"OFWeiche_center_line_width_mm": 700.374,
"OFWeiche_center_line_height_mm": 700.185,
"Objekt_width_mm": 753.917,
"Objekt_height_mm": 859.67,
"TEFWeiche_center_line_width_mm": 540.75,
"TEFWeiche_center_line_height_mm": 738.62,
"OFWeiche_CP1_x_mm": 53.544,
"OFWeiche_CP1_y_mm": 721.225,
"OFWeiche_CP3_x_mm": 753.9172,
"OFWeiche_CP3_y_mm": 21.0400,
"OFWeiche_CP4_x_mm": 53.544,
"OFWeiche_CP4_y_mm": 361.25,
"TEFWeiche_CPR1_x_mm": 153.554,
"TEFWeiche_CPR1_y_mm": 859.67,
"TEFWeiche_CPR2_x_mm": 694.3037,
"TEFWeiche_CPR2_y_mm": 121.050,
"KurvenRichtung": 2,
"SivasnrTEF": "0_B10080+0_B10090"
},
{
"Sivasnr": "0_BG090090+834372101",
"ProfilTyp": "WEICHE S D 45°-350/700, KPL. MIT P mit TEF links",
"WeichenTyp": "Doppelweiche",
"KurvenWinkel": 45,
"Schaltungstyp": "P",
"OFWeiche_center_line_width_mm": 700.258,
"OFWeiche_center_line_height_mm": 700.123,
"Objekt_width_mm": 807.859,
"Objekt_height_mm": 858.309,
"TEFWeiche_center_line_width_mm": 320.7103,
"TEFWeiche_center_line_height_mm": 772.5864,
"OFWeiche_CP1_x_mm": 442.8527,
"OFWeiche_CP1_y_mm": 715.0010,
"OFWeiche_CP2_x_mm": 92.7236,
"OFWeiche_CP2_y_mm": 14.8778,
"OFWeiche_CP3_x_mm": 792.9823,
"OFWeiche_CP3_y_mm": 14.8778,
"TEFWeiche_CPL1_x_mm": 342.8427,
"TEFWeiche_CPL1_y_mm":858.309,
"TEFWeiche_CPL2_x_mm": 22.1324,
"TEFWeiche_CPL2_y_mm": 85.7221,
"KurvenRichtung": 3,
"SivasnrTEF": "0_B10090+0_B10090"
},
{
"Sivasnr": "834372101+0_BG090090",
"ProfilTyp": "WEICHE S D 45°-350/700, KPL. MIT P mit TEF rechts",
"WeichenTyp": "Doppelweiche",
"KurvenWinkel": 45,
"Schaltungstyp": "P",
"OFWeiche_center_line_width_mm": 700.258,
"OFWeiche_center_line_height_mm": 700.123,
"Objekt_width_mm": 807.859,
"Objekt_height_mm": 858.309,
"TEFWeiche_center_line_width_mm": 320.7103,
"TEFWeiche_center_line_height_mm": 772.5864,
"OFWeiche_CP1_x_mm": 365.0067,
"OFWeiche_CP1_y_mm": 715.0010,
"OFWeiche_CP2_x_mm": 14.8778,
"OFWeiche_CP2_y_mm": 14.8778,
"OFWeiche_CP3_x_mm": 715.1357,
"OFWeiche_CP3_y_mm": 14.8778,
"TEFWeiche_CPR1_x_mm": 465.0167,
"TEFWeiche_CPR1_y_mm": 858.3085,
"TEFWeiche_CPR2_x_mm": 785.7270,
"TEFWeiche_CPR2_y_mm": 85.7221,
"KurvenRichtung": 3,
"SivasnrTEF": "0_B10090+0_B10090"
},
{
"Sivasnr": "0_BG090090+834372101+0_BG090090",
"ProfilTyp": "WEICHE S D 45°-350/700, KPL. MIT P mit TEF beideseitig",
"WeichenTyp": "Doppelweiche",
"KurvenWinkel": 45,
"Schaltungstyp": "P",
"OFWeiche_center_line_width_mm": 700.258,
"OFWeiche_center_line_height_mm": 700.123,
"Objekt_width_mm": 885.705,
"Objekt_height_mm": 858.309,
"TEFWeiche_center_line_width_mm": 841.4406,
"TEFWeiche_center_line_height_mm": 772.5864,
"OFWeiche_CP1_x_mm": 442.8527,
"OFWeiche_CP1_y_mm": 715.0010,
"OFWeiche_CP2_x_mm": 92.7236,
"OFWeiche_CP2_y_mm": 14.8778,
"OFWeiche_CP3_x_mm": 792.9823,
"OFWeiche_CP3_y_mm": 14.8778,
"TEFWeiche_CPL1_x_mm": 342.8427,
"TEFWeiche_CPL1_y_mm": 858.309,
"TEFWeiche_CPL2_x_mm": 22.1324,
"TEFWeiche_CPL2_y_mm": 85.7221,
"TEFWeiche_CPR1_x_mm": 542.8627,
"TEFWeiche_CPR1_y_mm": 858.3085,
"TEFWeiche_CPR2_x_mm": 863.5730,
"TEFWeiche_CPR2_y_mm": 85.7221,
"KurvenRichtung": 3,
"SivasnrTEF": "0_B10090+0_B10090+0_B10090+0_B10090"
},
{
"Sivasnr": "0_BG190090+834372104",
"ProfilTyp": "WEICHE S D 45°-400/750, KPL. MIT P mit TEF links",
"WeichenTyp": "Doppelweiche",
"KurvenWinkel": 45,
"Schaltungstyp": "P",
"OFWeiche_center_line_width_mm": 800.0,
"OFWeiche_center_line_height_mm": 750,
"Objekt_width_mm": 853.96,
"Objekt_height_mm": 903.949,
"TEFWeiche_center_line_width_mm": 316.9403,
"TEFWeiche_center_line_height_mm": 764.5864,
"OFWeiche_CP1_x_mm": 439.0827,
"OFWeiche_CP1_y_mm": 764.8776,
"OFWeiche_CP2_x_mm": 39.0827,
"OFWeiche_CP2_y_mm": 14.8775,
"OFWeiche_CP3_x_mm": 839.0827,
"OFWeiche_CP3_y_mm": 14.8775,
"TEFWeiche_CPL1_x_mm": 339.0727,
"TEFWeiche_CPL1_y_mm": 903.9492,
"TEFWeiche_CPL2_x_mm": 22.1324,
"TEFWeiche_CPL2_y_mm": 139.3628,
"KurvenRichtung": 3,
"SivasnrTEF": "0_B10090+0_B10090"
},
{
"Sivasnr": "834372104+0_BG190090",
"ProfilTyp": "WEICHE S D 45°-400/750, KPL. MIT P mit TEF rechts",
"WeichenTyp": "Doppelweiche",
"KurvenWinkel": 45,
"Schaltungstyp": "P",
"OFWeiche_center_line_width_mm": 800.0,
"OFWeiche_center_line_height_mm": 750,
"Objekt_width_mm": 853.96,
"Objekt_height_mm": 903.949,
"TEFWeiche_center_line_width_mm": 316.9403,
"TEFWeiche_center_line_height_mm": 764.5864,
"OFWeiche_CP1_x_mm": 414.8776,
"OFWeiche_CP1_y_mm": 764.8776,
"OFWeiche_CP2_x_mm": 14.8775,
"OFWeiche_CP2_y_mm": 14.8775,
"OFWeiche_CP3_x_mm": 814.8776,
"OFWeiche_CP3_y_mm": 14.8775,
"TEFWeiche_CPR1_x_mm": 414.8776,
"TEFWeiche_CPR1_y_mm": 903.9492,
"TEFWeiche_CPR2_x_mm": 831.8279,
"TEFWeiche_CPR2_y_mm": 139.3628,
"KurvenRichtung": 3,
"SivasnrTEF": "0_B10090+0_B10090"
},
{
"Sivasnr": "0_BG190090+834372104+0_BG190090",
"ProfilTyp": "WEICHE S D 45°-400/750, KPL. MIT P mit TEF beideseitig",
"WeichenTyp": "Doppelweiche",
"KurvenWinkel": 45,
"Schaltungstyp": "P",
"OFWeiche_center_line_width_mm": 800.0,
"OFWeiche_center_line_height_mm": 750,
"Objekt_width_mm": 878.165,
"Objekt_height_mm": 903.949,
"TEFWeiche_center_line_width_mm": 833.9006,
"TEFWeiche_center_line_height_mm": 764.5864,
"OFWeiche_CP1_x_mm": 439.0827,
"OFWeiche_CP1_y_mm": 764.8776,
"OFWeiche_CP2_x_mm": 39.0827,
"OFWeiche_CP2_y_mm": 14.8775,
"OFWeiche_CP3_x_mm": 839.0827,
"OFWeiche_CP3_y_mm": 14.8775,
"TEFWeiche_CPL1_x_mm": 339.0727,
"TEFWeiche_CPL1_y_mm": 903.9492,
"TEFWeiche_CPL2_x_mm": 22.1324,
"TEFWeiche_CPL2_y_mm": 139.3628,
"TEFWeiche_CPR1_x_mm": 539.0927,
"TEFWeiche_CPR1_y_mm": 903.9492,
"TEFWeiche_CPR2_x_mm": 856.0330,
"TEFWeiche_CPR2_y_mm": 139.3628,
"KurvenRichtung": 3,
"SivasnrTEF": "0_B10090+0_B10090+0_B10090+0_B10090"
},
{
"Sivasnr": "0_BG080090+834372110",
"ProfilTyp": "WEICHE S D 90°-700/700, KPL. MIT P mit TEF links",
"WeichenTyp": "Doppelweiche",
"KurvenWinkel": 90,
"Schaltungstyp": "P",
"OFWeiche_center_line_width_mm": 1400.739,
"OFWeiche_center_line_height_mm": 700.176,
"Objekt_width_mm": 1400.739,
"Objekt_height_mm": 859.292,
"TEFWeiche_center_line_width_mm": 599.685,
"TEFWeiche_center_line_height_mm": 738.242,
"OFWeiche_CP1_x_mm": 700.370,
"OFWeiche_CP1_y_mm": 721.216,
"OFWeiche_CP2_x_mm": 0,
"OFWeiche_CP2_y_mm": 21.040,
"OFWeiche_CP3_x_mm": 400.739,
"OFWeiche_CP3_y_mm": 21.040,
"TEFWeiche_CPL1_x_mm": 600.360,
"TEFWeiche_CPL1_y_mm": 859.292,
"TEFWeiche_CPL2_x_mm": 0,
"TEFWeiche_CPL2_y_mm": 121.050,
"KurvenRichtung": 3,
"SivasnrTEF": "0_B10080+0_B10090"
},
{
"Sivasnr": "834372110+0_BG080090",
"ProfilTyp": "WEICHE S D 90°-700/700, KPL. MIT P mit TEF rechts",
"WeichenTyp": "Doppelweiche",
"KurvenWinkel": 90,
"Schaltungstyp": "P",
"OFWeiche_center_line_width_mm": 1400.739,
"OFWeiche_center_line_height_mm": 700.176,
"Objekt_width_mm": 1400.739,
"Objekt_height_mm": 859.67,
"TEFWeiche_center_line_width_mm": 540.750,
"TEFWeiche_center_line_height_mm": 738.620,
"OFWeiche_CP1_x_mm": 700.370,
"OFWeiche_CP1_y_mm": 721.216,
"OFWeiche_CP2_x_mm": 0,
"OFWeiche_CP2_y_mm": 21.040,
"OFWeiche_CP3_x_mm": 400.739,
"OFWeiche_CP3_y_mm": 21.040,
"TEFWeiche_CPR1_x_mm": 800.380,
"TEFWeiche_CPR1_y_mm": 859.67,
"TEFWeiche_CPR2_x_mm": 1341.130,
"TEFWeiche_CPR2_y_mm": 121.050,
"KurvenRichtung": 3,
"SivasnrTEF": "0_B10090+0_B10080"
},
{
"Sivasnr": "0_BG080090+834372110+0_BG080090",
"ProfilTyp": "WEICHE S D 90°-700/700, KPL. MIT P mit TEF beideseitig",
"WeichenTyp": "Doppelweiche",
"KurvenWinkel": 90,
"Schaltungstyp": "P",
"OFWeiche_center_line_width_mm": 1400.739,
"OFWeiche_center_line_height_mm": 700.176,
"Objekt_width_mm": 1400.739,
"Objekt_height_mm": 859.67,
"TEFWeiche_center_line_width_mm": 1340.4546,
"TEFWeiche_center_line_height_mm": 738.6200,
"OFWeiche_CP1_x_mm": 700.370,
"OFWeiche_CP1_y_mm": 721.216,
"OFWeiche_CP2_x_mm": 0,
"OFWeiche_CP2_y_mm": 21.040,
"OFWeiche_CP3_x_mm": 400.739,
"OFWeiche_CP3_y_mm": 21.040,
"TEFWeiche_CPL1_x_mm": 600.360,
"TEFWeiche_CPL1_y_mm": 859.292,
"TEFWeiche_CPL2_x_mm": 0,
"TEFWeiche_CPL2_y_mm": 121.050,
"TEFWeiche_CPR1_x_mm": 800.380,
"TEFWeiche_CPR1_y_mm": 859.67,
"TEFWeiche_CPR2_x_mm": 1341.130,
"TEFWeiche_CPR2_y_mm": 121.050,
"KurvenRichtung": 3,
"SivasnrTEF": "0_B10080+0_B10090+0_B10080+0_B10090"
},
{
"Sivasnr": "0_BG090090+834372201",
"ProfilTyp": "WEICHE S T 45°-350/700, KPL. MIT P mit TEF links",
"WeichenTyp": "Dreiwegeweiche",
"KurvenWinkel": 45,
"Schaltungstyp": "P",
"OFWeiche_center_line_width_mm": 700.258,
"OFWeiche_center_line_height_mm": 700.123,
"Objekt_width_mm": 807.859,
"Objekt_height_mm": 858.309,
"TEFWeiche_center_line_width_mm": 320.7103,
"TEFWeiche_center_line_height_mm": 772.5864,
"OFWeiche_CP1_x_mm": 442.8527,
"OFWeiche_CP1_y_mm": 715.0010,
"OFWeiche_CP2_x_mm": 92.7236,
"OFWeiche_CP2_y_mm": 14.8778,
"OFWeiche_CP3_x_mm": 792.9823,
"OFWeiche_CP3_y_mm": 14.8778,
"TEFWeiche_CPL1_x_mm": 342.8427,
"TEFWeiche_CPL1_y_mm":858.309,
"TEFWeiche_CPL2_x_mm": 22.1324,
"TEFWeiche_CPL2_y_mm": 85.7221,
"OFWeiche_CP4_x_mm": 442.8527,
"OFWeiche_CP4_y_mm": 355.0010,
"KurvenRichtung": 7,
"SivasnrTEF": "0_B10090+0_B10090"
},
{
"Sivasnr": "834372201+0_BG090090",
"ProfilTyp": "WEICHE S T 45°-350/700, KPL. MIT P mit TEF rechts",
"WeichenTyp": "Dreiwegeweiche",
"KurvenWinkel": 45,
"Schaltungstyp": "P",
"OFWeiche_center_line_width_mm": 700.258,
"OFWeiche_center_line_height_mm": 700.123,
"Objekt_width_mm": 807.859,
"Objekt_height_mm": 858.309,
"TEFWeiche_center_line_width_mm": 320.7103,
"TEFWeiche_center_line_height_mm": 772.5864,
"OFWeiche_CP1_x_mm": 365.0067,
"OFWeiche_CP1_y_mm": 715.0010,
"OFWeiche_CP2_x_mm": 14.8778,
"OFWeiche_CP2_y_mm": 14.8778,
"OFWeiche_CP3_x_mm": 715.1357,
"OFWeiche_CP3_y_mm": 14.8778,
"TEFWeiche_CPR1_x_mm": 465.0167,
"TEFWeiche_CPR1_y_mm": 858.3085,
"TEFWeiche_CPR2_x_mm": 785.7270,
"TEFWeiche_CPR2_y_mm": 85.7221,
"OFWeiche_CP4_x_mm": 442.8527,
"OFWeiche_CP4_y_mm": 355.0010,
"KurvenRichtung": 7,
"SivasnrTEF": "0_B10090+0_B10090"
},
{
"Sivasnr": "0_BG090090+834372201+0_BG090090",
"ProfilTyp": "WEICHE S T 45°-350/700, KPL. MIT P mit TEF beideseitig",
"WeichenTyp": "Dreiwegeweiche",
"KurvenWinkel": 45,
"Schaltungstyp": "P",
"OFWeiche_center_line_width_mm": 700.258,
"OFWeiche_center_line_height_mm": 700.123,
"Objekt_width_mm": 885.705,
"Objekt_height_mm": 858.309,
"TEFWeiche_center_line_width_mm": 841.4406,
"TEFWeiche_center_line_height_mm": 772.5864,
"OFWeiche_CP1_x_mm": 442.8527,
"OFWeiche_CP1_y_mm": 715.0010,
"OFWeiche_CP2_x_mm": 92.7236,
"OFWeiche_CP2_y_mm": 14.8778,
"OFWeiche_CP3_x_mm": 792.9823,
"OFWeiche_CP3_y_mm": 14.8778,
"TEFWeiche_CPL1_x_mm": 342.8427,
"TEFWeiche_CPL1_y_mm": 858.309,
"TEFWeiche_CPL2_x_mm": 22.1324,
"TEFWeiche_CPL2_y_mm": 85.7221,
"TEFWeiche_CPR1_x_mm": 542.8627,
"TEFWeiche_CPR1_y_mm": 858.3085,
"TEFWeiche_CPR2_x_mm": 863.5730,
"TEFWeiche_CPR2_y_mm": 85.7221,
"OFWeiche_CP4_x_mm": 442.8527,
"OFWeiche_CP4_y_mm": 355.0010,
"KurvenRichtung": 7,
"SivasnrTEF": "0_B10090+0_B10090+0_B10090+0_B10090"
},
{
"Sivasnr": "0_BG190090+834372204",
"ProfilTyp": "WEICHE S T 45°-400/750, KPL. MIT P mit TEF links",
"WeichenTyp": "Dreiwegeweiche",
"KurvenWinkel": 45,
"Schaltungstyp": "P",
"OFWeiche_center_line_width_mm": 800.0,
"OFWeiche_center_line_height_mm": 750,
"Objekt_width_mm": 853.96,
"Objekt_height_mm": 903.949,
"TEFWeiche_center_line_width_mm": 316.9403,
"TEFWeiche_center_line_height_mm": 764.5864,
"OFWeiche_CP1_x_mm": 439.0827,
"OFWeiche_CP1_y_mm": 764.8776,
"OFWeiche_CP2_x_mm": 39.0827,
"OFWeiche_CP2_y_mm": 14.8775,
"OFWeiche_CP3_x_mm": 839.0827,
"OFWeiche_CP3_y_mm": 14.8775,
"TEFWeiche_CPL1_x_mm": 339.0727,
"TEFWeiche_CPL1_y_mm": 903.9492,
"TEFWeiche_CPL2_x_mm": 22.1324,
"TEFWeiche_CPL2_y_mm": 139.3628,
"OFWeiche_CP4_x_mm": 439.0827,
"OFWeiche_CP4_y_mm": 404.8776,
"KurvenRichtung": 7,
"SivasnrTEF": "0_B10090+0_B10090"
},
{
"Sivasnr": "834372204+0_BG190090",
"ProfilTyp": "WEICHE S T 45°-400/750, KPL. MIT P mit TEF rechts",
"WeichenTyp": "Dreiwegeweiche",
"KurvenWinkel": 45,
"Schaltungstyp": "P",
"OFWeiche_center_line_width_mm": 800.0,
"OFWeiche_center_line_height_mm": 750,
"Objekt_width_mm": 853.96,
"Objekt_height_mm": 903.949,
"TEFWeiche_center_line_width_mm": 316.9403,
"TEFWeiche_center_line_height_mm": 764.5864,
"OFWeiche_CP1_x_mm": 414.8776,
"OFWeiche_CP1_y_mm": 764.8776,
"OFWeiche_CP2_x_mm": 14.8775,
"OFWeiche_CP2_y_mm": 14.8775,
"OFWeiche_CP3_x_mm": 814.8776,
"OFWeiche_CP3_y_mm": 14.8775,
"TEFWeiche_CPR1_x_mm": 414.8776,
"TEFWeiche_CPR1_y_mm": 903.9492,
"TEFWeiche_CPR2_x_mm": 831.8279,
"TEFWeiche_CPR2_y_mm": 139.3628,
"OFWeiche_CP4_x_mm": 414.8776,
"OFWeiche_CP4_y_mm": 404.8776,
"KurvenRichtung": 7,
"SivasnrTEF": "0_B10090+0_B10090"
},
{
"Sivasnr": "0_BG190090+834372204+0_BG190090",
"ProfilTyp": "WEICHE S T 45°-400/750, KPL. MIT P mit TEF beideseitig",
"WeichenTyp": "Dreiwegeweiche",
"KurvenWinkel": 45,
"Schaltungstyp": "P",
"OFWeiche_center_line_width_mm": 800.0,
"OFWeiche_center_line_height_mm": 750,
"Objekt_width_mm": 878.165,
"Objekt_height_mm": 903.949,
"TEFWeiche_center_line_width_mm": 833.9006,
"TEFWeiche_center_line_height_mm": 764.5864,
"OFWeiche_CP1_x_mm": 439.0827,
"OFWeiche_CP1_y_mm": 764.8776,
"OFWeiche_CP2_x_mm": 39.0827,
"OFWeiche_CP2_y_mm": 14.8775,
"OFWeiche_CP3_x_mm": 839.0827,
"OFWeiche_CP3_y_mm": 14.8775,
"TEFWeiche_CPL1_x_mm": 339.0727,
"TEFWeiche_CPL1_y_mm": 903.9492,
"TEFWeiche_CPL2_x_mm": 22.1324,
"TEFWeiche_CPL2_y_mm": 139.3628,
"TEFWeiche_CPR1_x_mm": 539.0927,
"TEFWeiche_CPR1_y_mm": 903.9492,
"TEFWeiche_CPR2_x_mm": 856.0330,
"TEFWeiche_CPR2_y_mm": 139.3628,
"OFWeiche_CP4_x_mm": 439.0827,
"OFWeiche_CP4_y_mm": 404.8776,
"KurvenRichtung": 7,
"SivasnrTEF": "0_B10090+0_B10090+0_B10090+0_B10090"
},
{
"Sivasnr": "0_BG080090+834372210",
"ProfilTyp": "WEICHE S T 90°-700/700, KPL. MIT P mit TEF links",
"WeichenTyp": "Dreiwegeweiche",
"KurvenWinkel": 90,
"Schaltungstyp": "P",
"OFWeiche_center_line_width_mm": 1400.739,
"OFWeiche_center_line_height_mm": 700.176,
"Objekt_width_mm": 1400.739,
"Objekt_height_mm": 859.292,
"TEFWeiche_center_line_width_mm": 599.6846,
"TEFWeiche_center_line_height_mm": 738.2422,
"OFWeiche_CP1_x_mm": 700.370,
"OFWeiche_CP1_y_mm": 721.216,
"OFWeiche_CP2_x_mm": 0,
"OFWeiche_CP2_y_mm": 21.040,
"OFWeiche_CP3_x_mm": 400.739,
"OFWeiche_CP3_y_mm": 21.040,
"OFWeiche_CP4_x_mm": 700.370,
"OFWeiche_CP4_y_mm": 361.216,
"TEFWeiche_CPL1_x_mm": 600.360,
"TEFWeiche_CPL1_y_mm": 859.292,
"TEFWeiche_CPL2_x_mm": 0,
"TEFWeiche_CPL2_y_mm": 121.050,
"KurvenRichtung": 7,
"SivasnrTEF": "0_B10080+0_B10090"
},
{
"Sivasnr": "834372210+0_BG080090",
"ProfilTyp": "WEICHE S T 90°-700/700, KPL. MIT P mit TEF rechts",
"WeichenTyp": "Dreiwegeweiche",
"KurvenWinkel": 90,
"Schaltungstyp": "P",
"OFWeiche_center_line_width_mm": 1400.739,
"OFWeiche_center_line_height_mm": 700.176,
"Objekt_width_mm": 1400.739,
"Objekt_height_mm": 859.67,
"TEFWeiche_center_line_width_mm": 540.750,
"TEFWeiche_center_line_height_mm": 738.6200,
"OFWeiche_CP1_x_mm": 700.370,
"OFWeiche_CP1_y_mm": 721.216,
"OFWeiche_CP2_x_mm": 0,
"OFWeiche_CP2_y_mm": 21.040,
"OFWeiche_CP3_x_mm": 400.739,
"OFWeiche_CP3_y_mm": 21.040,
"OFWeiche_CP4_x_mm": 700.370,
"OFWeiche_CP4_y_mm": 361.216,
"TEFWeiche_CPR1_x_mm": 800.380,
"TEFWeiche_CPR1_y_mm": 859.67,
"TEFWeiche_CPR2_x_mm": 1341.130,
"TEFWeiche_CPR2_y_mm": 121.050,
"KurvenRichtung": 7,
"SivasnrTEF": "0_B10090+0_B10080"
},
{
"Sivasnr": "0_BG080090+834372210+0_BG080090",
"ProfilTyp": "WEICHE S T 90°-700/700, KPL. MIT P mit TEF beideseitig",
"WeichenTyp": "Dreiwegeweiche",
"KurvenWinkel": 90,
"Schaltungstyp": "P",
"OFWeiche_center_line_width_mm": 1400.739,
"OFWeiche_center_line_height_mm": 700.176,
"Objekt_width_mm": 1400.739,
"Objekt_height_mm": 859.67,
"TEFWeiche_center_line_width_mm": 1340.4546,
"TEFWeiche_center_line_height_mm": 738.6200,
"OFWeiche_CP1_x_mm": 700.370,
"OFWeiche_CP1_y_mm": 721.216,
"OFWeiche_CP2_x_mm": 0,
"OFWeiche_CP2_y_mm": 21.040,
"OFWeiche_CP3_x_mm": 400.739,
"OFWeiche_CP3_y_mm": 21.040,
"OFWeiche_CP4_x_mm": 700.370,
"OFWeiche_CP4_y_mm": 361.216,
"TEFWeiche_CPL1_x_mm": 600.360,
"TEFWeiche_CPL1_y_mm": 859.292,
"TEFWeiche_CPL2_x_mm": 0,
"TEFWeiche_CPL2_y_mm": 121.050,
"TEFWeiche_CPR1_x_mm": 800.380,
"TEFWeiche_CPR1_y_mm": 859.67,
"TEFWeiche_CPR2_x_mm": 1341.130,
"TEFWeiche_CPR2_y_mm": 121.050,
"KurvenRichtung": 7,
"SivasnrTEF": "0_B10080+0_B10090+0_B10080+0_B10090"
},
{
"Sivasnr": "0_BG071090+834372404+0_BG071090",
"ProfilTyp": "WEICHE S C DELTA 1600/800, KPL. MIT P mit TEF beideseitig",
"WeichenTyp": "Dreifachweiche",
"KurvenWinkel": 90,
"Schaltungstyp": "P",
"OFWeiche_center_line_width_mm": 1600.384,
"OFWeiche_center_line_height_mm": 800.266,
"Objekt_width_mm": 1878.94,
"Objekt_height_mm": 994.044,
"TEFWeiche_center_line_width_mm": 1878.94,
"TEFWeiche_center_line_height_mm": 838.62,
"OFWeiche_CP1_x_mm": 938.630,
"OFWeiche_CP1_y_mm": 800.266,
"OFWeiche_CP2_x_mm": 138.5055,
"OFWeiche_CP2_y_mm": 53.5437,
"OFWeiche_CP3_x_mm": 1738.8898,
"OFWeiche_CP3_y_mm": 53.5437,
"TEFWeiche_CPL1_x_mm": 838.6200,
"TEFWeiche_CPL1_y_mm": 994.0437,
"TEFWeiche_CPL2_x_mm": 0,
"TEFWeiche_CPL2_y_mm": 153.5537,
"TEFWeiche_CPR1_x_mm": 1038.6400,
"TEFWeiche_CPR1_y_mm": 994.0437,
"TEFWeiche_CPR2_x_mm": 1878.9400,
"TEFWeiche_CPR2_y_mm": 153.5537,
"KurvenRichtung": 7,
"SivasnrTEF": "0_B10071+0_B10090+0_B10090+0_B10071+0_B10090+0_B10090"
},
{
"Sivasnr": "0_BG071090+834372404",
"ProfilTyp": "WEICHE S C DELTA 1600/800, KPL. MIT P mit TEF links",
"WeichenTyp": "Dreifachweiche",
"KurvenWinkel": 90,
"Schaltungstyp": "P",
"OFWeiche_center_line_width_mm": 1600.384,
"OFWeiche_center_line_height_mm": 800.266,
"Objekt_width_mm": 1878.9400,
"Objekt_height_mm": 994.0437,
"TEFWeiche_center_line_width_mm": 840.49,
"TEFWeiche_center_line_height_mm": 838.62,
"OFWeiche_CP1_x_mm": 938.630,
"OFWeiche_CP1_y_mm": 853.8099,
"OFWeiche_CP2_x_mm": 138.5055,
"OFWeiche_CP2_y_mm": 53.5437,
"OFWeiche_CP3_x_mm": 1738.8898,
"OFWeiche_CP3_y_mm": 53.5437,
"TEFWeiche_CPL1_x_mm": 838.6200,
"TEFWeiche_CPL1_y_mm": 994.0437,
"TEFWeiche_CPL2_x_mm": 0,
"TEFWeiche_CPL2_y_mm": 153.5537,
"KurvenRichtung": 7,
"SivasnrTEF": "0_B10071+0_B10090+0_B10090"
},
{
"Sivasnr": "834372404+0_BG071090",
"ProfilTyp": "WEICHE S C DELTA 1600/800, KPL. MIT P mit TEF rechts",
"WeichenTyp": "Dreifachweiche",
"KurvenWinkel": 90,
"Schaltungstyp": "P",
"OFWeiche_center_line_width_mm": 1600.384,
"OFWeiche_center_line_height_mm": 800.266,
"Objekt_width_mm": 1740.435,
"Objekt_height_mm": 992.364,
"TEFWeiche_center_line_width_mm": 840.3,
"TEFWeiche_center_line_height_mm": 838.81,
"OFWeiche_CP1_x_mm": 800.1245,
"OFWeiche_CP1_y_mm": 853.8099,
"OFWeiche_CP2_x_mm": 0,
"OFWeiche_CP2_y_mm": 53.5437,
"OFWeiche_CP3_x_mm": 1600.384,
"OFWeiche_CP3_y_mm": 53.5437,
"TEFWeiche_CPR1_x_mm": 900.1345,
"TEFWeiche_CPR1_y_mm": 992.3637,
"TEFWeiche_CPR2_x_mm": 1740.435,
"TEFWeiche_CPR2_y_mm": 153.5537,
"KurvenRichtung": 7,
"SivasnrTEF": "0_B10090+0_B10090+0_B10071"
}
]
File diff suppressed because it is too large Load Diff