324 lines
12 KiB
Python
324 lines
12 KiB
Python
"""
|
|
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")
|