Push
This commit is contained in:
@@ -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})
|
||||
Reference in New Issue
Block a user