Files
Masterprojekt_V3/Netzqualität_Genauigkeit.py
2026-01-19 14:48:24 +01:00

259 lines
9.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import numpy as np
import plotly.graph_objects as go
from scipy.stats import f as f_dist
import pandas as pd
class Genauigkeitsmaße:
@staticmethod
def berechne_s0apost(v: np.ndarray, P: np.ndarray, r: int) -> float:
vTPv_matrix = v.T @ P @ v
vTPv = float(vTPv_matrix.item())
s0apost = np.sqrt(vTPv / r)
return float(s0apost)
def helmert_punktfehler(Qxx, s0_apost, unbekannten_liste, dim=3):
diagQ = np.diag(Qxx)
daten = []
n_punkte = len(unbekannten_liste) // 3
for i in range(n_punkte):
sym_x = str(unbekannten_liste[3 * i]) # z.B. "X10009"
punkt = sym_x[1:] # -> "10009"
qx = diagQ[3 * i]
qy = diagQ[3 * i + 1]
qz = diagQ[3 * i + 2]
sx = s0_apost * np.sqrt(qx)
sy = s0_apost * np.sqrt(qy)
sz = s0_apost * np.sqrt(qz)
if dim == 2:
sP = s0_apost * np.sqrt(qx + qy)
else:
sP = s0_apost * np.sqrt(qx + qy + qz)
daten.append([
punkt,
float(sx),
float(sy),
float(sz),
float(sP)
])
helmert_punktfehler = pd.DataFrame(daten, columns=["Punkt", "σx", "σy", "σz", f"σP_{dim}D"])
return helmert_punktfehler
def standardellipse(Qxx, s0_apost, unbekannten_liste, dim_labels=3):
Qxx = np.asarray(Qxx, float)
data = []
n_punkte = len(unbekannten_liste) // dim_labels
for i in range(n_punkte):
sym_x = str(unbekannten_liste[dim_labels * i]) # z.B. "X10009"
punkt = sym_x[1:] # -> "10009"
ix = dim_labels * i
iy = dim_labels * i + 1
# 2x2-Kofaktorblock
Qxx_ = Qxx[ix, ix]
Qyy_ = Qxx[iy, iy]
Qyx_ = Qxx[iy, ix]
# Standardabweichungen der Koordinatenkomponenten
sx = s0_apost * np.sqrt(Qxx_)
sy = s0_apost * np.sqrt(Qyy_)
sxy = (s0_apost ** 2) * Qyx_
# k und Eigenwerte (Q_dmax, Q_dmin)
k = np.sqrt((Qxx_ - Qyy_) ** 2 + 4 * (Qyx_ ** 2))
Q_dmax = 0.5 * (Qxx_ + Qyy_ + k)
Q_dmin = 0.5 * (Qxx_ + Qyy_ - k)
# Halbachsen (Standardabweichungen entlang Hauptachsen)
s_max = s0_apost * np.sqrt(Q_dmax)
s_min = s0_apost * np.sqrt(Q_dmin)
# Richtungswinkel theta (Hauptachse) in rad:
theta_rad = 0.5 * np.arctan2(2 * Qyx_, (Qxx_ - Qyy_))
# in gon
theta_gon = theta_rad * (200 / np.pi)
if theta_gon < 0:
theta_gon += 200.0
data.append([
punkt,
float(sx), float(sy), float(sxy),
float(s_max), float(s_min),
float(theta_gon)
])
standardellipse = pd.DataFrame(data, columns=["Punkt", "σx", "σy", "σxy", "s_max", "s_min", "θ [gon]"])
return standardellipse
def konfidenzellipse(Qxx, s0_apost, unbekannten_liste, R, alpha=0.05):
Qxx = np.asarray(Qxx, float)
data = []
n_punkte = len(unbekannten_liste) // 3 # X,Y,Z je Punkt angenommen
k = float(np.sqrt(2.0 * f_dist.ppf(1.0 - alpha, 2, R)))
for i in range(n_punkte):
punkt = str(unbekannten_liste[3 * i])[1:] # "X10009" -> "10009"
ix = 3 * i
iy = 3 * i + 1
Qxx_ = Qxx[ix, ix]
Qyy_ = Qxx[iy, iy]
Qxy_ = Qxx[iy, ix] # = Qyx
# k für Eigenwerte
kk = np.sqrt((Qxx_ - Qyy_) ** 2 + 4 * (Qxy_ ** 2))
Q_dmax = 0.5 * (Qxx_ + Qyy_ + kk)
Q_dmin = 0.5 * (Qxx_ + Qyy_ - kk)
# Standard-Halbachsen (1-sigma)
s_max = s0_apost * np.sqrt(Q_dmax)
s_min = s0_apost * np.sqrt(Q_dmin)
# Orientierung (Hauptachse) in gon
theta_rad = 0.5 * np.arctan2(2 * Qxy_, (Qxx_ - Qyy_))
theta_gon = theta_rad * (200 / np.pi)
if theta_gon < 0:
theta_gon += 200.0
# Konfidenz-Halbachsen
a_K = k * s_max
b_K = k * s_min
data.append([punkt, float(a_K), float(b_K), float(theta_gon)])
konfidenzellipsen = pd.DataFrame(data, columns=["Punkt", "a_K", "b_K", "θ [gon]"])
return konfidenzellipsen
def plot_netz_komplett_final(x_vektor, unbekannten_labels, beobachtungs_labels, Qxx, sigma0_apost,
k_faktor=2.447, v_faktor=1000):
"""
Optimierter Plot für Jupyter Notebook:
- k_faktor: Statistischer Sicherheitsfaktor (2.447 entspricht 95% für 2D)
- v_faktor: Optische Überhöhung der Ellipsen (z.B. 1000 = mm werden als m dargestellt)
"""
x_vektor = np.asarray(x_vektor, float).reshape(-1)
Qxx = np.asarray(Qxx, float)
# 1. Datenaufbereitung
coords = {}
punkt_ids = sorted(set(str(l)[1:] for l in unbekannten_labels if str(l).startswith(('X', 'Y', 'Z'))))
pts_data = []
for pid in punkt_ids:
try:
ix = next(i for i, s in enumerate(unbekannten_labels) if str(s) == f"X{pid}")
iy = next(i for i, s in enumerate(unbekannten_labels) if str(s) == f"Y{pid}")
x, y = float(x_vektor[ix]), float(x_vektor[iy])
coords[pid] = (x, y)
# Kovarianzmatrix extrahieren und mit s0^2 skalieren
q_idx = [ix, iy]
Q_sub = Qxx[np.ix_(q_idx, q_idx)] * (sigma0_apost ** 2)
pts_data.append({'id': pid, 'x': x, 'y': y, 'Q': Q_sub})
except:
continue
if len(pts_data) == 0:
raise ValueError(
"Keine Netzpunkte extrahiert. Prüfe: x_vektor Form (u,) und Qxx Form (u,u) sowie Labels 'X<id>'/'Y<id>'.")
fig = go.Figure()
# 2. Beobachtungen (Gruppiert)
beob_typen = {
'GNSS-Basislinien': {'pattern': 'gnss', 'color': 'rgba(255, 100, 0, 0.4)'},
'Nivellement': {'pattern': 'niv', 'color': 'rgba(0, 200, 100, 0.4)'},
'Tachymeter': {'pattern': '', 'color': 'rgba(100, 100, 100, 0.3)'}
}
for typ, info in beob_typen.items():
x_l, y_l = [], []
for bl in beobachtungs_labels:
bl_str = str(bl).lower()
if (info['pattern'] in bl_str and info['pattern'] != '') or (
info['pattern'] == '' and 'gnss' not in bl_str and 'niv' not in bl_str):
pts = [pid for pid in coords if f"_{pid}" in str(bl) or str(bl).startswith(f"{pid}_")]
if len(pts) >= 2:
x_l.extend([coords[pts[0]][0], coords[pts[1]][0], None])
y_l.extend([coords[pts[0]][1], coords[pts[1]][1], None])
if x_l:
fig.add_trace(go.Scatter(x=x_l, y=y_l, mode='lines', name=typ, line=dict(color=info['color'], width=1)))
# 3. Konfidenzellipsen mit v_faktor
for pt in pts_data:
vals, vecs = np.linalg.eigh(pt['Q'])
order = vals.argsort()[::-1]
vals, vecs = vals[order], vecs[:, order]
theta = np.degrees(np.arctan2(vecs[1, 0], vecs[0, 0]))
# Skalierung: k_faktor (Statistik) * v_faktor (Optik)
a = k_faktor * np.sqrt(vals[0]) * v_faktor
b = k_faktor * np.sqrt(vals[1]) * v_faktor
t = np.linspace(0, 2 * np.pi, 40)
e_x = a * np.cos(t)
e_y = b * np.sin(t)
R = np.array([[np.cos(np.radians(theta)), -np.sin(np.radians(theta))],
[np.sin(np.radians(theta)), np.cos(np.radians(theta))]])
rot = np.dot(R, np.array([e_x, e_y]))
fig.add_trace(go.Scatter(
x=rot[0, :] + pt['x'], y=rot[1, :] + pt['y'],
mode='lines', line=dict(color='red', width=1.5),
name=f"Ellipsen (Vergrößert {v_faktor}x)",
legendgroup="Ellipsen",
showlegend=(pt == pts_data[0]), # Nur einmal in der Legende zeigen
hoverinfo='skip'
))
# 4. Punkte
df_pts = pd.DataFrame(pts_data)
fig.add_trace(go.Scatter(
x=df_pts['x'], y=df_pts['y'], mode='markers+text',
text=df_pts['id'], textposition="top center",
marker=dict(size=8, color='black'), name="Netzpunkte"
))
# 5. Layout & Notebook-Größe
fig.update_layout(
title=f"Netzausgleichung: Ellipsen {v_faktor}-fach vergrößert (k={k_faktor})",
xaxis=dict(title="X [m]", tickformat="f", separatethousands=True, scaleanchor="y", scaleratio=1, showgrid=True,
gridcolor='lightgrey'),
yaxis=dict(title="Y [m]", tickformat="f", separatethousands=True, showgrid=True, gridcolor='lightgrey'),
width=1100, # Breite angepasst
height=900, # Höhe deutlich vergrößert für Jupiter Notebook
plot_bgcolor='white',
legend=dict(yanchor="top", y=0.99, xanchor="left", x=0.01, bgcolor="rgba(255,255,255,0.8)")
)
# Info-Annotation als Ersatz für einen physischen Maßstabstab
fig.add_annotation(
text=f"<b>Maßstab Ellipsen:</b><br>Dargestellte Größe = Wahre Ellipse × {v_faktor}",
align='left', showarrow=False, xref='paper', yref='paper', x=0.02, y=0.05,
bgcolor="white", bordercolor="black", borderwidth=1)
fig.show(config={'scrollZoom': True})