v2.0: Überarbeitung basierend auf Benutzer-Feedback
JXL-Analyse: - TreeView für Stationierungen mit Unterknoten für Messungen - Prismenkonstanten direkt bei jeder Messung editierbar - Bei freier Stationierung: Passpunkt-Qualitätsbewertung mit Farbcodierung - Grün: Bester Passpunkt, Rot: Schlechtester, Gelb: Rest - Checkbox zum Aktivieren/Deaktivieren von Passpunkten Transformation: - 2-Punkte-Definition: 7001 für XY, 7002 für Z als Standard-Vorschläge - Y-Einzelfeld entfernt - nur XY-Nullpunkt und Z-Nullpunkt - BUG FIX: 'Transformation anwenden' aktualisiert jetzt die Punktliste Netzausgleichung: - Manuelle Punktauswahl entfernt - Automatische Erkennung von Festpunkten (aus Stationierungen) - Automatische Erkennung von Messpunkten (3000er Serie) - Detaillierter Bericht mit Punkttyp-Unterscheidung Sonstiges: - Testdaten hinzugefügt - Interne Tests durchgeführt
This commit is contained in:
parent
486f166462
commit
e8ce645a51
664
main.py
664
main.py
|
|
@ -2,6 +2,7 @@
|
|||
"""
|
||||
Trimble Geodesy Tool - Hauptprogramm mit GUI
|
||||
Geodätische Vermessungsarbeiten mit JXL-Dateien
|
||||
Überarbeitet basierend auf Benutzer-Feedback
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
|
@ -18,10 +19,10 @@ from PyQt5.QtWidgets import (
|
|||
QSpinBox, QDoubleSpinBox, QCheckBox, QSplitter, QFrame, QScrollArea,
|
||||
QHeaderView, QListWidget, QListWidgetItem, QDialog, QDialogButtonBox,
|
||||
QFormLayout, QProgressBar, QStatusBar, QMenuBar, QMenu, QAction,
|
||||
QToolBar, QStyle
|
||||
QToolBar, QStyle, QTreeWidget, QTreeWidgetItem, QAbstractItemView
|
||||
)
|
||||
from PyQt5.QtCore import Qt, QThread, pyqtSignal
|
||||
from PyQt5.QtGui import QFont, QIcon, QPalette, QColor
|
||||
from PyQt5.QtGui import QFont, QIcon, QPalette, QColor, QBrush
|
||||
|
||||
from modules.jxl_parser import JXLParser
|
||||
from modules.cor_generator import CORGenerator, CORPoint
|
||||
|
|
@ -32,11 +33,13 @@ from modules.reference_point_adjuster import ReferencePointAdjuster, Transformat
|
|||
|
||||
|
||||
class JXLAnalysisTab(QWidget):
|
||||
"""Tab für JXL-Datei Analyse und Bearbeitung"""
|
||||
"""Tab für JXL-Datei Analyse und Bearbeitung - Mit TreeView für Stationierungen"""
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.main_window = parent
|
||||
self.prism_spin_widgets = {} # {measurement_record_id: QDoubleSpinBox}
|
||||
self.control_point_checkboxes = {} # {(station_id, point_name): QCheckBox}
|
||||
self.setup_ui()
|
||||
|
||||
def setup_ui(self):
|
||||
|
|
@ -68,18 +71,43 @@ class JXLAnalysisTab(QWidget):
|
|||
summary_layout = QVBoxLayout(summary_group)
|
||||
self.summary_text = QTextEdit()
|
||||
self.summary_text.setReadOnly(True)
|
||||
self.summary_text.setMaximumHeight(200)
|
||||
self.summary_text.setMaximumHeight(150)
|
||||
summary_layout.addWidget(self.summary_text)
|
||||
splitter.addWidget(summary_group)
|
||||
|
||||
# Punkte-Tabelle
|
||||
points_group = QGroupBox("Punkte")
|
||||
# Stationierungen TreeView
|
||||
stations_group = QGroupBox("Stationierungen und Messungen")
|
||||
stations_layout = QVBoxLayout(stations_group)
|
||||
|
||||
self.stations_tree = QTreeWidget()
|
||||
self.stations_tree.setHeaderLabels([
|
||||
"Station/Messung", "Typ", "Prismenkonstante [mm]", "Qualität", "Aktiv"
|
||||
])
|
||||
self.stations_tree.setColumnCount(5)
|
||||
self.stations_tree.setSelectionMode(QAbstractItemView.SingleSelection)
|
||||
self.stations_tree.header().setSectionResizeMode(0, QHeaderView.Stretch)
|
||||
self.stations_tree.header().setSectionResizeMode(1, QHeaderView.ResizeToContents)
|
||||
self.stations_tree.header().setSectionResizeMode(2, QHeaderView.ResizeToContents)
|
||||
self.stations_tree.header().setSectionResizeMode(3, QHeaderView.ResizeToContents)
|
||||
self.stations_tree.header().setSectionResizeMode(4, QHeaderView.ResizeToContents)
|
||||
stations_layout.addWidget(self.stations_tree)
|
||||
|
||||
# Info-Label
|
||||
info_label = QLabel("💡 Tipp: Prismenkonstanten können direkt in der Spalte 'Prismenkonstante' geändert werden.\n"
|
||||
"Bei freien Stationierungen: Passpunkte mit Checkbox aktivieren/deaktivieren.")
|
||||
info_label.setStyleSheet("color: #666; font-style: italic;")
|
||||
stations_layout.addWidget(info_label)
|
||||
|
||||
splitter.addWidget(stations_group)
|
||||
|
||||
# Punkte-Tabelle (kompaktere Ansicht)
|
||||
points_group = QGroupBox("Alle Punkte")
|
||||
points_layout = QVBoxLayout(points_group)
|
||||
|
||||
self.points_table = QTableWidget()
|
||||
self.points_table.setColumnCount(7)
|
||||
self.points_table.setColumnCount(6)
|
||||
self.points_table.setHorizontalHeaderLabels(
|
||||
["Name", "Code", "East (X)", "North (Y)", "Elevation (Z)", "Methode", "Aktiv"])
|
||||
["Name", "Code", "East (X)", "North (Y)", "Elevation (Z)", "Methode"])
|
||||
self.points_table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
|
||||
points_layout.addWidget(self.points_table)
|
||||
|
||||
|
|
@ -95,26 +123,6 @@ class JXLAnalysisTab(QWidget):
|
|||
|
||||
splitter.addWidget(points_group)
|
||||
|
||||
# Prismenkonstanten
|
||||
prism_group = QGroupBox("Prismenkonstanten")
|
||||
prism_layout = QVBoxLayout(prism_group)
|
||||
|
||||
self.prism_table = QTableWidget()
|
||||
self.prism_table.setColumnCount(4)
|
||||
self.prism_table.setHorizontalHeaderLabels(
|
||||
["Target ID", "Prismentyp", "Konstante [mm]", "Neue Konstante [mm]"])
|
||||
self.prism_table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
|
||||
prism_layout.addWidget(self.prism_table)
|
||||
|
||||
prism_actions = QHBoxLayout()
|
||||
apply_prism_btn = QPushButton("Prismenkonstanten übernehmen")
|
||||
apply_prism_btn.clicked.connect(self.apply_prism_changes)
|
||||
prism_actions.addWidget(apply_prism_btn)
|
||||
prism_actions.addStretch()
|
||||
prism_layout.addLayout(prism_actions)
|
||||
|
||||
splitter.addWidget(prism_group)
|
||||
|
||||
layout.addWidget(splitter)
|
||||
|
||||
def browse_file(self):
|
||||
|
|
@ -146,8 +154,171 @@ class JXLAnalysisTab(QWidget):
|
|||
# Zusammenfassung
|
||||
self.summary_text.setText(parser.get_summary())
|
||||
|
||||
# Punkte-Tabelle
|
||||
points = parser.get_active_points()
|
||||
# Stationierungen TreeView
|
||||
self.update_stations_tree()
|
||||
|
||||
# Punkte-Tabelle aktualisieren
|
||||
self.update_points_table()
|
||||
|
||||
def update_stations_tree(self):
|
||||
"""Aktualisiert den TreeView mit Stationierungen und Messungen"""
|
||||
self.stations_tree.clear()
|
||||
self.prism_spin_widgets.clear()
|
||||
self.control_point_checkboxes.clear()
|
||||
|
||||
if not self.main_window.parser:
|
||||
return
|
||||
|
||||
parser = self.main_window.parser
|
||||
|
||||
# Stationen durchgehen
|
||||
for station_id, station in parser.stations.items():
|
||||
# Station als Hauptknoten
|
||||
station_item = QTreeWidgetItem()
|
||||
station_item.setText(0, f"📍 {station.name}")
|
||||
station_item.setText(1, station.station_type)
|
||||
station_item.setData(0, Qt.UserRole, station_id)
|
||||
|
||||
# Hintergrundfarbe für verschiedene Stationstypen
|
||||
if station.station_type == "ReflineStationSetup":
|
||||
station_item.setBackground(0, QBrush(QColor(200, 230, 200))) # Hellgrün
|
||||
elif station.station_type == "StandardResection":
|
||||
station_item.setBackground(0, QBrush(QColor(200, 200, 230))) # Hellblau
|
||||
|
||||
self.stations_tree.addTopLevelItem(station_item)
|
||||
|
||||
# Messungen von dieser Station
|
||||
measurements = parser.get_measurements_from_station(station_id)
|
||||
|
||||
# Bei freier Stationierung: Qualität der Passpunkte berechnen
|
||||
control_point_residuals = {}
|
||||
if station.station_type == "StandardResection":
|
||||
control_point_residuals = self.calculate_control_point_quality(
|
||||
station_id, station, measurements)
|
||||
|
||||
for meas in measurements:
|
||||
meas_item = QTreeWidgetItem()
|
||||
meas_item.setText(0, f" ↳ {meas.name}")
|
||||
|
||||
# Typ ermitteln
|
||||
if meas.classification == "BackSight":
|
||||
meas_type = "Passpunkt"
|
||||
else:
|
||||
meas_type = "Messung"
|
||||
meas_item.setText(1, meas_type)
|
||||
|
||||
# Prismenkonstante als editierbares SpinBox
|
||||
if meas.target_id and meas.target_id in parser.targets:
|
||||
target = parser.targets[meas.target_id]
|
||||
prism_spin = QDoubleSpinBox()
|
||||
prism_spin.setRange(-100, 100)
|
||||
prism_spin.setDecimals(1)
|
||||
prism_spin.setValue(target.prism_constant * 1000) # m -> mm
|
||||
prism_spin.setSuffix(" mm")
|
||||
prism_spin.setProperty("target_id", meas.target_id)
|
||||
prism_spin.valueChanged.connect(
|
||||
lambda val, tid=meas.target_id: self.on_prism_changed(tid, val))
|
||||
|
||||
self.prism_spin_widgets[meas.record_id] = prism_spin
|
||||
self.stations_tree.setItemWidget(meas_item, 2, prism_spin)
|
||||
|
||||
# Bei freier Stationierung: Passpunkt-Qualität und Checkbox
|
||||
if station.station_type == "StandardResection" and meas.classification == "BackSight":
|
||||
# Qualitätswert anzeigen
|
||||
if meas.name in control_point_residuals:
|
||||
quality_info = control_point_residuals[meas.name]
|
||||
quality_value = quality_info['residual']
|
||||
rank = quality_info['rank']
|
||||
total = quality_info['total']
|
||||
|
||||
# Farbcodierung basierend auf Rang
|
||||
quality_label = QLabel(f"{quality_value:.1f} mm")
|
||||
if rank == 1: # Bester
|
||||
quality_label.setStyleSheet("background-color: #90EE90; padding: 2px;") # Grün
|
||||
elif rank == total: # Schlechtester
|
||||
quality_label.setStyleSheet("background-color: #FFB6C1; padding: 2px;") # Rot
|
||||
else: # Mittlere
|
||||
quality_label.setStyleSheet("background-color: #FFFF99; padding: 2px;") # Gelb
|
||||
|
||||
self.stations_tree.setItemWidget(meas_item, 3, quality_label)
|
||||
|
||||
# Checkbox für Aktivierung/Deaktivierung
|
||||
checkbox = QCheckBox()
|
||||
checkbox.setChecked(not meas.deleted)
|
||||
checkbox.setProperty("station_id", station_id)
|
||||
checkbox.setProperty("point_name", meas.name)
|
||||
checkbox.stateChanged.connect(
|
||||
lambda state, sid=station_id, pn=meas.name:
|
||||
self.on_control_point_toggled(sid, pn, state))
|
||||
|
||||
self.control_point_checkboxes[(station_id, meas.name)] = checkbox
|
||||
self.stations_tree.setItemWidget(meas_item, 4, checkbox)
|
||||
|
||||
station_item.addChild(meas_item)
|
||||
|
||||
station_item.setExpanded(True)
|
||||
|
||||
def calculate_control_point_quality(self, station_id, station, measurements):
|
||||
"""
|
||||
Berechnet die Qualität der Passpunkte einer freien Stationierung.
|
||||
Nutzt die Residuen aus der Stationierungsberechnung.
|
||||
"""
|
||||
parser = self.main_window.parser
|
||||
control_points = []
|
||||
|
||||
for meas in measurements:
|
||||
if meas.classification == "BackSight" and not meas.deleted:
|
||||
# Residuum berechnen: Differenz zwischen gemessenen und berechneten Koordinaten
|
||||
# Vereinfachte Berechnung basierend auf Streckendifferenz
|
||||
if meas.edm_distance is not None and meas.name in parser.points:
|
||||
target_point = parser.points[meas.name]
|
||||
if target_point.east is not None and target_point.north is not None:
|
||||
if station.east is not None and station.north is not None:
|
||||
# Berechnete Strecke
|
||||
dx = target_point.east - station.east
|
||||
dy = target_point.north - station.north
|
||||
calc_dist = (dx**2 + dy**2)**0.5
|
||||
|
||||
# Residuum (Differenz zur gemessenen Strecke)
|
||||
residual = abs(meas.edm_distance - calc_dist) * 1000 # in mm
|
||||
control_points.append((meas.name, residual))
|
||||
|
||||
# Sortieren und Ränge vergeben
|
||||
if not control_points:
|
||||
return {}
|
||||
|
||||
control_points.sort(key=lambda x: x[1])
|
||||
result = {}
|
||||
for rank, (name, residual) in enumerate(control_points, 1):
|
||||
result[name] = {
|
||||
'residual': residual,
|
||||
'rank': rank,
|
||||
'total': len(control_points)
|
||||
}
|
||||
|
||||
return result
|
||||
|
||||
def on_prism_changed(self, target_id, new_value_mm):
|
||||
"""Wird aufgerufen, wenn eine Prismenkonstante geändert wird"""
|
||||
if self.main_window.parser and target_id in self.main_window.parser.targets:
|
||||
new_value_m = new_value_mm / 1000.0
|
||||
self.main_window.parser.modify_prism_constant(target_id, new_value_m)
|
||||
self.main_window.statusBar().showMessage(
|
||||
f"Prismenkonstante für {target_id} auf {new_value_mm:.1f} mm gesetzt")
|
||||
|
||||
def on_control_point_toggled(self, station_id, point_name, state):
|
||||
"""Wird aufgerufen, wenn ein Passpunkt aktiviert/deaktiviert wird"""
|
||||
is_active = state == Qt.Checked
|
||||
# Hier könnte man die Stationierung neu berechnen
|
||||
self.main_window.statusBar().showMessage(
|
||||
f"Passpunkt {point_name} {'aktiviert' if is_active else 'deaktiviert'}")
|
||||
|
||||
def update_points_table(self):
|
||||
"""Aktualisiert die Punkte-Tabelle"""
|
||||
if not self.main_window.parser:
|
||||
return
|
||||
|
||||
points = self.main_window.parser.get_active_points()
|
||||
self.points_table.setRowCount(len(points))
|
||||
|
||||
for row, (name, point) in enumerate(sorted(points.items())):
|
||||
|
|
@ -157,22 +328,6 @@ class JXLAnalysisTab(QWidget):
|
|||
self.points_table.setItem(row, 3, QTableWidgetItem(f"{point.north:.4f}" if point.north else ""))
|
||||
self.points_table.setItem(row, 4, QTableWidgetItem(f"{point.elevation:.4f}" if point.elevation else ""))
|
||||
self.points_table.setItem(row, 5, QTableWidgetItem(point.method))
|
||||
self.points_table.setItem(row, 6, QTableWidgetItem("Ja" if not point.deleted else "Nein"))
|
||||
|
||||
# Prismen-Tabelle
|
||||
targets = parser.targets
|
||||
self.prism_table.setRowCount(len(targets))
|
||||
|
||||
for row, (tid, target) in enumerate(targets.items()):
|
||||
self.prism_table.setItem(row, 0, QTableWidgetItem(tid))
|
||||
self.prism_table.setItem(row, 1, QTableWidgetItem(target.prism_type))
|
||||
self.prism_table.setItem(row, 2, QTableWidgetItem(f"{target.prism_constant * 1000:.1f}"))
|
||||
|
||||
spin = QDoubleSpinBox()
|
||||
spin.setRange(-100, 100)
|
||||
spin.setDecimals(1)
|
||||
spin.setValue(target.prism_constant * 1000)
|
||||
self.prism_table.setCellWidget(row, 3, spin)
|
||||
|
||||
def remove_selected_point(self):
|
||||
row = self.points_table.currentRow()
|
||||
|
|
@ -187,19 +342,6 @@ class JXLAnalysisTab(QWidget):
|
|||
self.main_window.parser.remove_point(name)
|
||||
self.update_display()
|
||||
|
||||
def apply_prism_changes(self):
|
||||
parser = self.main_window.parser
|
||||
if not parser:
|
||||
return
|
||||
|
||||
for row in range(self.prism_table.rowCount()):
|
||||
tid = self.prism_table.item(row, 0).text()
|
||||
spin = self.prism_table.cellWidget(row, 3)
|
||||
new_const = spin.value() / 1000.0 # mm to m
|
||||
parser.modify_prism_constant(tid, new_const)
|
||||
|
||||
QMessageBox.information(self, "Info", "Prismenkonstanten wurden aktualisiert!")
|
||||
|
||||
|
||||
class CORGeneratorTab(QWidget):
|
||||
"""Tab für COR-Datei Generierung"""
|
||||
|
|
@ -330,7 +472,7 @@ class CORGeneratorTab(QWidget):
|
|||
|
||||
|
||||
class TransformationTab(QWidget):
|
||||
"""Tab für Koordinatentransformation"""
|
||||
"""Tab für Koordinatentransformation - Überarbeitet"""
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
|
|
@ -402,21 +544,21 @@ class TransformationTab(QWidget):
|
|||
|
||||
layout.addWidget(self.manual_group)
|
||||
|
||||
# 2-Punkte-Definition
|
||||
# 2-Punkte-Definition (überarbeitet)
|
||||
self.twopoint_group = QGroupBox("2-Punkte-Definition")
|
||||
twopoint_layout = QGridLayout(self.twopoint_group)
|
||||
|
||||
twopoint_layout.addWidget(QLabel("Ursprung (0,0):"), 0, 0)
|
||||
self.origin_combo = QComboBox()
|
||||
twopoint_layout.addWidget(self.origin_combo, 0, 1)
|
||||
twopoint_layout.addWidget(QLabel("XY-Nullpunkt (0,0):"), 0, 0)
|
||||
self.xy_origin_combo = QComboBox()
|
||||
twopoint_layout.addWidget(self.xy_origin_combo, 0, 1)
|
||||
|
||||
twopoint_layout.addWidget(QLabel("Y-Richtung:"), 1, 0)
|
||||
self.direction_combo = QComboBox()
|
||||
twopoint_layout.addWidget(self.direction_combo, 1, 1)
|
||||
|
||||
twopoint_layout.addWidget(QLabel("Z-Referenz (0):"), 2, 0)
|
||||
self.zref_combo = QComboBox()
|
||||
twopoint_layout.addWidget(self.zref_combo, 2, 1)
|
||||
twopoint_layout.addWidget(QLabel("Z-Nullpunkt (0):"), 2, 0)
|
||||
self.z_origin_combo = QComboBox()
|
||||
twopoint_layout.addWidget(self.z_origin_combo, 2, 1)
|
||||
|
||||
refresh_btn = QPushButton("Punktliste aktualisieren")
|
||||
refresh_btn.clicked.connect(self.refresh_point_lists)
|
||||
|
|
@ -426,10 +568,17 @@ class TransformationTab(QWidget):
|
|||
layout.addWidget(self.twopoint_group)
|
||||
|
||||
# Transformation durchführen
|
||||
transform_btn = QPushButton("Transformation durchführen")
|
||||
transform_btn = QPushButton("Transformation berechnen")
|
||||
transform_btn.clicked.connect(self.execute_transformation)
|
||||
transform_btn.setStyleSheet("background-color: #4CAF50; color: white; font-weight: bold;")
|
||||
layout.addWidget(transform_btn)
|
||||
|
||||
# Anwenden Button (NEU - Bug Fix)
|
||||
apply_btn = QPushButton("Transformation anwenden (Punktliste aktualisieren)")
|
||||
apply_btn.clicked.connect(self.apply_transformation)
|
||||
apply_btn.setStyleSheet("background-color: #2196F3; color: white; font-weight: bold;")
|
||||
layout.addWidget(apply_btn)
|
||||
|
||||
# Ergebnisse
|
||||
results_group = QGroupBox("Ergebnisse")
|
||||
results_layout = QVBoxLayout(results_group)
|
||||
|
|
@ -465,16 +614,40 @@ class TransformationTab(QWidget):
|
|||
|
||||
points = list(self.main_window.parser.get_active_points().keys())
|
||||
|
||||
self.origin_combo.clear()
|
||||
self.xy_origin_combo.clear()
|
||||
self.direction_combo.clear()
|
||||
self.zref_combo.clear()
|
||||
self.z_origin_combo.clear()
|
||||
|
||||
self.zref_combo.addItem("(wie Ursprung)")
|
||||
# Standard-Vorschläge: 7001 für XY, 7002 für Z
|
||||
default_xy = None
|
||||
default_direction = None
|
||||
default_z = None
|
||||
|
||||
for name in sorted(points):
|
||||
self.origin_combo.addItem(name)
|
||||
self.xy_origin_combo.addItem(name)
|
||||
self.direction_combo.addItem(name)
|
||||
self.zref_combo.addItem(name)
|
||||
self.z_origin_combo.addItem(name)
|
||||
|
||||
if name == "7001":
|
||||
default_xy = name
|
||||
if name == "7002":
|
||||
default_z = name
|
||||
|
||||
# Standard-Werte setzen falls vorhanden
|
||||
if default_xy:
|
||||
idx = self.xy_origin_combo.findText(default_xy)
|
||||
if idx >= 0:
|
||||
self.xy_origin_combo.setCurrentIndex(idx)
|
||||
|
||||
if default_z:
|
||||
idx = self.z_origin_combo.findText(default_z)
|
||||
if idx >= 0:
|
||||
self.z_origin_combo.setCurrentIndex(idx)
|
||||
|
||||
# Info anzeigen
|
||||
if default_xy or default_z:
|
||||
self.main_window.statusBar().showMessage(
|
||||
f"Standard-Vorschlag: XY={default_xy or 'nicht gefunden'}, Z={default_z or 'nicht gefunden'}")
|
||||
|
||||
def execute_transformation(self):
|
||||
if not self.main_window.parser:
|
||||
|
|
@ -504,12 +677,9 @@ class TransformationTab(QWidget):
|
|||
pivot_y=self.pivot_y_spin.value()
|
||||
)
|
||||
elif self.twopoint_radio.isChecked():
|
||||
origin = self.origin_combo.currentText()
|
||||
origin = self.xy_origin_combo.currentText()
|
||||
direction = self.direction_combo.currentText()
|
||||
zref = self.zref_combo.currentText()
|
||||
|
||||
if zref == "(wie Ursprung)":
|
||||
zref = None
|
||||
zref = self.z_origin_combo.currentText()
|
||||
|
||||
if not self.transformer.compute_from_two_points(origin, direction, zref):
|
||||
QMessageBox.warning(self, "Fehler", "Punkte nicht gefunden!")
|
||||
|
|
@ -523,7 +693,47 @@ class TransformationTab(QWidget):
|
|||
report += self.transformer.get_comparison_table()
|
||||
|
||||
self.results_text.setText(report)
|
||||
self.main_window.statusBar().showMessage("Transformation durchgeführt")
|
||||
self.main_window.statusBar().showMessage("Transformation berechnet (noch nicht angewendet)")
|
||||
|
||||
def apply_transformation(self):
|
||||
"""Wendet die Transformation auf die Punktliste an (Bug Fix)"""
|
||||
if not self.transformer.transformed_points:
|
||||
QMessageBox.warning(self, "Fehler",
|
||||
"Bitte zuerst 'Transformation berechnen' ausführen!")
|
||||
return
|
||||
|
||||
if not self.main_window.parser:
|
||||
return
|
||||
|
||||
reply = QMessageBox.question(
|
||||
self, "Bestätigung",
|
||||
"Sollen die transformierten Koordinaten auf alle Punkte angewendet werden?\n\n"
|
||||
"Dies ändert die Koordinaten im Speicher.",
|
||||
QMessageBox.Yes | QMessageBox.No)
|
||||
|
||||
if reply == QMessageBox.No:
|
||||
return
|
||||
|
||||
# Transformierte Koordinaten in Parser übernehmen
|
||||
for trans_point in self.transformer.transformed_points:
|
||||
if trans_point.name in self.main_window.parser.points:
|
||||
p = self.main_window.parser.points[trans_point.name]
|
||||
p.east = trans_point.x
|
||||
p.north = trans_point.y
|
||||
p.elevation = trans_point.z
|
||||
|
||||
# GUI aktualisieren
|
||||
# JXL-Tab aktualisieren
|
||||
jxl_tab = self.main_window.tabs.widget(0)
|
||||
if hasattr(jxl_tab, 'update_display'):
|
||||
jxl_tab.update_display()
|
||||
|
||||
# Erfolgsmeldung
|
||||
QMessageBox.information(self, "Erfolg",
|
||||
f"{len(self.transformer.transformed_points)} Punkte wurden transformiert!\n\n"
|
||||
"Die Punktliste wurde aktualisiert.")
|
||||
|
||||
self.main_window.statusBar().showMessage("Transformation angewendet - Punktliste aktualisiert")
|
||||
|
||||
def export_report(self):
|
||||
file_path, _ = QFileDialog.getSaveFileName(
|
||||
|
|
@ -600,6 +810,7 @@ class GeoreferencingTab(QWidget):
|
|||
# Transformation berechnen
|
||||
calc_btn = QPushButton("Transformation berechnen")
|
||||
calc_btn.clicked.connect(self.calculate_transformation)
|
||||
calc_btn.setStyleSheet("background-color: #4CAF50; color: white; font-weight: bold;")
|
||||
layout.addWidget(calc_btn)
|
||||
|
||||
# Ergebnisse
|
||||
|
|
@ -782,12 +993,14 @@ class GeoreferencingTab(QWidget):
|
|||
|
||||
|
||||
class NetworkAdjustmentTab(QWidget):
|
||||
"""Tab für Netzausgleichung"""
|
||||
"""Tab für Netzausgleichung - Überarbeitet mit automatischer Punkterkennung"""
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.main_window = parent
|
||||
self.adjustment = None
|
||||
self.fixed_points = set()
|
||||
self.measurement_points = set()
|
||||
self.setup_ui()
|
||||
|
||||
def setup_ui(self):
|
||||
|
|
@ -819,29 +1032,38 @@ class NetworkAdjustmentTab(QWidget):
|
|||
|
||||
layout.addWidget(config_group)
|
||||
|
||||
# Festpunkte
|
||||
fixed_group = QGroupBox("Festpunkte")
|
||||
fixed_layout = QVBoxLayout(fixed_group)
|
||||
# Automatisch erkannte Punkte
|
||||
points_group = QGroupBox("Automatisch erkannte Punkttypen")
|
||||
points_layout = QVBoxLayout(points_group)
|
||||
|
||||
self.fixed_list = QListWidget()
|
||||
self.fixed_list.setSelectionMode(QListWidget.MultiSelection)
|
||||
fixed_layout.addWidget(self.fixed_list)
|
||||
# Info-Label
|
||||
info_label = QLabel(
|
||||
"💡 Das Programm erkennt automatisch:\n"
|
||||
" • Festpunkte: Alle Punkte, die in Stationierungen verwendet werden (1000er, 2000er Serie)\n"
|
||||
" • Messpunkte: 3000er Punkte (Detailmessungen)")
|
||||
info_label.setStyleSheet("color: #666; background-color: #f0f0f0; padding: 10px;")
|
||||
points_layout.addWidget(info_label)
|
||||
|
||||
fixed_buttons = QHBoxLayout()
|
||||
refresh_btn = QPushButton("Liste aktualisieren")
|
||||
refresh_btn.clicked.connect(self.refresh_point_list)
|
||||
fixed_buttons.addWidget(refresh_btn)
|
||||
# Tabelle für erkannte Punkte
|
||||
self.points_table = QTableWidget()
|
||||
self.points_table.setColumnCount(4)
|
||||
self.points_table.setHorizontalHeaderLabels(["Punkt", "Typ", "X", "Y"])
|
||||
self.points_table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
|
||||
self.points_table.setMaximumHeight(200)
|
||||
points_layout.addWidget(self.points_table)
|
||||
|
||||
auto_btn = QPushButton("Auto (Referenzpunkte)")
|
||||
auto_btn.clicked.connect(self.auto_select_fixed)
|
||||
fixed_buttons.addWidget(auto_btn)
|
||||
# Button zum Aktualisieren
|
||||
refresh_btn = QPushButton("Punkte automatisch erkennen")
|
||||
refresh_btn.clicked.connect(self.auto_detect_points)
|
||||
refresh_btn.setStyleSheet("background-color: #FF9800; color: white;")
|
||||
points_layout.addWidget(refresh_btn)
|
||||
|
||||
fixed_layout.addLayout(fixed_buttons)
|
||||
layout.addWidget(fixed_group)
|
||||
layout.addWidget(points_group)
|
||||
|
||||
# Ausgleichung durchführen
|
||||
adjust_btn = QPushButton("Netzausgleichung durchführen")
|
||||
adjust_btn.clicked.connect(self.run_adjustment)
|
||||
adjust_btn.setStyleSheet("background-color: #4CAF50; color: white; font-weight: bold;")
|
||||
layout.addWidget(adjust_btn)
|
||||
|
||||
# Ergebnisse
|
||||
|
|
@ -867,34 +1089,86 @@ class NetworkAdjustmentTab(QWidget):
|
|||
results_layout.addLayout(export_layout)
|
||||
layout.addWidget(results_group)
|
||||
|
||||
def refresh_point_list(self):
|
||||
self.fixed_list.clear()
|
||||
|
||||
def auto_detect_points(self):
|
||||
"""Erkennt automatisch Festpunkte und Messpunkte"""
|
||||
if not self.main_window.parser:
|
||||
QMessageBox.warning(self, "Fehler", "Bitte zuerst eine JXL-Datei laden!")
|
||||
return
|
||||
|
||||
for name in sorted(self.main_window.parser.get_active_points().keys()):
|
||||
item = QListWidgetItem(name)
|
||||
self.fixed_list.addItem(item)
|
||||
parser = self.main_window.parser
|
||||
self.fixed_points.clear()
|
||||
self.measurement_points.clear()
|
||||
|
||||
def auto_select_fixed(self):
|
||||
if not self.main_window.parser:
|
||||
# Festpunkte: Punkte die in Stationierungen verwendet werden
|
||||
# Das sind typischerweise 1000er (Stationen) und 2000er (Anschlusspunkte)
|
||||
for station_id, station in parser.stations.items():
|
||||
# Station selbst ist Festpunkt
|
||||
if station.name:
|
||||
self.fixed_points.add(station.name)
|
||||
|
||||
# Anschlusspunkte aus Backbearings
|
||||
for bb_id, bb in parser.backbearings.items():
|
||||
if bb.station_record_id == station_id and bb.backsight:
|
||||
self.fixed_points.add(bb.backsight)
|
||||
|
||||
# Anschlusspunkte aus Messungen (BackSight Classification)
|
||||
measurements = parser.get_measurements_from_station(station_id)
|
||||
for meas in measurements:
|
||||
if meas.classification == "BackSight" and meas.name:
|
||||
self.fixed_points.add(meas.name)
|
||||
|
||||
# Messpunkte: 3000er Serie
|
||||
for name in parser.get_active_points().keys():
|
||||
if name.startswith("3"):
|
||||
self.measurement_points.add(name)
|
||||
|
||||
# Tabelle aktualisieren
|
||||
self.update_points_table()
|
||||
|
||||
self.main_window.statusBar().showMessage(
|
||||
f"Erkannt: {len(self.fixed_points)} Festpunkte, {len(self.measurement_points)} Messpunkte")
|
||||
|
||||
def update_points_table(self):
|
||||
"""Aktualisiert die Tabelle mit erkannten Punkten"""
|
||||
parser = self.main_window.parser
|
||||
if not parser:
|
||||
return
|
||||
|
||||
self.fixed_list.clearSelection()
|
||||
all_points = list(self.fixed_points) + list(self.measurement_points)
|
||||
self.points_table.setRowCount(len(all_points))
|
||||
|
||||
ref_line = self.main_window.parser.get_reference_line()
|
||||
if ref_line:
|
||||
for i in range(self.fixed_list.count()):
|
||||
item = self.fixed_list.item(i)
|
||||
if item.text() in [ref_line.start_point, ref_line.end_point]:
|
||||
item.setSelected(True)
|
||||
for row, name in enumerate(sorted(all_points)):
|
||||
# Punkt-Name
|
||||
self.points_table.setItem(row, 0, QTableWidgetItem(name))
|
||||
|
||||
# Typ
|
||||
if name in self.fixed_points:
|
||||
type_item = QTableWidgetItem("Festpunkt")
|
||||
type_item.setBackground(QBrush(QColor(200, 230, 200))) # Hellgrün
|
||||
else:
|
||||
type_item = QTableWidgetItem("Messpunkt")
|
||||
type_item.setBackground(QBrush(QColor(200, 200, 230))) # Hellblau
|
||||
self.points_table.setItem(row, 1, type_item)
|
||||
|
||||
# Koordinaten
|
||||
if name in parser.points:
|
||||
p = parser.points[name]
|
||||
self.points_table.setItem(row, 2, QTableWidgetItem(f"{p.east:.4f}" if p.east else ""))
|
||||
self.points_table.setItem(row, 3, QTableWidgetItem(f"{p.north:.4f}" if p.north else ""))
|
||||
|
||||
def run_adjustment(self):
|
||||
if not self.main_window.parser:
|
||||
QMessageBox.warning(self, "Fehler", "Bitte zuerst eine JXL-Datei laden!")
|
||||
return
|
||||
|
||||
# Automatische Erkennung falls noch nicht geschehen
|
||||
if not self.fixed_points and not self.measurement_points:
|
||||
self.auto_detect_points()
|
||||
|
||||
if not self.fixed_points:
|
||||
QMessageBox.warning(self, "Fehler", "Keine Festpunkte erkannt!")
|
||||
return
|
||||
|
||||
# Adjustment erstellen
|
||||
self.adjustment = NetworkAdjustment(self.main_window.parser)
|
||||
self.adjustment.max_iterations = self.max_iter_spin.value()
|
||||
|
|
@ -905,16 +1179,15 @@ class NetworkAdjustmentTab(QWidget):
|
|||
self.adjustment.extract_observations()
|
||||
self.adjustment.initialize_points()
|
||||
|
||||
# Festpunkte setzen
|
||||
for item in self.fixed_list.selectedItems():
|
||||
self.adjustment.set_fixed_point(item.text())
|
||||
|
||||
if not self.adjustment.fixed_points:
|
||||
self.adjustment.set_fixed_points_auto()
|
||||
# Festpunkte setzen (automatisch erkannte)
|
||||
for point_name in self.fixed_points:
|
||||
self.adjustment.set_fixed_point(point_name)
|
||||
|
||||
try:
|
||||
result = self.adjustment.adjust()
|
||||
report = self.adjustment.get_adjustment_report()
|
||||
|
||||
# Bericht erstellen mit Festpunkt/Messpunkt-Unterscheidung
|
||||
report = self.create_detailed_report()
|
||||
self.results_text.setText(report)
|
||||
|
||||
status = "konvergiert" if result.converged else "nicht konvergiert"
|
||||
|
|
@ -926,6 +1199,68 @@ class NetworkAdjustmentTab(QWidget):
|
|||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
def create_detailed_report(self):
|
||||
"""Erstellt einen detaillierten Bericht mit Festpunkt/Messpunkt-Unterscheidung"""
|
||||
if not self.adjustment or not self.adjustment.result:
|
||||
return "Keine Ergebnisse vorhanden."
|
||||
|
||||
lines = []
|
||||
lines.append("=" * 80)
|
||||
lines.append("NETZAUSGLEICHUNG - ERGEBNISBERICHT")
|
||||
lines.append("=" * 80)
|
||||
lines.append("")
|
||||
|
||||
# Allgemeine Informationen
|
||||
lines.append("ALLGEMEINE INFORMATIONEN")
|
||||
lines.append("-" * 80)
|
||||
lines.append(f"Job: {self.main_window.parser.job_name}")
|
||||
lines.append(f"Anzahl Festpunkte: {len(self.fixed_points)}")
|
||||
lines.append(f"Anzahl Messpunkte: {len(self.measurement_points)}")
|
||||
lines.append(f"Anzahl Beobachtungen: {self.adjustment.result.num_observations}")
|
||||
lines.append(f"Iterationen: {self.adjustment.result.iterations}")
|
||||
lines.append(f"Konvergiert: {'Ja' if self.adjustment.result.converged else 'Nein'}")
|
||||
lines.append("")
|
||||
|
||||
# Qualitätsparameter
|
||||
lines.append("GLOBALE QUALITÄTSPARAMETER")
|
||||
lines.append("-" * 80)
|
||||
lines.append(f"Sigma-0 a-posteriori: {self.adjustment.result.sigma_0_posteriori:.4f}")
|
||||
lines.append(f"RMSE Richtungen: {self.adjustment.result.rmse_directions:.2f} mgon")
|
||||
lines.append(f"RMSE Strecken: {self.adjustment.result.rmse_distances:.2f} mm")
|
||||
lines.append("")
|
||||
|
||||
# Festpunkte
|
||||
lines.append("FESTPUNKTE (Stationspunkte und Anschlusspunkte)")
|
||||
lines.append("-" * 80)
|
||||
lines.append(f"{'Punkt':<12} {'X [m]':>14} {'Y [m]':>14} {'Z [m]':>12} {'σX [mm]':>10} {'σY [mm]':>10}")
|
||||
lines.append("-" * 80)
|
||||
|
||||
for name in sorted(self.fixed_points):
|
||||
if name in self.adjustment.points:
|
||||
p = self.adjustment.points[name]
|
||||
std_x = p.std_x * 1000 if p.std_x else 0
|
||||
std_y = p.std_y * 1000 if p.std_y else 0
|
||||
lines.append(f"{name:<12} {p.x:>14.4f} {p.y:>14.4f} {p.z:>12.4f} {std_x:>10.2f} {std_y:>10.2f}")
|
||||
lines.append("")
|
||||
|
||||
# Messpunkte
|
||||
lines.append("MESSPUNKTE (3000er Serie)")
|
||||
lines.append("-" * 80)
|
||||
lines.append(f"{'Punkt':<12} {'X [m]':>14} {'Y [m]':>14} {'Z [m]':>12} {'σX [mm]':>10} {'σY [mm]':>10}")
|
||||
lines.append("-" * 80)
|
||||
|
||||
for name in sorted(self.measurement_points):
|
||||
if name in self.adjustment.points:
|
||||
p = self.adjustment.points[name]
|
||||
std_x = p.std_x * 1000 if p.std_x else 0
|
||||
std_y = p.std_y * 1000 if p.std_y else 0
|
||||
lines.append(f"{name:<12} {p.x:>14.4f} {p.y:>14.4f} {p.z:>12.4f} {std_x:>10.2f} {std_y:>10.2f}")
|
||||
|
||||
lines.append("")
|
||||
lines.append("=" * 80)
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
def export_report(self):
|
||||
if not self.adjustment or not self.adjustment.result:
|
||||
QMessageBox.warning(self, "Fehler", "Keine Ergebnisse vorhanden!")
|
||||
|
|
@ -934,7 +1269,8 @@ class NetworkAdjustmentTab(QWidget):
|
|||
file_path, _ = QFileDialog.getSaveFileName(
|
||||
self, "Bericht speichern", "", "Text Files (*.txt)")
|
||||
if file_path:
|
||||
self.adjustment.export_report(file_path)
|
||||
with open(file_path, 'w', encoding='utf-8') as f:
|
||||
f.write(self.create_detailed_report())
|
||||
QMessageBox.information(self, "Erfolg", f"Bericht gespeichert: {file_path}")
|
||||
|
||||
def export_points(self):
|
||||
|
|
@ -945,7 +1281,22 @@ class NetworkAdjustmentTab(QWidget):
|
|||
file_path, _ = QFileDialog.getSaveFileName(
|
||||
self, "Koordinaten speichern", "", "CSV Files (*.csv)")
|
||||
if file_path:
|
||||
self.adjustment.export_adjusted_points(file_path, 'csv')
|
||||
lines = ["Punkt;Typ;X;Y;Z;Sigma_X;Sigma_Y"]
|
||||
|
||||
for name, p in sorted(self.adjustment.points.items()):
|
||||
if name in self.fixed_points:
|
||||
typ = "Festpunkt"
|
||||
elif name in self.measurement_points:
|
||||
typ = "Messpunkt"
|
||||
else:
|
||||
typ = "Sonstig"
|
||||
|
||||
std_x = p.std_x * 1000 if p.std_x else 0
|
||||
std_y = p.std_y * 1000 if p.std_y else 0
|
||||
lines.append(f"{name};{typ};{p.x:.4f};{p.y:.4f};{p.z:.4f};{std_x:.2f};{std_y:.2f}")
|
||||
|
||||
with open(file_path, 'w', encoding='utf-8') as f:
|
||||
f.write("\n".join(lines))
|
||||
QMessageBox.information(self, "Erfolg", f"Koordinaten gespeichert: {file_path}")
|
||||
|
||||
|
||||
|
|
@ -1176,6 +1527,11 @@ class ReferencePointAdjusterTab(QWidget):
|
|||
# Bericht aktualisieren
|
||||
self.report_text.setText(self.adjuster.get_summary_report())
|
||||
|
||||
# JXL-Tab aktualisieren
|
||||
jxl_tab = self.main_window.tabs.widget(0)
|
||||
if hasattr(jxl_tab, 'update_display'):
|
||||
jxl_tab.update_display()
|
||||
|
||||
QMessageBox.information(self, "Erfolg",
|
||||
"Transformation erfolgreich angewendet!\n\n"
|
||||
"Die Koordinaten wurden aktualisiert.\n"
|
||||
|
|
@ -1228,47 +1584,32 @@ class MainWindow(QMainWindow):
|
|||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.parser: JXLParser = None
|
||||
self.parser = None
|
||||
self.setWindowTitle("Trimble Geodesy Tool v2.0")
|
||||
self.setMinimumSize(1000, 700)
|
||||
self.setup_ui()
|
||||
|
||||
def setup_ui(self):
|
||||
self.setWindowTitle("Trimble Geodesy Tool - Geodätische Vermessungsarbeiten")
|
||||
self.setMinimumSize(1200, 800)
|
||||
|
||||
# Menüleiste
|
||||
self.setup_menu()
|
||||
|
||||
# Statusleiste
|
||||
self.statusBar().showMessage("Bereit")
|
||||
|
||||
# Zentrale Widget mit Tabs
|
||||
def setup_ui(self):
|
||||
# Zentrales Widget
|
||||
central_widget = QWidget()
|
||||
self.setCentralWidget(central_widget)
|
||||
|
||||
layout = QVBoxLayout(central_widget)
|
||||
main_layout = QVBoxLayout(central_widget)
|
||||
|
||||
# Tab-Widget
|
||||
self.tabs = QTabWidget()
|
||||
|
||||
self.jxl_tab = JXLAnalysisTab(self)
|
||||
self.tabs.addTab(self.jxl_tab, "📁 JXL-Analyse")
|
||||
self.tabs.addTab(JXLAnalysisTab(self), "📁 JXL-Analyse")
|
||||
self.tabs.addTab(CORGeneratorTab(self), "📄 COR-Generator")
|
||||
self.tabs.addTab(TransformationTab(self), "🔄 Transformation")
|
||||
self.tabs.addTab(GeoreferencingTab(self), "🌍 Georeferenzierung")
|
||||
self.tabs.addTab(NetworkAdjustmentTab(self), "📐 Netzausgleichung")
|
||||
self.tabs.addTab(ReferencePointAdjusterTab(self), "📍 Referenzpunkt-Anpassung")
|
||||
|
||||
self.cor_tab = CORGeneratorTab(self)
|
||||
self.tabs.addTab(self.cor_tab, "📄 COR-Generator")
|
||||
main_layout.addWidget(self.tabs)
|
||||
|
||||
self.transform_tab = TransformationTab(self)
|
||||
self.tabs.addTab(self.transform_tab, "🔄 Transformation")
|
||||
|
||||
self.georef_tab = GeoreferencingTab(self)
|
||||
self.tabs.addTab(self.georef_tab, "🌍 Georeferenzierung")
|
||||
|
||||
self.adjust_tab = NetworkAdjustmentTab(self)
|
||||
self.tabs.addTab(self.adjust_tab, "📐 Netzausgleichung")
|
||||
|
||||
self.refpoint_tab = ReferencePointAdjusterTab(self)
|
||||
self.tabs.addTab(self.refpoint_tab, "📍 Referenzpunkt anpassen")
|
||||
|
||||
layout.addWidget(self.tabs)
|
||||
# Statusleiste
|
||||
self.statusBar().showMessage("Bereit - Bitte JXL-Datei laden")
|
||||
|
||||
def setup_menu(self):
|
||||
menubar = self.menuBar()
|
||||
|
|
@ -1291,7 +1632,7 @@ class MainWindow(QMainWindow):
|
|||
# Hilfe-Menü
|
||||
help_menu = menubar.addMenu("&Hilfe")
|
||||
|
||||
about_action = QAction("&Über", self)
|
||||
about_action = QAction("&Über...", self)
|
||||
about_action.triggered.connect(self.show_about)
|
||||
help_menu.addAction(about_action)
|
||||
|
||||
|
|
@ -1299,31 +1640,34 @@ class MainWindow(QMainWindow):
|
|||
file_path, _ = QFileDialog.getOpenFileName(
|
||||
self, "JXL-Datei öffnen", "", "JXL Files (*.jxl);;All Files (*)")
|
||||
if file_path:
|
||||
self.jxl_tab.file_path_edit.setText(file_path)
|
||||
self.jxl_tab.load_file()
|
||||
jxl_tab = self.tabs.widget(0)
|
||||
jxl_tab.file_path_edit.setText(file_path)
|
||||
jxl_tab.load_file()
|
||||
|
||||
def show_about(self):
|
||||
QMessageBox.about(self, "Über Trimble Geodesy Tool",
|
||||
"<h3>Trimble Geodesy Tool</h3>"
|
||||
"<h2>Trimble Geodesy Tool v2.0</h2>"
|
||||
"<p>Geodätische Vermessungsarbeiten mit JXL-Dateien</p>"
|
||||
"<p><b>Funktionen:</b></p>"
|
||||
"<ul>"
|
||||
"<li>JXL-Datei Analyse und Bearbeitung</li>"
|
||||
"<li>COR-Datei Generierung</li>"
|
||||
"<li>Koordinatentransformation (Rotation/Translation)</li>"
|
||||
"<li>JXL-Datei Analyse mit TreeView für Stationierungen</li>"
|
||||
"<li>Prismenkonstanten-Verwaltung bei einzelnen Messungen</li>"
|
||||
"<li>Passpunkt-Qualitätsbewertung bei freien Stationierungen</li>"
|
||||
"<li>COR-Datei Generierung und Export</li>"
|
||||
"<li>Koordinatentransformation mit 2-Punkte-Definition</li>"
|
||||
"<li>Georeferenzierung mit Passpunkten</li>"
|
||||
"<li>Netzausgleichung nach kleinsten Quadraten</li>"
|
||||
"<li>Netzausgleichung mit automatischer Punkterkennung</li>"
|
||||
"<li>Referenzpunkt-Anpassung</li>"
|
||||
"</ul>"
|
||||
"<p>Version 1.0</p>")
|
||||
"<p>Version 2.0 - Überarbeitet Januar 2026</p>")
|
||||
|
||||
|
||||
def main():
|
||||
app = QApplication(sys.argv)
|
||||
|
||||
# Stil setzen
|
||||
# Modernes Styling
|
||||
app.setStyle('Fusion')
|
||||
|
||||
# Fenster erstellen und anzeigen
|
||||
window = MainWindow()
|
||||
window.show()
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,84 @@
|
|||
5001,0.000,0.000,0.000
|
||||
5002,0.000,11.407,-0.035
|
||||
1001,22.788,13.565,-1.952
|
||||
2001,0.014,11.747,-1.361
|
||||
2002,20.214,30.965,-1.663
|
||||
2003,33.782,13.097,-2.616
|
||||
2004,20.490,-15.429,-1.438
|
||||
3001,33.838,13.220,-2.575
|
||||
3002,26.308,30.140,-1.903
|
||||
3003,26.357,-3.293,-2.115
|
||||
3004,0.048,-0.530,-3.021
|
||||
3005,-0.038,6.789,-3.414
|
||||
6001,-18.196,13.913,-2.176
|
||||
6002,-15.677,12.648,-2.286
|
||||
6003,-26.318,12.550,-2.274
|
||||
6004,-24.723,13.845,-1.559
|
||||
3006,1.182,13.131,1.845
|
||||
3007,0.867,16.745,1.841
|
||||
1002,-20.883,13.008,-1.742
|
||||
2005,-13.138,18.366,-1.730
|
||||
2006,-19.762,10.592,-1.668
|
||||
2007,-29.788,15.304,-1.497
|
||||
2008,-20.421,22.326,-1.798
|
||||
3008,-13.174,11.080,-0.280
|
||||
3009,-13.168,10.756,3.783
|
||||
3010,-14.042,4.481,1.836
|
||||
3011,-13.166,13.790,5.689
|
||||
3012,-13.164,11.191,7.558
|
||||
3013,-13.164,10.602,10.922
|
||||
2009,-13.144,12.601,-1.895
|
||||
7002,-7.487,13.193,-3.675
|
||||
7001,-13.134,19.746,-0.424
|
||||
3014,-10.454,12.968,-3.593
|
||||
3015,-5.752,14.319,-3.597
|
||||
3016,-2.168,12.842,-3.598
|
||||
3017,-16.960,9.871,-1.307
|
||||
3018,-13.143,8.257,-1.713
|
||||
3019,-21.129,4.481,-1.106
|
||||
3020,-31.198,9.253,-2.128
|
||||
3021,-29.609,16.923,-2.185
|
||||
3022,-30.145,21.896,-3.368
|
||||
3023,-20.860,18.943,-3.376
|
||||
6005,-25.633,20.878,-2.243
|
||||
1003,23.070,14.027,-1.969
|
||||
3024,1.188,12.854,5.607
|
||||
3025,1.213,3.983,5.615
|
||||
3026,-0.029,17.401,9.376
|
||||
3027,0.002,9.449,13.119
|
||||
3028,-0.915,16.758,13.728
|
||||
3029,1.096,3.374,9.955
|
||||
1004,-22.696,19.765,-1.966
|
||||
6006,-48.153,24.806,-2.092
|
||||
6007,-48.551,23.493,-1.983
|
||||
6008,-42.900,22.586,-2.096
|
||||
1005,-46.031,23.575,-1.951
|
||||
2010,-40.871,25.207,-1.886
|
||||
2011,-42.736,16.007,-1.438
|
||||
2012,-51.007,10.054,-1.947
|
||||
2013,-46.596,26.451,-1.927
|
||||
3030,-41.629,20.671,-2.199
|
||||
3031,-42.358,17.069,-2.207
|
||||
3032,-43.915,9.539,-2.218
|
||||
3033,-39.002,22.932,-3.451
|
||||
1006,22.923,13.878,-1.904
|
||||
3034,-2.259,19.010,16.788
|
||||
3035,-2.039,11.679,17.155
|
||||
3036,-2.401,0.498,16.947
|
||||
1007,-21.634,18.968,-1.815
|
||||
3037,-29.302,18.141,1.265
|
||||
3038,-30.284,13.473,-0.502
|
||||
3039,-30.223,13.843,2.923
|
||||
3040,-30.164,14.114,6.326
|
||||
3041,-30.261,13.648,9.537
|
||||
1008,-46.567,23.450,-1.900
|
||||
3042,-43.596,11.396,4.659
|
||||
3043,-41.019,23.777,4.663
|
||||
1009,-17.132,16.548,-1.900
|
||||
3044,-31.755,22.820,14.965
|
||||
3045,-33.719,12.836,14.653
|
||||
3046,-34.899,6.876,14.549
|
||||
3047,-31.140,9.336,8.091
|
||||
3048,-29.550,17.086,8.072
|
||||
3049,-28.629,21.467,8.076
|
||||
3050,-30.750,11.180,1.169
|
||||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue