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)