Files
Masterprojekt_V3/Export.py
2026-02-10 06:58:04 +01:00

300 lines
14 KiB
Python

import csv
from datetime import datetime
from Koordinatentransformationen import Transformationen
import numpy as np
import os
import pandas as pd
import sympy as sp
import webbrowser
class Export:
"""Hilfsfunktionen zum Exportieren von Ergebnissen und Protokollen.
Die Klasse stellt Methoden zur Verfügung für:
- Export von Matrizen in CSV-Dateien,
- Export von Ausgleichungsergebnissen (Skalare und Matrizen) in CSV-Dateien,
- Erzeugung und Speicherung eines HTML-Protokolls inklusive automatischem Öffnen im Browser.
"""
@staticmethod
def matrix_to_csv(dateiname: str, liste_spaltenbeschriftung: list, liste_zeilenbeschriftung: list, Matrix: np.ndarray | sp.Matrix,
beschriftung_kopfzeile: object = "") -> None:
"""Schreibt eine Matrix mit Zeilen- und Spaltenbeschriftungen in eine CSV-Datei.
Die Ausgabe erfolgt mit Semikolon als Trennzeichen. Die Kopfzeile enthält optional eine
zusätzliche Beschriftung sowie Spaltenbeschriftungen.
Zudem werden Zeilenbeschriftungen durchgeführt.
:param dateiname: Pfad der zu erstellenden CSV-Datei.
:type dateiname: str
:param liste_spaltenbeschriftung: Liste der Spaltenbeschriftungen.
:type liste_spaltenbeschriftung: list
:param liste_zeilenbeschriftung: Liste der Zeilenbeschriftungen.
:type liste_zeilenbeschriftung: list
:param Matrix: Zu exportierende Matrix.
:type Matrix: np.ndarray | sp.Matrix
:param beschriftung_kopfzeile: Optionaler Eintrag in der linken oberen Zelle der Kopfzeile.
:type beschriftung_kopfzeile: object
:return: None
:rtype: None
"""
with open(dateiname, "w", newline="", encoding="utf-8") as csvfile:
writer = csv.writer(csvfile, delimiter=";")
kopfzeile = [beschriftung_kopfzeile]
for spaltenbeschriftung in liste_spaltenbeschriftung:
kopfzeile.append(str(spaltenbeschriftung))
writer.writerow(kopfzeile)
for zeilenbeschriftung, zeile in zip(liste_zeilenbeschriftung, Matrix.tolist()):
zeile_als_text = [zeilenbeschriftung]
for eintrag in zeile:
try:
# Dezimaltrenner von Punkt in Komma ändern, weil Python und Excel andere Konventionen vertreten.
eintrag_text = str(eintrag).replace(".", ",")
try:
eintrag_text = float(eintrag_text)
except:
eintrag_text = eintrag_text
except Exception:
eintrag_text = str(eintrag)
zeile_als_text.append(eintrag_text)
writer.writerow(zeile_als_text)
@staticmethod
def erzeuge_ergebnis_datenframes(pfad_datenbank: str, pfad_tif_quasigeoidundulation: str, dict_koordinaten_ausgleichungsergebnis: dict) -> tuple[pd.DataFrame, pd.DataFrame]:
"""
Erzeugt Ergebnis-DataFrames für ausgeglichene Koordinaten in ECEF (XYZ) und UTM.
Die Funktion nimmt ein Dictionary mit ausgeglichenen geozentrisch-kartesischen Koordinaten
entgegen, transformiert diese über die Transformationen-Klasse in UTM mit Normalhöhen
unter Nutzung des GCG2016
- df_x_final: Geozentrische Koordinaten X/Y/Z [m],
- df_utm_final: UTM-Koordinaten (Rechtswert/Hochwert/Höhe) [m].
:param pfad_datenbank: Pfad zur SQLite-Datenbank (für Koordinaten-/Transformationszugriffe).
:type pfad_datenbank: str
:param pfad_tif_quasigeoidundulation: Pfad zum GCG 2016 als GeoTIFF.
:type pfad_tif_quasigeoidundulution: str
:param dict_koordinaten_ausgleichungsergebnis: Dictionary der ausgeglichenen ECEF-Koordinaten je Punkt.
:type dict_koordinaten_ausgleichungsergebnis: dict
:return: Tuple aus DataFrame der ECEF-Koordinaten (XYZ) und DataFrame der UTM-Koordinaten.
:rtype: tuple[pandas.DataFrame, pandas.DataFrame]
"""
# Transformation initialisieren
trafos = Transformationen(pfad_datenbank)
dict_koordinaten_utm = trafos.ecef_to_utm(
dict_koordinaten_ausgleichungsergebnis,
pfad_tif_quasigeoidundulation
)
namen = list(dict_koordinaten_ausgleichungsergebnis.keys())
# SymPy-Matrizen zu Liste
koordinaten_liste = []
for k in namen:
matrix_werte = dict_koordinaten_ausgleichungsergebnis[k]
koordinaten_liste.append([
float(matrix_werte[0]),
float(matrix_werte[1]),
float(matrix_werte[2])
])
# DataFrame geozentrisch
df_x_final = pd.DataFrame(
koordinaten_liste,
columns=['X [m]', 'Y [m]', 'Z [m]'],
index=namen
)
df_x_final.index.name = 'Punktnummer'
# DataFrame UTM
utm_flach = {
k: np.array(v).flatten().tolist()
for k, v in dict_koordinaten_utm.items()
}
df_utm_final = pd.DataFrame.from_dict(
utm_flach,
orient='index',
columns=['Rechtswert [m]', 'Hochwert [m]', 'Höhe [m]']
)
df_utm_final.index.name = 'Punktnummer'
# Zahlenformatierung
df_x_final = df_x_final.map(lambda val: f"{val:.4f}")
df_utm_final = df_utm_final.map(lambda val: f"{val:.4f}")
return df_x_final, df_utm_final
def speichere_html_protokoll(metadaten: dict, ergebnisse: dict) -> None:
"""Erzeugt ein HTML-Protokoll der Ausgleichungsergebnisse und speichert es als Datei.
Es wird der Unterordner Protokolle angelegt, falls dieser noch nicht existiert.
Das Protokoll wird als HTML-Datei gespeichert und anschließend im Browser geöffnet.
Erwartete Eingaben in metadaten sind u. a. "projekt", "bearbeiter" und "datum".
Erwartete Eingaben in ergebnisse sind u. a. "df_globaltest", "df_redundanz",
"df_ellipsen", "df_konfidenzellipsen", "df_koordinaten_geozentrisch_kartesisch"
und "df_koordinaten_utm". Die zugehörigen Werte müssen die Methode to_html bereitstellen.
:param metadaten: Dictionary mit Metadaten zum Protokoll.
:type metadaten: dict
:param ergebnisse: Dictionary mit Ergebnisobjekten (z. B. DataFrames) zur HTML-Ausgabe.
:type ergebnisse: dict
:return: None
:rtype: None
"""
for key in ergebnisse:
wert = ergebnisse[key]
if isinstance(wert, tuple):
if len(wert) > 0 and isinstance(wert[0], pd.DataFrame):
ergebnisse[key] = wert[0]
else:
ergebnisse[key] = pd.DataFrame(list(wert))
elif isinstance(wert, dict):
ergebnisse[key] = pd.DataFrame([wert])
# Pfad für den Ordner
ordner = "Protokolle"
if not os.path.exists(ordner):
os.makedirs(ordner)
dateiname = f"{ordner}/Protokoll_{metadaten['projekt']}.html"
abs_path = os.path.abspath(dateiname)
# HTML Inhalt
html_content = f"""
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<style>
body {{ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; margin: 40px; line-height: 1.6; color: #333; }}
h1 {{ color: #2c3e50; border-bottom: 2px solid #2c3e50; }}
h3 {{ color: #2980b9; margin-top: 30px; }}
.metadata {{ background: #f9f9f9; padding: 15px; border-radius: 5px; border-left: 5px solid #2980b9; }}
/* Styling für das Inhaltsverzeichnis */
.toc {{ background: #fff; border: 1px solid #ddd; padding: 15px; border-radius: 5px; display: inline-block; min-width: 300px; }}
.toc ul {{ list-style: none; padding-left: 0; }}
.toc li {{ margin: 5px 0; }}
.toc a {{ text-decoration: none; color: #2980b9; font-weight: bold; }}
.toc a:hover {{ text-decoration: underline; }}
/* Styling für Bilder */
.plot-container {{ margin-top: 20px; text-align: center; background: #fff; padding: 10px; border: 1px solid #ddd; }}
.plot-img {{ width: 100%; max-width: 1000px; height: auto; border: 1px solid #eee; }}
.plot-caption {{ font-style: italic; color: #666; margin-top: 5px; }}
table {{ border-collapse: collapse; width: 100%; margin-top: 10px; }}
th, td {{ text-align: left; padding: 8px; border-bottom: 1px solid #ddd; }}
tr:nth-child(even) {{ background-color: #f2f2f2; }}
th {{ background-color: #2980b9; color: white; }}
.footer {{ margin-top: 50px; font-size: 0.8em; color: #7f8c8d; text-align: center; }}
</style>
<title>Netzprotokoll - {metadaten['projekt']}</title>
</head>
<body>
<h1>Netzprotokoll: {metadaten['projekt']}</h1>
<div class="metadata">
<p><strong>Bearbeiter:</strong> {metadaten['bearbeiter']}</p>
<p><strong>Datum:</strong> {metadaten['datum']}</p>
<p><strong>Ausgleichungsmodus:</strong> Methode der kleinsten Quadrate (L2-Norm)</p>
<p><strong>Koordinatenreferenzsystem:</strong> {metadaten['koordinatenreferenzsystem']}</p>
<p><strong>Datumsfestlegung:</strong> {metadaten['datumsfestlegung']}</p>
<p><strong>Dimension:</strong> 3D</p>
</div>
<div class="toc">
<strong>Inhaltsverzeichnis</strong>
<ul>
<li><a href="#globaltest">1. Globaltest</a></li>
<li><a href="#redundanz">2. Redundanzanteile</a></li>
<li><a href="#lokaltest">3. Lokaltest</a></li>
<li><a href="#vks">4. Varianzkomponentenschätzung</a></li>
<li><a href="#aeussere_zuver">5. Äussere Zuverlässigkeit</a></li>
<li><a href="#Helmertscher_Punktfehler">6. Standardabweichungen und Helmert'scher Punktfehler</a></li>
<li><a href="#Standardellipsoid">7. Standardellipsoid</a></li>
<li><a href="#Konfidenzellipoid">8. Konfidenzellipoid</a></li>
<li><a href="#Konfidenzellipse_ENU">9. Konfidenzellipsen im ENU-System</a></li>
<li><a href="#netzplot_gesamt">10. Netzplot im ENU-System mit Konfidenzellipsen - Gesamtes Netz</a></li>
<li><a href="#netzplot_zoom">11. Netzplot im ENU-System mit Konfidenzellipsen - Ausschnitt</a></li>
<li><a href="#koordinaten_xyz">12. Koordinaten Geozentrisch</a></li>
<li><a href="#koordinaten_utm">13. Koordinaten UTM</a></li>
</ul>
</div>
<h3 id="globaltest">1. Globaltest</h3>
{ergebnisse['df_globaltest'].to_html(index=False)}
<h3 id="redundanz">2. Redundanzanteile</h3>
{ergebnisse['df_redundanz'].to_html(index=False)}
<h3 id="lokaltest">3. Lokaltest</h3>
{ergebnisse['df_lokaltest'].to_html(index=False)}
<h3 id="vks">4. Varianzkomponentenschätzung</h3>
{ergebnisse['df_vks'].to_html(index=False)}
<h3 id="aeussere_zuver">5. Äussere Zuverlässigkeit</h3>
{ergebnisse['df_aeussere_zuver'].to_html(index=False)}
<h3 id="Helmertscher_Punktfehler">6. Standardabweichungen und Helmert'scher Punktfehler</h3>
{ergebnisse['df_punktfehler'].to_html(index=False)}
<h3 id="Standardellipsoid">7. Standardellipsoid</h3>
{ergebnisse['df_standardellipsoid'].to_html(index=False)}
<h3 id="Konfidenzellipoid">8. Konfidenzellipoid</h3>
{ergebnisse['df_konfidenzellipsoid'].to_html(index=False)}
<h3 id="Konfidenzellipse_ENU">9. Konfidenzellipsen im ENU-System</h3>
{ergebnisse['df_konfidenzellipsen_enu'].to_html(index=False)}
<h3 id="netzplot_gesamt">10. Plots: Konfidenzellipsen im ENU-System (Gesamtes Netz)</h3>
<div class="plot-container">
<h4>Gesamtnetz</h4>
<img src="../Netzqualitaet/netzplot_ellipsen_volle_ausdehnung.png" class="plot-img" alt="Gesamtes Netz">
<p class="plot-caption">Netzplot im ENU-System mit Konfidenzellipsen - Gesamtes Netz</p>
</div>
<h3 id="netzplot_zoom">11. Plots: Konfidenzellipsen im ENU-System (Auschnitt) </h3>
<div class="plot-container">
<h4>Detailansicht (Zoom)</h4>
<img src="../Netzqualitaet/netzplot_ellipsen_zoom_ansicht.png" class="plot-img" alt="2Netzplot im ENU-System mit Konfidenzellipsen - Gesamtes Netz">
<p class="plot-caption">Netzplot im ENU-System mit Konfidenzellipsen - Ausschnitt</p>
</div>
<h3 id="koordinaten_xyz">12. Koordinaten Geozentrisch Kartesisch</h3>
{ergebnisse['df_koordinaten_geozentrisch_kartesisch'].to_html(index=True)}
<h3 id="koordinaten_utm">13. Koordinaten UTM</h3>
{ergebnisse['df_koordinaten_utm'].to_html(index=True)}
<div class="footer">
Erstellt automatisch am {datetime.now().strftime('%d.%m.%Y um %H:%M:%S')}
</div>
</body>
</html>
"""
# Datei schreiben
with open(dateiname, "w", encoding="utf-8") as f:
f.write(html_content)
print(f"✅ Protokoll wurde gespeichert unter: {abs_path}")
# Datei automatisch im Standard-Browser öffnen
webbrowser.open(f"file://{abs_path}")