diff --git a/dashborad.py b/dashborad.py new file mode 100644 index 0000000..cb72040 --- /dev/null +++ b/dashborad.py @@ -0,0 +1,384 @@ +from dash import Dash, html, dcc, Input, Output, State, no_update +import dash +import plotly.graph_objects as go +import numpy as np + +from GHA_triaxial.panou import gha1_ana +from GHA_triaxial.panou_2013_2GHA_num import gha2_num +from ellipsoide import EllipsoidTriaxial +import winkelumrechnungen as wu + + +app = Dash(__name__, suppress_callback_exceptions=True) +app.title = "Geodätische Hauptaufgaben" + + +def abplattung(a, b): + return (a - b) / a + +def ellipsoid_figure(ax, ay, b, pts=None, lines=None, title="Dreiachsiges Ellipsoid"): + u = np.linspace(-np.pi/2, np.pi/2, 80) + v = np.linspace(-np.pi, np.pi, 160) + U, V = np.meshgrid(u, v) + X = ax * np.cos(U) * np.cos(V) + Y = ay * np.cos(U) * np.sin(V) + Z = b * np.sin(U) + + fig = go.Figure() + fig.add_trace(go.Surface( + x=X, y=Y, z=Z, showscale=False, opacity=0.7, + surfacecolor=np.zeros_like(X), + colorscale=[[0, "rgb(200,220,255)"], [1, "rgb(200,220,255)"]], + name="Ellipsoid" + )) + + meridians_deg = np.arange(0, 360, 15) + lat_line = np.linspace(-np.pi/2, np.pi/2, 240) + for lon_deg in meridians_deg: + lam = np.deg2rad(lon_deg) + phi = lat_line + xm = ax * np.cos(phi) * np.cos(lam) + ym = ay * np.cos(phi) * np.sin(lam) + zm = b * np.sin(phi) + fig.add_trace(go.Scatter3d( + x=xm, y=ym, z=zm, mode="lines", + line=dict(width=1, color="black"), + showlegend=False + )) + parallels_deg = np.arange(-75, 90, 15) + lon_line = np.linspace(0, 2*np.pi, 360) + for lat_deg in parallels_deg: + phi = np.deg2rad(lat_deg) + lam = lon_line + xp = ax * np.cos(phi) * np.cos(lam) + yp = ay * np.cos(phi) * np.sin(lam) + zp = b * np.sin(phi) * np.ones_like(lam) + fig.add_trace(go.Scatter3d( + x=xp, y=yp, z=zp, mode="lines", + line=dict(width=1, color="black"), + showlegend=False + )) + + if pts: + for name, (px, py, pz), color in pts: + fig.add_trace(go.Scatter3d( + x=[px], y=[py], z=[pz], + mode="markers+text", + marker=dict(size=6, color=color), + text=[name], textposition="top center", + name=name, showlegend=False + )) + + if lines: + for (p1, p2) in lines: + xline = [p1[0], p2[0]] + yline = [p1[1], p2[1]] + zline = [p1[2], p2[2]] + fig.add_trace(go.Scatter3d( + x=xline, y=yline, z=zline, + mode="lines", + line=dict(width=4, color="red"), + showlegend=False + )) + + rx, ry, rz = 1.05*ax, 1.05*ay, 1.05*b + fig.update_layout( + title=title, + scene=dict( + xaxis=dict(range=[-rx, rx], title="X [m]"), + yaxis=dict(range=[-ry, ry], title="Y [m]"), + zaxis=dict(range=[-rz, rz], title="Z [m]"), + aspectmode="data" + ), + margin=dict(l=0, r=0, t=40, b=0), + ) + return fig + + +app.layout = html.Div( + style={"fontFamily": "Arial", "margin": "40px"}, + children=[ + html.H1("Geodätische Hauptaufgaben"), + html.H2("für dreiachsige Ellipsoide"), + + html.Label("Ellipsoid wählen:"), + dcc.Dropdown( + id="my-dropdown", + options=[ + {"label": "BursaFialova1993", "value": "BursaFialova1993"}, + {"label": "BursaSima1980", "value": "BursaSima1980"}, + {"label": "BursaSima1980round", "value": "BursaSima1980round"}, + {"label": "Eitschberger1978", "value": "Eitschberger1978"}, + {"label": "Bursa1972", "value": "Bursa1972"}, + {"label": "Bursa1970", "value": "Bursa1970"}, + {"label": "Bessel-biaxial", "value": "Bessel-biaxial"}, + #{"label": "Ei", "value": "Ei"}, + ], + value="", + style={"width": "300px", "marginBottom": "20px"}, + ), + + html.Label("Halbachsen:"), + dcc.Input( + id="input-1", + type="number", + placeholder="ax...", + style={"marginBottom": "10px", "display": "block", "width": "300px"}, + ), + dcc.Input( + id="input-2", + type="number", + placeholder="ay...", + style={"marginBottom": "10px", "display": "block", "width": "300px"}, + ), + dcc.Input( + id="input-3", + type="number", + placeholder="b...", + style={"marginBottom": "20px", "display": "block", "width": "300px"}, + ), + + html.Button( + "Ellipsoid Berechnen", + id="calc-ell", + n_clicks=0, + style={"marginRight": "10px", "marginBottom": "20px"}, + ), + + html.Div(id="output-area", style={"marginBottom": "20px"}), + + dcc.Tabs( + id="tabs-GHA", + value="tab-GHA1", + style={"marginRight": "10px", "marginBottom": "20px", "width": "50%"}, + children=[ + dcc.Tab(label="Erste Hauptaufgabe", value="tab-GHA1"), + dcc.Tab(label="Zweite Hauptaufgabe", value="tab-GHA2"), + ], + ), + + html.Div( + id="tabs-GHA-out", + style={"marginRight": "10px", "marginBottom": "20px", "width": "50%"}, + ), + + html.Div(id="output-gha1", style={"marginBottom": "20px"}), + + html.Div(id="output-gha2", style={"marginBottom": "20px"}), + + dcc.Graph( + id="ellipsoid-plot", + style={"height": "500px", "width": "700px"}, + ), + + html.P( + "© 2025", + style={ + "margin": 0, + "fontSize": "12px", + "color": "gray", + "textAlign": "center", + "padding": "5px 0", + }, + ), + ], +) + + +@app.callback( + Output("input-1", "value"), + Output("input-2", "value"), + Output("input-3", "value"), + Input("my-dropdown", "value"), +) +def fill_inputs_from_dropdown(selected_ell): + if not selected_ell: + return None, None, None + + + ell = EllipsoidTriaxial.init_name(selected_ell) + ax = ell.ax + ay = ell.ay + b = ell.b + return ax, ay, b + + +@app.callback( + Output("output-area", "children"), + Input("calc-ell", "n_clicks"), + State("input-1", "value"), + State("input-3", "value"), +) +def update_output(n_clicks, ax, b): + if not n_clicks or ax is None or b is None: + return "" + f = abplattung(ax, b) + return f"Abplattung f = {f:.10e}" + + +@app.callback( + Output("tabs-GHA-out", "children"), + Input("tabs-GHA", "value"), +) +def render_content(tab): + show1 = {"display": "block"} if tab == "tab-GHA1" else {"display": "none"} + show2 = {"display": "block"} if tab == "tab-GHA2" else {"display": "none"} + + pane_gha1 = html.Div( + [ + dcc.Input(id="input-GHA1-beta1", type="number", placeholder="β1...[°]", + style={"marginBottom": "20px", "display": "block", "width": "300px"}), + dcc.Input(id="input-GHA1-lamb1", type="number", placeholder="λ1...[°]", + style={"marginBottom": "20px", "display": "block", "width": "300px"}), + dcc.Input(id="input-GHA1-s", type="number", placeholder="s...[m]", + style={"marginBottom": "20px", "display": "block", "width": "300px"}), + dcc.Input(id="input-GHA1-a", type="number", placeholder="α...[°]", + style={"marginBottom": "20px", "display": "block", "width": "300px"}), + + dcc.Checklist( + id="method-checklist-1", + options=[ + {"label": "Analytisch", "value": "analytisch"}, + {"label": "Numerisch", "value": "numerisch"}, + {"label": "Stochastisch (ES)", "value": "stochastisch"}, + ], + value=[], + style={"marginBottom": "20px"}, + ), + html.Div( + [ + html.Button( + "Berechnen", + id="button-calc-gha1", + n_clicks=0, + style={"marginRight": "10px"}, + ), + ], + style={"marginBottom": "20px"}, + ), + ], + id="pane-gha1", + style=show1, + ) + + pane_gha2 = html.Div( + [ + dcc.Input(id="input-GHA2-beta1", type="number", placeholder="β1...[°]", + style={"marginBottom": "20px", "display": "block", "width": "300px"}), + dcc.Input(id="input-GHA2-lamb1", type="number", placeholder="λ1...[°]", + style={"marginBottom": "20px", "display": "block", "width": "300px"}), + dcc.Input(id="input-GHA2-beta2", type="number", placeholder="β2...[°]", + style={"marginBottom": "20px", "display": "block", "width": "300px"}), + dcc.Input(id="input-GHA2-lamb2", type="number", placeholder="λ2...[°]", + style={"marginBottom": "20px", "display": "block", "width": "300px"}), + + dcc.Checklist( + id="method-checklist-2", + options=[ + {"label": "Analytisch", "value": "analytisch"}, + {"label": "Numerisch", "value": "numerisch"}, + {"label": "Stochastisch (ES)", "value": "stochastisch"}, + ], + value=[], + style={"marginBottom": "20px"}, + ), + html.Div( + [ + html.Button( + "Berechnen", + id="button-calc-gha2", + n_clicks=0, + style={"marginRight": "10px"}, + ), + ], + style={"marginBottom": "20px"}, + ), + ], + id="pane-gha2", + style=show2, + ) + + return html.Div([pane_gha1, pane_gha2]) + + +@app.callback( + Output("output-gha1", "children"), + Output("output-gha2", "children"), + Output("ellipsoid-plot", "figure"), + Input("button-calc-gha1", "n_clicks"), + Input("button-calc-gha2", "n_clicks"), + State("input-GHA1-beta1", "value"), + State("input-GHA1-lamb1", "value"), + State("input-GHA1-s", "value"), + State("input-GHA1-a", "value"), + State("input-GHA2-beta1", "value"), + State("input-GHA2-lamb1", "value"), + State("input-GHA2-beta2", "value"), + State("input-GHA2-lamb2", "value"), + State("my-dropdown", "value"), + prevent_initial_call=True, +) +def calc_and_plot(n1, n2, + beta11, lamb11, s, a_deg, + beta1, lamb1, beta2, lamb2, + ell_name): + + if not (n1 or n2): + return no_update, no_update, no_update + + if not ell_name: + return "Bitte Ellipsoid wählen.", "", go.Figure() + + ell = EllipsoidTriaxial.init_name(ell_name) + + if dash.ctx.triggered_id == "button-calc-gha1": + if None in (beta11, lamb11, s, a_deg): + return "Bitte β₁, λ₁, s und α eingeben.", "", go.Figure() + + beta_rad = wu.deg2rad(float(beta11)) + lamb_rad = wu.deg2rad(float(lamb11)) + alpha_rad = wu.deg2rad(float(a_deg)) + s_val = float(s) + + p1 = tuple(map(float, ell.ell2cart(beta_rad, lamb_rad))) + + x2, y2, z2 = gha1_ana(ell, p1, alpha_rad, s_val, 70) + p2 = (float(x2), float(y2), float(z2)) + + fig = ellipsoid_figure( + ell.ax, ell.ay, ell.b, + pts=[("P1", p1, "black"), ("P2", p2, "red")], + lines=[(p1, p2)], + title="Erste Hauptaufgabe - analystisch" + ) + + out1 = f"x₂={p2[0]:.3f}, y₂={p2[1]:.3f}, z₂={p2[2]:.3f}" + return out1, "", fig + + if dash.ctx.triggered_id == "button-calc-gha2": + if None in (beta1, lamb1, beta2, lamb2): + return "", "Bitte β₁, λ₁, β₂, λ₂ eingeben.", go.Figure() + + alpha_1, alpha_2, s12 = gha2_num( + ell, + np.deg2rad(float(beta1)), np.deg2rad(float(lamb1)), + np.deg2rad(float(beta2)), np.deg2rad(float(lamb2)) + ) + + p1 = tuple(ell.ell2cart(np.deg2rad(float(beta1)), np.deg2rad(float(lamb1)))) + p2 = tuple(ell.ell2cart(np.deg2rad(float(beta2)), np.deg2rad(float(lamb2)))) + + fig = ellipsoid_figure( + ell.ax, ell.ay, ell.b, + pts=[("P1", p1, "black"), ("P2", p2, "red")], + lines=[(p1, p2)], + title=f"Zweite Hauptaufgabe - numerisch" + ) + out2 = f"a₁₂={np.rad2deg(alpha_1):.6f}°, a₂₁={np.rad2deg(alpha_2):.6f}°, s={s12:.4f} m" + return "", out2, fig + + return no_update, no_update, no_update + + +if __name__ == "__main__": + app.run(debug=False)