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="dropdown-ellipsoid",
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-ax",
type="number",
min=0,
placeholder="ax...[m]",
style={"marginBottom": "10px", "display": "block", "width": "300px"},
),
dcc.Input(
id="input-ay",
type="number",
min=0,
placeholder="ay...[m]",
style={"marginBottom": "10px", "display": "block", "width": "300px"},
),
dcc.Input(
id="input-b",
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-ax", "value"),
Output("input-ay", "value"),
Output("input-b", "value"),
Input("dropdown-ellipsoid", "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-ax", "value"),
State("input-ay", "value"),
State("input-b", "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-ax", "value"),
State("input-ay", "value"),
State("input-b", "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, 10000)
beta2_num, lamb2_num = ell.cart2ell(p2_num)
out1.append(
html.Div([
html.Strong("Numerisch: "),
html.Br(),
html.Span(f"kartesisch: x₂={p2_num[0]:.4f} m, y₂={p2_num[1]:.4f} m, z₂={p2_num[2]:.4f} m"),
html.Br(),
html.Span(f"ellipsoidisch: {aus.gms('β₂', beta2_num, 4)}, {aus.gms('λ₂', lamb2_num, 4)}")
])
)
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")])
#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")])
return "", out2, fig
return no_update, no_update, no_update
if __name__ == "__main__":
app.run(debug=False)