This commit is contained in:
2026-01-19 14:48:24 +01:00
parent d2b6f604f0
commit 7a170f5ead
11 changed files with 56477 additions and 43084 deletions

View File

@@ -146,50 +146,114 @@ class Genauigkeitsmaße:
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)
"""
@staticmethod
def plot_ellipsen(punkt_coords: dict, ellipsen_parameter: list, scale: float = 1000):
fig = go.Figure()
x_vektor = np.asarray(x_vektor, float).reshape(-1)
Qxx = np.asarray(Qxx, float)
# Titel dynamisch anpassen
prob = ellipsen_parameter[0].get('prob', 0)
titel = "Standard-Fehlerellipsen" if prob < 0.4 else f"{prob * 100:.0f}% Konfidenzellipsen"
# 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 p in ellipsen_parameter:
name = p['name']
x0, y0 = punkt_coords[name][0], punkt_coords[name][1]
a, b, theta = p['a'], p['b'], p['theta']
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)
t = np.linspace(0, 2 * np.pi, 100)
xs, ys = a * scale * np.cos(t), b * scale * np.sin(t)
# 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
x_plot = x0 + xs * np.cos(theta) - ys * np.sin(theta)
y_plot = y0 + xs * np.sin(theta) + ys * np.cos(theta)
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>'.")
# Punkt
fig.add_trace(go.Scatter(
x=[x0], y=[y0], mode='markers+text',
name=f"Punkt {name}", text=[name], textposition="top right",
marker=dict(size=8, color='black')
))
fig = go.Figure()
# Ellipse
fig.add_trace(go.Scatter(
x=x_plot, y=y_plot, mode='lines',
name=f"Ellipse {name}",
line=dict(color='blue' if prob > 0.4 else 'red', width=2,
dash='dash' if prob > 0.4 else 'solid'),
fill='toself',
fillcolor='rgba(0, 0, 255, 0.1)' if prob > 0.4 else 'rgba(255, 0, 0, 0.1)',
hoverinfo='text',
text=(f"Punkt: {name}<br>a: {a * 1000:.2f} mm<br>"
f"b: {b * 1000:.2f} mm<br>Theta: {np.degrees(theta):.2f}°")
))
# 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)'}
}
fig.update_layout(
title=titel,
xaxis_title="Rechtswert (E) [m]", yaxis_title="Hochwert (N) [m]",
yaxis=dict(scaleanchor="x", scaleratio=1),
template="plotly_white", showlegend=True
)
return fig
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})