""" Reference Point Adjuster Module - Referenzpunkt-Anpassung für JXL-Dateien Ermöglicht die Änderung des Referenzpunktes 5001 und Neuberechnung aller Koordinaten """ import xml.etree.ElementTree as ET from dataclasses import dataclass, field from typing import List, Dict, Optional, Tuple import math import copy import os from modules.jxl_parser import JXLParser, Point, Station @dataclass class TransformationResult: """Ergebnis der Koordinatentransformation""" original_point: str original_coords: Tuple[float, float, float] new_coords: Tuple[float, float, float] delta: Tuple[float, float, float] class ReferencePointAdjuster: """Klasse zur Anpassung des Referenzpunktes und Neuberechnung der JXL-Datei""" def __init__(self, parser: JXLParser = None): self.parser = parser self.reference_point_name = "5001" self.original_coords: Tuple[float, float, float] = (0.0, 0.0, 0.0) self.new_coords: Tuple[float, float, float] = (0.0, 0.0, 0.0) self.affected_points: List[TransformationResult] = [] self.transformation_applied = False def set_parser(self, parser: JXLParser): """Setzt den Parser für die JXL-Datei""" self.parser = parser self.find_reference_point() def find_reference_point(self) -> Optional[Point]: """Findet den Referenzpunkt 5001 in der JXL-Datei""" if not self.parser: return None # Suche nach dem Punkt mit Name "5001" for name, point in self.parser.points.items(): if name == self.reference_point_name or name == "5001": self.reference_point_name = name self.original_coords = ( point.east if point.east is not None else 0.0, point.north if point.north is not None else 0.0, point.elevation if point.elevation is not None else 0.0 ) return point # Falls 5001 nicht gefunden, suche nach dem ersten Punkt mit Coordinates/KeyedIn for name, point in self.parser.points.items(): if point.method == "Coordinates" and point.survey_method == "KeyedIn": self.reference_point_name = name self.original_coords = ( point.east if point.east is not None else 0.0, point.north if point.north is not None else 0.0, point.elevation if point.elevation is not None else 0.0 ) return point return None def get_reference_point_info(self) -> Dict: """Gibt Informationen zum Referenzpunkt zurück""" if not self.parser: return {"found": False, "message": "Kein Parser geladen"} point = self.find_reference_point() if not point: return {"found": False, "message": "Referenzpunkt nicht gefunden"} return { "found": True, "name": self.reference_point_name, "east": self.original_coords[0], "north": self.original_coords[1], "elevation": self.original_coords[2], "method": point.method, "survey_method": point.survey_method } def set_new_coordinates(self, east: float, north: float, elevation: float): """Setzt neue Koordinaten für den Referenzpunkt""" self.new_coords = (east, north, elevation) def calculate_translation(self) -> Tuple[float, float, float]: """Berechnet die Translation zwischen alten und neuen Koordinaten""" delta_x = self.new_coords[0] - self.original_coords[0] delta_y = self.new_coords[1] - self.original_coords[1] delta_z = self.new_coords[2] - self.original_coords[2] return (delta_x, delta_y, delta_z) def preview_transformation(self) -> List[TransformationResult]: """Zeigt eine Vorschau der Transformation für alle betroffenen Punkte""" if not self.parser: return [] delta = self.calculate_translation() results = [] for name, point in self.parser.points.items(): if point.east is None or point.north is None: continue original = ( point.east, point.north, point.elevation if point.elevation is not None else 0.0 ) new = ( original[0] + delta[0], original[1] + delta[1], original[2] + delta[2] ) results.append(TransformationResult( original_point=name, original_coords=original, new_coords=new, delta=delta )) self.affected_points = results return results def apply_transformation(self) -> bool: """Wendet die Transformation auf alle Punkte in der JXL-Datei an""" if not self.parser or not self.parser.raw_xml: return False delta = self.calculate_translation() root = self.parser.raw_xml.getroot() fieldbook = root.find('FieldBook') if fieldbook is None: return False modified_count = 0 # Durchsuche alle PointRecords und aktualisiere die Koordinaten for element in fieldbook: if element.tag == 'PointRecord': modified = self._update_point_coordinates(element, delta) if modified: modified_count += 1 # Aktualisiere auch die internen Parser-Daten for name, point in self.parser.points.items(): if point.east is not None: point.east += delta[0] if point.north is not None: point.north += delta[1] if point.elevation is not None: point.elevation += delta[2] self.transformation_applied = True return modified_count > 0 def _update_point_coordinates(self, element: ET.Element, delta: Tuple[float, float, float]) -> bool: """Aktualisiert die Koordinaten eines einzelnen Punktes im XML""" modified = False # Grid-Koordinaten aktualisieren grid = element.find('Grid') if grid is not None: modified |= self._update_grid_element(grid, delta) # ComputedGrid-Koordinaten aktualisieren computed_grid = element.find('ComputedGrid') if computed_grid is not None: modified |= self._update_grid_element(computed_grid, delta) return modified def _update_grid_element(self, grid: ET.Element, delta: Tuple[float, float, float]) -> bool: """Aktualisiert ein Grid oder ComputedGrid Element""" modified = False east = grid.find('East') if east is not None and east.text: try: old_value = float(east.text) new_value = old_value + delta[0] east.text = str(new_value) modified = True except ValueError: pass north = grid.find('North') if north is not None and north.text: try: old_value = float(north.text) new_value = old_value + delta[1] north.text = str(new_value) modified = True except ValueError: pass elevation = grid.find('Elevation') if elevation is not None and elevation.text: try: old_value = float(elevation.text) new_value = old_value + delta[2] elevation.text = str(new_value) modified = True except ValueError: pass return modified def export_jxl(self, output_path: str) -> bool: """Exportiert die modifizierte JXL-Datei""" if not self.parser or not self.parser.raw_xml: return False try: # XML mit Deklaration schreiben tree = self.parser.raw_xml tree.write(output_path, encoding='UTF-8', xml_declaration=True) return True except Exception as e: print(f"Fehler beim Exportieren: {e}") return False def get_summary_report(self) -> str: """Erstellt einen zusammenfassenden Bericht der Transformation""" delta = self.calculate_translation() report = [] report.append("=" * 60) report.append("REFERENZPUNKT-ANPASSUNG - ZUSAMMENFASSUNG") report.append("=" * 60) report.append("") report.append(f"Referenzpunkt: {self.reference_point_name}") report.append("") report.append("Ursprüngliche Koordinaten:") report.append(f" East (X): {self.original_coords[0]:12.4f} m") report.append(f" North (Y): {self.original_coords[1]:12.4f} m") report.append(f" Elevation (Z):{self.original_coords[2]:12.4f} m") report.append("") report.append("Neue Koordinaten:") report.append(f" East (X): {self.new_coords[0]:12.4f} m") report.append(f" North (Y): {self.new_coords[1]:12.4f} m") report.append(f" Elevation (Z):{self.new_coords[2]:12.4f} m") report.append("") report.append("Translation (Delta):") report.append(f" ΔX: {delta[0]:+12.4f} m") report.append(f" ΔY: {delta[1]:+12.4f} m") report.append(f" ΔZ: {delta[2]:+12.4f} m") report.append("") report.append("-" * 60) report.append(f"Anzahl betroffener Punkte: {len(self.affected_points)}") report.append("-" * 60) if self.affected_points: report.append("") report.append(f"{'Punkt':<10} {'Alt X':>12} {'Alt Y':>12} {'Alt Z':>10} -> {'Neu X':>12} {'Neu Y':>12} {'Neu Z':>10}") report.append("-" * 90) for result in self.affected_points[:20]: # Max 20 Punkte anzeigen report.append( f"{result.original_point:<10} " f"{result.original_coords[0]:>12.4f} " f"{result.original_coords[1]:>12.4f} " f"{result.original_coords[2]:>10.4f} -> " f"{result.new_coords[0]:>12.4f} " f"{result.new_coords[1]:>12.4f} " f"{result.new_coords[2]:>10.4f}" ) if len(self.affected_points) > 20: report.append(f"... und {len(self.affected_points) - 20} weitere Punkte") report.append("") report.append("=" * 60) if self.transformation_applied: report.append("✓ Transformation wurde angewendet") else: report.append("⚠ Transformation noch nicht angewendet") report.append("=" * 60) return "\n".join(report) def validate_input(self, east: float, north: float, elevation: float) -> Tuple[bool, str]: """Validiert die eingegebenen Koordinaten""" warnings = [] # Prüfe auf extrem große Werte (möglicher Eingabefehler) if abs(east) > 1000000: warnings.append(f"East-Koordinate ({east}) ist sehr groß - bitte überprüfen") if abs(north) > 1000000: warnings.append(f"North-Koordinate ({north}) ist sehr groß - bitte überprüfen") if abs(elevation) > 10000: warnings.append(f"Elevation ({elevation}) ist sehr groß - bitte überprüfen") # Berechne Translation delta = ( east - self.original_coords[0], north - self.original_coords[1], elevation - self.original_coords[2] ) # Warnung bei sehr großer Translation total_shift = math.sqrt(delta[0]**2 + delta[1]**2) if total_shift > 100000: warnings.append(f"Horizontale Verschiebung ({total_shift:.1f} m) ist sehr groß") if abs(delta[2]) > 1000: warnings.append(f"Höhenverschiebung ({delta[2]:.1f} m) ist sehr groß") if warnings: return (False, "\n".join(warnings)) return (True, "Koordinaten sind plausibel")