anpassung ellipsoid statt ellipse, anpassung lokaltest wegen falschem beta
This commit is contained in:
@@ -56,9 +56,9 @@ class Genauigkeitsmaße:
|
||||
|
||||
Aus der Kofaktor-Matrix der Unbekannten Qxx werden die Kofaktoren punktweise ausgelesen. Durch Multiplikation
|
||||
mit der a-posteriori Standardabweichung der Gewichtseinheit s₀ werden die Standardabweichungen je Koordinate
|
||||
(σx, σy, σz) sowie der Helmertsche Punktfehler σP berechnet:
|
||||
(σx, σy, σz) sowie der Helmert'sche Punktfehler σP berechnet:
|
||||
|
||||
Die Punktzuordnung erfolgt über die Symbolnamen der Unbekanntenliste (z.B. X1, Y1, Z1).
|
||||
Die Punktzuordnung erfolgt über die Symbolnamen der Unbekanntenliste.
|
||||
Die Dimension (2D/3D) wird interaktiv per Eingabe abgefragt. Zusätzlich werden die
|
||||
Ergebnisse als Tabelle ausgegeben und in eine Excel-Datei exportiert.
|
||||
|
||||
@@ -116,30 +116,35 @@ class Genauigkeitsmaße:
|
||||
|
||||
|
||||
@staticmethod
|
||||
def standardellipse(Qxx, s0_apost, unbekannten_liste):
|
||||
def standardellipsoid(Qxx, s0_apost, unbekannten_liste):
|
||||
"""
|
||||
Berechnet die Standardellipse (Helmertsche Fehlerellipse) für die Punkte aus Qxx und s₀ a posteriori.
|
||||
Berechnet Achsenlängen des Fehlerellipsoids (a, b, c) sowie den 2D-Richtungswinkel θ aus Qxx und s₀ a posteriori.
|
||||
|
||||
Für jeden Punkt werden aus der Kofaktor-Matrix der Unbekannten Qxx die
|
||||
Kofaktoren von X und Y ausgelesen (qxx, qyy, qyx).
|
||||
Daraus werden Standardabweichungen σx, σy sowie die Kovarianz σxy bestimmt und
|
||||
anschließend die Parameter der Standardellipse berechnet:
|
||||
Für jeden Punkt wird aus der Kofaktor-Matrix der Unbekannten Qxx die 3×3-Submatrix
|
||||
der Koordinaten (X, Y, Z) gebildet. Daraus werden zunächst die Standardabweichungen
|
||||
σx, σy, σz bestimmt. Anschließend werden über eine Eigenwertzerlegung der Submatrix
|
||||
die Halbachsen des (punktbezogenen) Fehlerellipsoids berechnet:
|
||||
|
||||
- Große und kleine Halbachse der Standardellipse,
|
||||
- Richtungswinkel θ der großen Halbachse in gon.
|
||||
- Halbachse a aus dem größten Eigenwert,
|
||||
- Halbachse b aus dem mittleren Eigenwert,
|
||||
- Halbachse c aus dem kleinsten Eigenwert.
|
||||
|
||||
Die Punktzuordnung erfolgt über die Symbolnamen der Unbekanntenliste (z.B. X1, Y1).
|
||||
Zusätzlich werden die Ergebnisse tabellarisch ausgegeben und in eine Excel-Datei expoertiert.
|
||||
Zusätzlich wird zur zweidimensionalen Darstellung ein Richtungswinkel θ in der X/Y-Ebene
|
||||
aus den Kovarianzanteilen (qxx, qyy, qyx) berechnet (Winkel in gon).
|
||||
|
||||
Die Punktzuordnung erfolgt über die Symbolnamen der Unbekanntenliste.
|
||||
Die Ergebnisse werden tabellarisch ausgegeben und in eine Excel-Datei exportiert.
|
||||
|
||||
:param Qxx: Kofaktor-Matrix der Unbekannten.
|
||||
:type Qxx: numpy.ndarray
|
||||
:param s0_apost: a-posteriori Standardabweichung der Gewichtseinheit s₀.
|
||||
:param s0_apost: A-posteriori Standardabweichung der Gewichtseinheit s₀.
|
||||
:type s0_apost: float
|
||||
:param unbekannten_liste: Liste der Unbekannten.
|
||||
:type unbekannten_liste: list
|
||||
:return: Tabelle mit Standardabweichungen und Parametern der Standardellipse je Punkt.
|
||||
:return: Tabelle mit Standardabweichungen (σx, σy, σz), Ellipsoid-Halbachsen (a, b, c) und Richtungswinkel θ je Punkt.
|
||||
:rtype: pandas.DataFrame
|
||||
"""
|
||||
|
||||
Qxx = np.asarray(Qxx, float)
|
||||
daten = []
|
||||
namen_str = [str(sym) for sym in unbekannten_liste]
|
||||
@@ -153,65 +158,55 @@ class Genauigkeitsmaße:
|
||||
try:
|
||||
idx_x = next(i for i, n in enumerate(namen_str) if n.upper() == f"X{pid}".upper())
|
||||
idx_y = next(i for i, n in enumerate(namen_str) if n.upper() == f"Y{pid}".upper())
|
||||
idx_z = next(i for i, n in enumerate(namen_str) if n.upper() == f"Z{pid}".upper())
|
||||
|
||||
qxx = Qxx[idx_x, idx_x]
|
||||
qyy = Qxx[idx_y, idx_y]
|
||||
qyx = Qxx[idx_y, idx_x]
|
||||
indices = [idx_x, idx_y, idx_z]
|
||||
Q_sub = Qxx[np.ix_(indices, indices)]
|
||||
|
||||
# Standardabweichungen
|
||||
sx = s0_apost * np.sqrt(qxx)
|
||||
sy = s0_apost * np.sqrt(qyy)
|
||||
sxy = (s0_apost ** 2) * qyx
|
||||
sx = s0_apost * np.sqrt(Q_sub[0, 0])
|
||||
sy = s0_apost * np.sqrt(Q_sub[1, 1])
|
||||
sz = s0_apost * np.sqrt(Q_sub[2, 2])
|
||||
|
||||
k = np.sqrt((qxx - qyy) ** 2 + 4 * (qyx ** 2))
|
||||
|
||||
# Q_dmax/min = 0.5 * (Qyy + Qxx +/- k)
|
||||
q_dmax = 0.5 * (qyy + qxx + k)
|
||||
q_dmin = 0.5 * (qyy + qxx - k)
|
||||
# Eigenwertzerlegung
|
||||
eigenwerte, eigenvektoren = np.linalg.eigh(Q_sub)
|
||||
eigenwerte = np.sort(eigenwerte)[::-1]
|
||||
|
||||
# Halbachsen
|
||||
s_max = s0_apost * np.sqrt(q_dmax)
|
||||
s_min = s0_apost * np.sqrt(q_dmin)
|
||||
s_a = s0_apost * np.sqrt(eigenwerte[0]) # Große Halbachse a
|
||||
s_b = s0_apost * np.sqrt(eigenwerte[1]) # Mittlere Halbachse b
|
||||
s_c = s0_apost * np.sqrt(eigenwerte[2]) # Kleine Halbachse c
|
||||
|
||||
# Richtungswinkel theta in gon:
|
||||
zaehler = 2 * qyx
|
||||
nenner = qxx - qyy
|
||||
t_grund = 0.5 * np.arctan(abs(zaehler) / abs(nenner)) * (200 / np.pi)
|
||||
|
||||
# Quadrantenabfrage
|
||||
if nenner > 0 and qyx > 0: # Qxx - Qyy > 0 und Qyx > 0
|
||||
t_gon = t_grund # 0 - 50 gon
|
||||
elif nenner < 0 and qyx > 0: # Qxx - Qyy < 0 und Qyx > 0
|
||||
t_gon = 100 - t_grund # 50 - 100 gon
|
||||
elif nenner < 0 and qyx < 0: # Qxx - Qyy < 0 und Qyx < 0
|
||||
t_gon = 100 + t_grund # 100 - 150 gon
|
||||
elif nenner > 0 and qyx < 0: # Qxx - Qyy > 0 und Qyx < 0
|
||||
t_gon = 200 - t_grund # 150 - 200 gon
|
||||
else:
|
||||
t_gon = 0.0
|
||||
qyx = Q_sub[1, 0]
|
||||
qxx = Q_sub[0, 0]
|
||||
qyy = Q_sub[1, 1]
|
||||
t_gon = 0.5 * np.arctan2(2 * qyx, qxx - qyy) * (200 / np.pi)
|
||||
if t_gon < 0:
|
||||
t_gon += 200
|
||||
|
||||
daten.append([
|
||||
pid,
|
||||
float(sx), float(sy), float(sxy),
|
||||
float(s_max), float(s_min),
|
||||
float(t_gon)
|
||||
pid, float(sx), float(sy), float(sz),
|
||||
float(s_a), float(s_b), float(s_c), float(t_gon)
|
||||
])
|
||||
except:
|
||||
continue
|
||||
standardellipse = pd.DataFrame(daten, columns=["Punkt", "σx [m]", "σy [m]", "σxy [m]", "Große Halbachse [m]", "Kleine Halbachse [m]", "θ [gon]"])
|
||||
standardellipse["σx [m]"] = standardellipse["σx [m]"].astype(float).round(4)
|
||||
standardellipse["σy [m]"] = standardellipse["σy [m]"].astype(float).round(4)
|
||||
standardellipse["Große Halbachse [m]"] = standardellipse["Große Halbachse [m]"].astype(float).round(4)
|
||||
standardellipse["Kleine Halbachse [m]"] = standardellipse["Kleine Halbachse [m]"].astype(float).round(4)
|
||||
standardellipse["θ [gon]"] = standardellipse["θ [gon]"].astype(float).round(3)
|
||||
display(HTML(standardellipse.to_html(index=False)))
|
||||
standardellipse.to_excel(r"Netzqualitaet\Standardellipse.xlsx", index=False)
|
||||
return standardellipse
|
||||
standardellipsoid = pd.DataFrame(daten, columns=["Punkt", "σx [m]", "σy [m]", "σz [m]", "Halbachse a [m]", "Halbachse b [m]", "Halbachse c [m]", "θ [gon]"])
|
||||
standardellipsoid["σx [m]"] = standardellipsoid["σx [m]"].astype(float).round(4)
|
||||
standardellipsoid["σy [m]"] = standardellipsoid["σy [m]"].astype(float).round(4)
|
||||
standardellipsoid["σz [m]"] = standardellipsoid["σz [m]"].astype(float).round(4)
|
||||
standardellipsoid["Halbachse a [m]"] = standardellipsoid["Halbachse a [m]"].astype(float).round(4)
|
||||
standardellipsoid["Halbachse b [m]"] = standardellipsoid["Halbachse b [m]"].astype(float).round(4)
|
||||
standardellipsoid["Halbachse c [m]"] = standardellipsoid["Halbachse c [m]"].astype(float).round(4)
|
||||
standardellipsoid["θ [gon]"] = standardellipsoid["θ [gon]"].astype(float).round(3)
|
||||
display(HTML(standardellipsoid.to_html(index=False)))
|
||||
standardellipsoid.to_excel(r"Netzqualitaet\Standardellipsoid.xlsx", index=False)
|
||||
return standardellipsoid
|
||||
|
||||
|
||||
|
||||
@staticmethod
|
||||
def konfidenzellipse(Qxx, s0_apost, unbekannten_liste, R, ausgabe_erfolgt):
|
||||
def konfidenzellipsoid(Qxx, s0_apost, unbekannten_liste, R, ausgabe_erfolgt):
|
||||
"""
|
||||
Berechnet die Konfidenzellipse für Punkte aus Qxx und einem Konfidenzniveau.
|
||||
|
||||
@@ -238,7 +233,7 @@ class Genauigkeitsmaße:
|
||||
:rtype: tuple[pandas.DataFrame, float]
|
||||
:raises ValueError: Wenn alpha nicht in (0, 1) liegt oder nicht in float umgewandelt werden kann.
|
||||
"""
|
||||
alpha_input = input("Konfidenzniveau wählen (z.B. 0.05 für 95%, 0.01 für 99%) [Standard=0.05]: ")
|
||||
alpha_input = input("Irrtumswahrscheinlichkeit α wählen (z.B. 0.05, 0.01) [Standard=0.05]: ")
|
||||
if alpha_input.strip() == "":
|
||||
alpha = 0.05
|
||||
else:
|
||||
@@ -250,72 +245,53 @@ class Genauigkeitsmaße:
|
||||
|
||||
punkt_ids = [n[1:] for n in namen_str if n.upper().startswith('X')]
|
||||
|
||||
# Faktor für Konfidenzellipse (F-Verteilung)
|
||||
kk = float(np.sqrt(2.0 * f.ppf(1.0 - alpha, 2, R)))
|
||||
# Faktor fürs Konfidenzellipoid (F-Verteilung)
|
||||
k = float(np.sqrt(3.0 * f.ppf(1.0 - alpha, 3, R)))
|
||||
|
||||
for pid in punkt_ids:
|
||||
try:
|
||||
idx_x = next(i for i, n in enumerate(namen_str) if n.upper() == f"X{pid}".upper())
|
||||
idx_y = next(i for i, n in enumerate(namen_str) if n.upper() == f"Y{pid}".upper())
|
||||
idx_z = next(i for i, n in enumerate(namen_str) if n.upper() == f"Z{pid}".upper())
|
||||
|
||||
qxx = Qxx[idx_x, idx_x]
|
||||
qyy = Qxx[idx_y, idx_y]
|
||||
qyx = Qxx[idx_y, idx_x]
|
||||
indices = [idx_x, idx_y, idx_z]
|
||||
Q_sub = Qxx[np.ix_(indices, indices)]
|
||||
|
||||
# Standardabweichungen
|
||||
sx = s0_apost * np.sqrt(qxx)
|
||||
sy = s0_apost * np.sqrt(qyy)
|
||||
sxy = (s0_apost ** 2) * qyx
|
||||
sx = s0_apost * np.sqrt(Q_sub[0, 0])
|
||||
sy = s0_apost * np.sqrt(Q_sub[1, 1])
|
||||
sz = s0_apost * np.sqrt(Q_sub[2, 2])
|
||||
|
||||
k = np.sqrt((qxx - qyy) ** 2 + 4 * (qyx ** 2))
|
||||
# Eigenwertzerlegung
|
||||
eigenwerte, eigenvektoren = np.linalg.eigh(Q_sub)
|
||||
eigenwerte = np.sort(eigenwerte)[::-1]
|
||||
|
||||
# Q_dmax/min = 0.5 * (Qyy + Qxx +/- k)
|
||||
q_dmax = 0.5 * (qyy + qxx + k)
|
||||
q_dmin = 0.5 * (qyy + qxx - k)
|
||||
|
||||
# Halbachsen der Standardellipse
|
||||
s_max = s0_apost * np.sqrt(q_dmax)
|
||||
s_min = s0_apost * np.sqrt(q_dmin)
|
||||
|
||||
# Halbachsen der Konfidenzellipse
|
||||
A_K = kk * s_max
|
||||
B_K = kk * s_min
|
||||
# Halbachsen des Konfidenzellipoid
|
||||
A_K = k * s0_apost * np.sqrt(eigenwerte[0])
|
||||
B_K = k * s0_apost * np.sqrt(eigenwerte[1])
|
||||
C_K = k * s0_apost * np.sqrt(eigenwerte[2])
|
||||
|
||||
# Richtungswinkel theta in gon:
|
||||
zaehler = 2 * qyx
|
||||
nenner = qxx - qyy
|
||||
t_grund = 0.5 * np.arctan(abs(zaehler) / abs(nenner)) * (200 / np.pi)
|
||||
|
||||
# Quadrantenabfrage
|
||||
if nenner > 0 and qyx > 0:
|
||||
t_gon = t_grund # 0 - 50 gon
|
||||
elif nenner < 0 and qyx > 0:
|
||||
t_gon = 100 - t_grund # 50 - 100 gon
|
||||
elif nenner < 0 and qyx < 0:
|
||||
t_gon = 100 + t_grund # 100 - 150 gon
|
||||
elif nenner > 0 and qyx < 0:
|
||||
t_gon = 200 - t_grund # 150 - 200 gon
|
||||
else:
|
||||
t_gon = 0.0
|
||||
qyx = Q_sub[1, 0]
|
||||
qxx = Q_sub[0, 0]
|
||||
qyy = Q_sub[1, 1]
|
||||
t_gon = 0.5 * np.arctan2(2 * qyx, qxx - qyy) * (200 / np.pi)
|
||||
if t_gon < 0:
|
||||
t_gon += 200
|
||||
|
||||
daten.append([
|
||||
pid,
|
||||
float(sx), float(sy), float(sxy),
|
||||
float(A_K), float(B_K),
|
||||
float(t_gon)
|
||||
pid, float(sx), float(sy), float(sz),
|
||||
float(A_K), float(B_K), float(C_K), float(t_gon)
|
||||
])
|
||||
|
||||
except:
|
||||
continue
|
||||
|
||||
konfidenzellipse = pd.DataFrame(daten, columns=["Punkt", "σx [m]", "σy [m]", "σxy [m]", "Große Halbachse [m]", "Kleine Halbachse [m]", "θ [gon]"])
|
||||
konfidenzellipse["Große Halbachse [m]"] = konfidenzellipse["Große Halbachse [m]"].round(4)
|
||||
konfidenzellipse["Kleine Halbachse [m]"] = konfidenzellipse["Kleine Halbachse [m]"].round(4)
|
||||
konfidenzellipse["θ [gon]"] = konfidenzellipse["θ [gon]"].round(3)
|
||||
konfidenzellipoid = pd.DataFrame(daten, columns=["Punkt", "σx [m]", "σy [m]", "σz [m]", "Halbachse a_k [m]", "Halbachse b_k [m]", "Halbachse c_k [m]", "θ [gon]"])
|
||||
if ausgabe_erfolgt == False:
|
||||
display(HTML(konfidenzellipse.to_html(index=False)))
|
||||
konfidenzellipse.to_excel(r"Netzqualitaet\Konfidenzellipse.xlsx", index=False)
|
||||
return konfidenzellipse, alpha
|
||||
display(HTML(konfidenzellipoid.to_html(index=False)))
|
||||
konfidenzellipoid.to_excel(r"Netzqualitaet\Konfidenzellipoid.xlsx", index=False)
|
||||
return konfidenzellipoid, alpha
|
||||
|
||||
|
||||
@staticmethod
|
||||
@@ -326,7 +302,7 @@ class Genauigkeitsmaße:
|
||||
Die Funktion transformiert zunächst die Kofaktor-Matrix der Unbekannten Qxx
|
||||
in ein East-North-Up-System (ENU) bezogen auf den Schwerpunkt der verwendeten
|
||||
Punkte (B0, L0). Anschließend wird auf Basis der transformierten Matrix die
|
||||
Konfidenzellipse über die Funktion "konfidenzellipse" bestimmt.
|
||||
Konfidenzellipse über die Funktion "konfidenzellipsoid" bestimmt.
|
||||
Zum Schluss werden Spaltennamen an die ENU-Notation angepasst, Werte gerundet,
|
||||
tabellarisch ausgegeben und als Excel-Datei exportiert.
|
||||
|
||||
@@ -362,8 +338,8 @@ class Genauigkeitsmaße:
|
||||
print(
|
||||
f"ENU-Referenz (Schwerpunkt): B0={Einheitenumrechnung.Einheitenumrechnung.rad_to_gon_Decimal(B0):.8f} rad, L0={Einheitenumrechnung.Einheitenumrechnung.rad_to_gon_Decimal(L0):.8f} rad")
|
||||
|
||||
# 2) Konfidenzellipse im ENU-System
|
||||
Konfidenzellipse_ENU, alpha = Genauigkeitsmaße.konfidenzellipse(
|
||||
# 2) Konfidenzellipoid im ENU-System
|
||||
Konfidenzellipse_ENU, alpha = Genauigkeitsmaße.konfidenzellipsoid(
|
||||
Qxx_enu,
|
||||
s0apost,
|
||||
liste_unbekannte,
|
||||
@@ -375,15 +351,17 @@ class Genauigkeitsmaße:
|
||||
Konfidenzellipse_ENU = Konfidenzellipse_ENU.rename(columns={
|
||||
"σx [m]": "σE [m]",
|
||||
"σy [m]": "σN [m]",
|
||||
"σxy [m]": "σEN [m]",
|
||||
"σz [m]": "σU [m]",
|
||||
"θ [gon]": "θ_EN [gon]"
|
||||
})
|
||||
|
||||
# 4) Runden und Anzeigen
|
||||
Konfidenzellipse_ENU["σE [m]"] = Konfidenzellipse_ENU["σE [m]"].round(4)
|
||||
Konfidenzellipse_ENU["σN [m]"] = Konfidenzellipse_ENU["σN [m]"].round(4)
|
||||
Konfidenzellipse_ENU["Große Halbachse [m]"] = Konfidenzellipse_ENU["Große Halbachse [m]"].round(4)
|
||||
Konfidenzellipse_ENU["Kleine Halbachse [m]"] = Konfidenzellipse_ENU["Kleine Halbachse [m]"].round(4)
|
||||
Konfidenzellipse_ENU["σE [m]"] = Konfidenzellipse_ENU["σE [m]"].round(6)
|
||||
Konfidenzellipse_ENU["σN [m]"] = Konfidenzellipse_ENU["σN [m]"].round(6)
|
||||
Konfidenzellipse_ENU["σU [m]"] = Konfidenzellipse_ENU["σU [m]"].round(6)
|
||||
Konfidenzellipse_ENU["Halbachse a_k [m]"] = Konfidenzellipse_ENU["Halbachse a_k [m]"].round(4)
|
||||
Konfidenzellipse_ENU["Halbachse b_k [m]"] = Konfidenzellipse_ENU["Halbachse b_k [m]"].round(4)
|
||||
Konfidenzellipse_ENU["Halbachse c_k [m]"] = Konfidenzellipse_ENU["Halbachse c_k [m]"].round(4)
|
||||
Konfidenzellipse_ENU["θ_EN [gon]"] = Konfidenzellipse_ENU["θ_EN [gon]"].round(4)
|
||||
|
||||
display(HTML(Konfidenzellipse_ENU.to_html(index=False)))
|
||||
@@ -416,26 +394,28 @@ class Plot:
|
||||
unbekannten_labels,
|
||||
beobachtungs_labels,
|
||||
df_konf_ellipsen_enu,
|
||||
skalierung=1000,
|
||||
skalierung,
|
||||
n_ellipse_pts=60,
|
||||
titel="Netzplot im ENU-System mit Konfidenzellipsen"
|
||||
):
|
||||
|
||||
"""
|
||||
Erstellt einen Netzplot im ENU-System inklusive Konfidenzellipsen, Netzpunkten und Beobachtungslinien.
|
||||
|
||||
Die Funktion visualisiert das geodätische Netz im East-North-Up-System (ENU)
|
||||
mit Plotly. Dabei werden:
|
||||
Die Funktion visualisiert das geodätische Netz im East-North-Up-System (ENU) mit Plotly.
|
||||
Dabei werden:
|
||||
|
||||
- Beobachtungen als Verbindungslinien zwischen Punkten dargestellt, deren Ansicht aus- und eingeschaltet werden kann,
|
||||
- Beobachtungen als Verbindungslinien zwischen Punkten dargestellt (als separate Traces/Legenden-Einträge),
|
||||
- Konfidenzellipsen je Punkt (Halbachsen und Richtungswinkel),
|
||||
- Netzpunkte mit Punkt-ID und Koordinaten im Hover-Text angezeigt.
|
||||
|
||||
Die Ellipsen werden zur besseren Sichtbarkeit mit einem Faktor "skalierung" vergrößert. Dieser kann angepasst werden.
|
||||
Die Ellipsen werden zur besseren Sichtbarkeit mit dem Faktor "skalierung" vergrößert.
|
||||
Der Richtungswinkel wird in gon erwartet und intern nach Radiant umgerechnet.
|
||||
Zusätzlich wird eine PNG-Grafik über "plot.write_image(...)" exportiert.
|
||||
|
||||
:param Koord_ENU: Dictionary der Punktkoordinaten im ENU-System.
|
||||
:type Koord_ENU: dict
|
||||
:param unbekannten_labels: Liste der Unbekannten zur Ableitung der Punkt-IDs (z.B. X1, Y1, Z1).
|
||||
:param unbekannten_labels: Liste der Unbekannten zur Ableitung der Punkt-IDs.
|
||||
:type unbekannten_labels: list
|
||||
:param beobachtungs_labels: Liste der Beobachtungen zur Ableitung von Verbindungslinien.
|
||||
:type beobachtungs_labels: list
|
||||
@@ -447,12 +427,12 @@ class Plot:
|
||||
:type n_ellipse_pts: int
|
||||
:param titel: Titel des Plots.
|
||||
:type titel: str
|
||||
:return: None
|
||||
:rtype: None
|
||||
:return: Plotly-Figure-Objekt mit Netz, Beobachtungen und Ellipsen.
|
||||
:rtype: plotly.graph_objects.Figure
|
||||
:raises ValueError: Wenn weder "θ_EN [gon]" noch "θ [gon]" im DataFrame vorhanden ist.
|
||||
"""
|
||||
|
||||
names = [str(s).strip() for s in unbekannten_labels]
|
||||
namen = [str(s).strip() for s in unbekannten_labels]
|
||||
|
||||
if "θ_EN [gon]" in df_konf_ellipsen_enu.columns:
|
||||
theta_col = "θ_EN [gon]"
|
||||
@@ -461,9 +441,9 @@ class Plot:
|
||||
else:
|
||||
raise ValueError("Spalte 'θ_EN [gon]' oder 'θ [gon]' fehlt im DataFrame.")
|
||||
|
||||
punkt_ids = sorted({nm[1:] for nm in names if nm and nm[0].upper() in ("X", "Y", "Z")})
|
||||
punkt_ids = sorted({nm[1:] for nm in namen if nm and nm[0].upper() in ("X", "Y", "Z")})
|
||||
|
||||
fig = go.Figure()
|
||||
plot = go.Figure()
|
||||
|
||||
# 1) Darstellungen der Beobachtungen
|
||||
beob_typen = {
|
||||
@@ -472,28 +452,28 @@ class Plot:
|
||||
}
|
||||
|
||||
for typ, info in beob_typen.items():
|
||||
x_l, y_l = [], []
|
||||
for bl in beobachtungs_labels:
|
||||
bl_str = str(bl).lower()
|
||||
x_linie, y_linie = [], []
|
||||
for beob in beobachtungs_labels:
|
||||
bl_str = str(beob).lower()
|
||||
is_typ = ((info['pattern'] in bl_str and info['pattern'] != '') or
|
||||
(info['pattern'] == '' and 'gnss' not in bl_str and 'niv' not in bl_str))
|
||||
if not is_typ:
|
||||
continue
|
||||
|
||||
bl_raw = str(bl)
|
||||
pts = []
|
||||
beob_text = str(beob)
|
||||
punkte = []
|
||||
for pid in punkt_ids:
|
||||
if (f"_{pid}" in bl_raw) or bl_raw.startswith(f"{pid}_"):
|
||||
if (f"_{pid}" in beob_text) or beob_text.startswith(f"{pid}_"):
|
||||
if pid in Koord_ENU:
|
||||
pts.append(pid)
|
||||
punkte.append(pid)
|
||||
|
||||
if len(pts) >= 2:
|
||||
p1, p2 = pts[0], pts[1]
|
||||
x_l.extend([Koord_ENU[p1][0], Koord_ENU[p2][0], None]) # E
|
||||
y_l.extend([Koord_ENU[p1][1], Koord_ENU[p2][1], None]) # N
|
||||
if len(punkte) >= 2:
|
||||
punkt1, punkt2 = punkte[0], punkte[1]
|
||||
x_linie.extend([Koord_ENU[punkt1][0], Koord_ENU[punkt2][0], None]) # E
|
||||
y_linie.extend([Koord_ENU[punkt1][1], Koord_ENU[punkt2][1], None]) # N
|
||||
|
||||
if x_l:
|
||||
fig.add_trace(go.Scatter(x=x_l, y=y_l, mode='lines', name=typ,
|
||||
if x_linie:
|
||||
plot.add_trace(go.Scatter(x=x_linie, y=y_linie, mode='lines', name=typ,
|
||||
line=dict(color=info['color'], width=1)))
|
||||
|
||||
# 2) Darstellung der Konfidenzellipsen
|
||||
@@ -504,8 +484,8 @@ class Plot:
|
||||
if pid not in Koord_ENU:
|
||||
continue
|
||||
|
||||
a = float(row["Große Halbachse [m]"]) * skalierung
|
||||
b = float(row["Kleine Halbachse [m]"]) * skalierung
|
||||
a = float(row["Halbachse a_k [m]"]) * skalierung
|
||||
b = float(row["Halbachse b_k [m]"]) * skalierung
|
||||
theta = float(row[theta_col]) * np.pi / 200.0 # gon->rad
|
||||
|
||||
ex = a * np.cos(t)
|
||||
@@ -517,7 +497,7 @@ class Plot:
|
||||
|
||||
E0, N0, _ = Koord_ENU[pid]
|
||||
|
||||
fig.add_trace(go.Scatter(
|
||||
plot.add_trace(go.Scatter(
|
||||
x=E0 + xr, y=N0 + yr,
|
||||
mode="lines",
|
||||
line=dict(color="red", width=1.5),
|
||||
@@ -539,7 +519,7 @@ class Plot:
|
||||
texts.append(pid)
|
||||
hovers.append(f"Punkt {pid}<br>E={E:.4f} m<br>N={N:.4f} m<br>U={U:.4f} m")
|
||||
|
||||
fig.add_trace(go.Scatter(
|
||||
plot.add_trace(go.Scatter(
|
||||
x=xs, y=ys, mode="markers+text",
|
||||
text=texts, textposition="top center",
|
||||
marker=dict(size=8, color="black"),
|
||||
@@ -547,7 +527,7 @@ class Plot:
|
||||
hovertext=hovers, hoverinfo="text"
|
||||
))
|
||||
|
||||
fig.update_layout(
|
||||
plot.update_layout(
|
||||
title=f"{titel} (Ellipsen ×{skalierung})",
|
||||
xaxis=dict(title="E [m]", scaleanchor="y", scaleratio=1, showgrid=True, gridcolor="lightgrey"),
|
||||
yaxis=dict(title="N [m]", showgrid=True, gridcolor="lightgrey"),
|
||||
@@ -556,16 +536,35 @@ class Plot:
|
||||
plot_bgcolor="white"
|
||||
)
|
||||
|
||||
fig.add_annotation(
|
||||
plot.add_annotation(
|
||||
text=f"<b>Maßstab Ellipsen:</b><br>Dargestellte Größe = Konfidenzellipse × {skalierung}",
|
||||
align='left', showarrow=False, xref='paper', yref='paper', x=0.02, y=0.05,
|
||||
bgcolor="white", bordercolor="black", borderwidth=1
|
||||
)
|
||||
fig.write_image(r"Netzqualitaet\netzplot_ellipsen_volle_ausdehnung.png")
|
||||
return fig
|
||||
plot.write_image(r"Netzqualitaet\netzplot_ellipsen_volle_ausdehnung.png")
|
||||
return plot
|
||||
|
||||
|
||||
def plot_speichere_aktuelle_ansicht(plot, dateiname=r"Netzqualitaet\netzplot_ellipsen_zoom_ansicht.png"):
|
||||
"""
|
||||
Speichert die aktuell dargestellte Plot-Ansicht als Bilddatei.
|
||||
|
||||
Die Funktion übernimmt ein vorhandenes Plotly-Figure-Objekt und exportiert die momentan im Layout
|
||||
eingestellte Ansicht (inklusive Zoom- oder Achsenbereich) als PNG-Datei. Dazu wird das aktuelle
|
||||
Layout übernommen und in ein neues Figure-Objekt kopiert, welches anschließend mit
|
||||
"write_image" gespeichert wird.
|
||||
|
||||
Zusätzlich werden die aktuellen Achsenbereiche (E/N) in der Konsole ausgegeben, um den gespeicherten
|
||||
Ausschnitt zu dokumentieren.
|
||||
|
||||
:param plot: Plotly-Figure-Objekt mit dem darzustellenden Netzplot.
|
||||
:type plot: plotly.graph_objects.Figure
|
||||
:param dateiname: Dateipfad für die Ausgabedatei (Standard: PNG im Ordner Netzqualitaet).
|
||||
:type dateiname: str
|
||||
:return: None
|
||||
:rtype: None
|
||||
:raises OSError: Wenn die Bilddatei nicht geschrieben werden kann (z.B. fehlender Pfad oder fehlendes Kaleido).
|
||||
"""
|
||||
|
||||
aktuelles_layout = plot.layout
|
||||
export_plot = go.Figure(plot.data, layout=aktuelles_layout)
|
||||
|
||||
Reference in New Issue
Block a user