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 import gha1_num from GHA_triaxial.panou_2013_2GHA_num import gha2_num from ellipsoide import EllipsoidTriaxial import winkelumrechnungen as wu import ausgaben as aus app = Dash(__name__, suppress_callback_exceptions=True) app.title = "Geodätische Hauptaufgaben" def abplattung(a, b): return (a - b) / a def ellipsoid_figure(ell: EllipsoidTriaxial, title="Dreiachsiges Ellipsoid"): fig = go.Figure() # Darstellung rx, ry, rz = 1.05*ell.ax, 1.05*ell.ay, 1.05*ell.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), ) # 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, Y, Z = ell.para2cart(U, V) 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" )) return fig def figure_constant_lines(fig, ell: EllipsoidTriaxial, coordsystem: str = "para"): if coordsystem == "para": constants_u = wu.deg2rad(np.arange(0, 360, 15)) all_v = np.linspace(-np.pi / 2, np.pi / 2, 361) for u in constants_u: xm, ym, zm = ell.para2cart(u, all_v) fig.add_trace(go.Scatter3d( x=xm, y=ym, z=zm, mode="lines", line=dict(width=1, color="black"), showlegend=False )) all_u = np.linspace(0, 2 * np.pi, 361) constants_v = wu.deg2rad(np.arange(-75, 90, 15)) for v in constants_v: x, y, z = ell.para2cart(all_u, v) fig.add_trace(go.Scatter3d( x=x, y=y, z=z, mode="lines", line=dict(width=1, color="black"), showlegend=False )) elif coordsystem == "ell": constants_beta = wu.deg2rad(np.arange(-75, 90, 15)) all_lamb = np.linspace(0, 2 * np.pi, 361) for beta in constants_beta: xyz = ell.ell2cart(beta, all_lamb) fig.add_trace(go.Scatter3d( x=xyz[:, 0], y=xyz[:, 1], z=xyz[:, 2], mode="lines", line=dict(width=1, color="black"), showlegend=False )) all_beta = np.linspace(-np.pi / 2, np.pi / 2, 361) constants_lamb = wu.deg2rad(np.arange(0, 360, 15)) for lamb in constants_lamb: xyz = ell.ell2cart(all_beta, lamb) fig.add_trace(go.Scatter3d( x=xyz[:, 0], y=xyz[:, 1], z=xyz[:, 2], mode="lines", line=dict(width=1, color="black"), showlegend=False )) elif coordsystem == "geod": constants_phi = wu.deg2rad(np.arange(-75, 90, 15)) all_lamb = np.linspace(0, 2 * np.pi, 361) for phi in constants_phi: x, y, z = ell.geod2cart(phi, all_lamb, 0) fig.add_trace(go.Scatter3d( x=x, y=y, z=z, mode="lines", line=dict(width=1, color="black"), showlegend=False )) all_phi = np.linspace(-np.pi / 2, np.pi / 2, 361) constants_lamb = wu.deg2rad(np.arange(0, 360, 15)) for lamb in constants_lamb: x, y, z = ell.geod2cart(all_phi, lamb, 0) fig.add_trace(go.Scatter3d( x=x, y=y, z=z, mode="lines", line=dict(width=1, color="black"), showlegend=False )) return fig def figure_points(fig, points): """ :param fig: plotly.graph_objects.Figure :param points: Punktliste [(name, (x,y,z), color)] :return: plotly.graph_objects.Figure """ for name, (px, py, pz), color in points: 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 )) return fig def figure_lines(fig, lines): """ :param fig: plotly.graph_objects.Figure :param lines: Linienliste [((x1,y1,z1), (x2,y2,z2), color)] :return: plotly.graph_objects.Figure """ for (p1, p2, color) 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=color), showlegend=False )) return fig app.layout = html.Div( style={"fontFamily": "Arial", "padding": "5px", "width": "70%", "margin-left": "auto"}, 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": "BesselBiaxial", "value": "BesselBiaxial"}, {"label": "Fiction", "value": "Fiction"}, #{"label": "Ei", "value": "Ei"}, ], value="", style={"width": "300px", "marginBottom": "20px"}, ), html.Label("Halbachsen:"), dcc.Input( id="input-1", type="number", min=0, placeholder="ax...[m]", style={"marginBottom": "10px", "display": "block", "width": "300px"}, ), dcc.Input( id="input-2", type="number", min=0, placeholder="ay...[m]", style={"marginBottom": "10px", "display": "block", "width": "300px"}, ), dcc.Input( id="input-3", type="number", min=0, placeholder="b...[m]", 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={ "fontSize": "12px", "color": "gray", "textAlign": "center", }, ), ], ) @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-2", "value"), State("input-3", "value"), ) def update_output(n_clicks, ax, ay, b): if not n_clicks: return "" if n_clicks and ax is None or ay is None or b is None: return html.Span("Bitte Ellipsoid auswählen!", style={"color": "red"}) if ay >= ax or b >= ay or ax <= 0 or ay <= 0 or b <= 0: return html.Span("Eingabe inkorrekt.", style={"color": "red"}) ell = EllipsoidTriaxial(ax, ay, b) return f"ex = {round(ell.ex, 6)}, ", f"ey = {round(ell.ey, 6)}, ", f"ee = {round(ell.ee, 6)}" @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", min=-90, max=90, placeholder="β1...[°]", style={"marginBottom": "20px", "display": "block", "width": "300px"}), dcc.Input(id="input-GHA1-lamb1", type="number", min=-180, max=180, placeholder="λ1...[°]", style={"marginBottom": "20px", "display": "block", "width": "300px"}), dcc.Input(id="input-GHA1-s", type="number", min=0, placeholder="s...[m]", style={"marginBottom": "20px", "display": "block", "width": "300px"}), dcc.Input(id="input-GHA1-a", type="number", min=0, max=360, 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", min=-90, max=90, placeholder="β1...[°]", style={"marginBottom": "20px", "display": "block", "width": "300px"}), dcc.Input(id="input-GHA2-lamb1", type="number", min=-180, max=180, placeholder="λ1...[°]", style={"marginBottom": "20px", "display": "block", "width": "300px"}), dcc.Input(id="input-GHA2-beta2", type="number", min=-90, max=90, placeholder="β2...[°]", style={"marginBottom": "20px", "display": "block", "width": "300px"}), dcc.Input(id="input-GHA2-lamb2", type="number", min=-180, max=180, placeholder="λ2...[°]", style={"marginBottom": "20px", "display": "block", "width": "300px"}), dcc.Checklist( id="method-checklist-2", options=[ {"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("input-1", "value"), State("input-2", "value"), State("input-3", "value"), State("method-checklist-1", "value"), State("method-checklist-2", "value"), prevent_initial_call=True, ) def calc_and_plot(n1, n2, beta11, lamb11, s, a_deg, beta21, lamb21, beta22, lamb22, ax, ay, b, method1, method2): if not (n1 or n2): return no_update, no_update, no_update if not ax or not ay or not b: return html.Span("Bitte Ellipsoid auswählen!", style={"color": "red"}), "", go.Figure() ell = EllipsoidTriaxial(ax, ay, b) if dash.ctx.triggered_id == "button-calc-gha1": if None in (beta11, lamb11, s, a_deg): return html.Span("Bitte β₁, λ₁, s und α eingeben.", style={"color": "red"}), "", 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))) out1 = [] if "analytisch" in method1: # ana x2, y2, z2 = gha1_ana(ell, p1, alpha_rad, s_val, 70) p2_ana = (float(x2), float(y2), float(z2)) beta2, lamb2 = ell.cart2ell([x2, y2, z2]) #out1 += f"kartesisch: x₂={p2[0]:.5f} m, y₂={p2[1]:.5f} m, z₂={p2[2]:.5f} m; ellipsoidisch: {aus.gms("β₂", beta2, 5)}, {aus.gms("λ₂", lamb2, 5)}," out1.append( html.Div([ html.Strong("Analytisch: "), html.Br(), html.Span(f"kartesisch: x₂={x2:.4f} m, y₂={y2:.4f} m, z₂={z2:.4f} m"), html.Br(), html.Span(f"ellipsoidisch: {aus.gms('β₂', beta2, 4)}, {aus.gms('λ₂', lamb2, 4)}") ]) ) if "numerisch" in method1: # num #p2_num = gha1_num(ell, p1, alpha_rad, s_val, 1000) p2_num = 5 #out1 += f" {p2_num}" out1.append( html.Div([ html.Strong("Numerisch: "), html.Span(f"{p2_num}") ]) ) if "stochastisch" in method1: # stoch p2_stoch = "noch nicht implementiert.." out1.append( html.Div([ html.Strong("Stochastisch (ES): "), html.Span(f"{p2_stoch}") ]) ) if not method1: return html.Span("Bitte Berechnungsverfahren auswählen!", style={"color": "red"}), "", go.Figure() fig = ellipsoid_figure(ell, title="Erste Hauptaufgabe - analystisch") #fig = figure_constant_lines(fig, ell, "geod") fig = figure_constant_lines(fig, ell, "ell") #fig = figure_constant_lines(fig, ell, "para") fig = figure_points(fig, [("P1", p1, "black"), ("P2", p2_ana, "red")]) fig = figure_lines(fig, [(p1, p2_ana, "red")]) #out1 = f"kartesisch: x₂={p2[0]:.5f} m, y₂={p2[1]:.5f} m, z₂={p2[2]:.5f} m; ellipsoidisch: {aus.gms("β₂", beta2, 5)}, {aus.gms("λ₂", lamb2, 5)}, {p2_num}" return out1, "", fig if dash.ctx.triggered_id == "button-calc-gha2": if None in (beta21, lamb21, beta22, lamb22): return html.Span("Bitte β₁, λ₁, β₂, λ₂ eingeben.", style={"color": "red"}), "", go.Figure() p1 = tuple(ell.ell2cart(np.deg2rad(float(beta21)), np.deg2rad(float(lamb21)))) p2 = tuple(ell.ell2cart(np.deg2rad(float(beta22)), np.deg2rad(float(lamb22)))) out2 = [] if "numerisch" in method2: alpha_1, alpha_2, s12 = gha2_num( ell, np.deg2rad(float(beta21)), np.deg2rad(float(lamb21)), np.deg2rad(float(beta22)), np.deg2rad(float(lamb22)) ) out2.append( html.Div([ html.Strong("Numerisch: "), html.Span(f"{aus.gms('α₁₂', alpha_1, 4)}, {aus.gms('α₂₁', alpha_2, 4)}, s = {s12:.4f} m"), ]) ) if "stochastisch" in method2: # stoch a_stoch = "noch nicht implementiert.." out2.append( html.Div([ html.Strong("Stochastisch (ES): "), html.Span(f"{a_stoch}") ]) ) if not method2: return html.Span("Bitte Berechnungsverfahren auswählen!", style={"color": "red"}), "", go.Figure() fig = ellipsoid_figure(ell, title="Zweite Hauptaufgabe") fig = figure_constant_lines(fig, ell, "ell") fig = figure_points(fig, [("P1", p1, "black"), ("P2", p2, "red")]) fig = figure_lines(fig, [(p1, p2, "red")]) return "", out2, fig return no_update, no_update, no_update if __name__ == "__main__": app.run(debug=False)