diff --git a/GHA_triaxial/gha1_approx.py b/GHA_triaxial/gha1_approx.py index 042f54a..80b65e9 100644 --- a/GHA_triaxial/gha1_approx.py +++ b/GHA_triaxial/gha1_approx.py @@ -47,7 +47,7 @@ def gha1_approx(ell: EllipsoidTriaxial, p0: np.ndarray, alpha0: float, s: float, pass if all_points: - return points[-1], alphas[-1], np.array(points) + return points[-1], alphas[-1], np.array(points), np.array(alphas) else: return points[-1], alphas[-1] @@ -82,7 +82,7 @@ if __name__ == '__main__': P0 = ell.para2cart(0.2, 0.3) alpha0 = wu.deg2rad(35) s = 13000000 - P1_app, alpha1_app, points = gha1_approx(ell, P0, alpha0, s, ds=10000, all_points=True) + P1_app, alpha1_app, points, alphas = gha1_approx(ell, P0, alpha0, s, ds=10000, all_points=True) P1_ana, alpha1_ana = gha1_ana(ell, P0, alpha0, s, maxM=60, maxPartCircum=16) show_points(points, P0, P1_ana) print(np.linalg.norm(P1_app - P1_ana)) diff --git a/dashboard.py b/dashboard.py index 71adcf4..e820286 100644 --- a/dashboard.py +++ b/dashboard.py @@ -1,6 +1,10 @@ from dash import Dash, html, dcc, Input, Output, State, no_update import plotly.graph_objects as go import numpy as np +import dash_bootstrap_components as dbc + +import webbrowser +from threading import Timer from ellipsoide import EllipsoidTriaxial import winkelumrechnungen as wu @@ -15,20 +19,75 @@ from GHA_triaxial.gha2_ES import gha2_ES from GHA_triaxial.gha2_approx import gha2_approx -app = Dash(__name__, suppress_callback_exceptions=True) +app = Dash(__name__, suppress_callback_exceptions=True, external_stylesheets=[dbc.themes.BOOTSTRAP]) app.title = "Geodätische Hauptaufgaben" def inputfeld(left_text, input_id, right_text="", width=200, min=None, max=None): return html.Div( - [ + children=[ html.Span(f"{left_text} =", style={"minWidth": 36, "textAlign": "right", "marginRight": 5}), - dcc.Input(id=input_id, type="number", placeholder=f"{left_text}...[{right_text}]", min=min, max=max, style={"width": width, "display": "block",}), + dcc.Input( + id=input_id, + type="number", + placeholder=f"{left_text}...[{right_text}]", + min=min, + max=max, + style={"width": width, "display": "block"}, + persistence=True, + persistence_type="memory", # oder "session"/"local" + ), html.Span(right_text, style={"marginLeft": 5}) if right_text else html.Span() ], - style={"display": "flex", "alignItems": "center", "marginBottom": "10px"} + style={"display": "flex", "alignItems": "center", "marginBottom": "10px"}, ) +def method_row(label, cb_id, input_id, placeholder=""): + if placeholder == "": + return html.Div( + [ + dcc.Checklist( + id=cb_id, + options=[{"label": "", "value": "on"}], + value=[], + style={"margin": "0"}, + persistence=True, + persistence_type="memory", + ), + html.Span(label, style={"marginLeft": "6px", "minWidth": "110px"}), + ], + style={"display": "flex", "alignItems": "center", "gap": "6px", + "marginLeft": "10px", "marginBottom": "6px"}, + ) + else: + return html.Div( + [ + dcc.Checklist( + id=cb_id, + options=[{"label": "", "value": "on"}], + value=[], + style={"margin": "0"}, + persistence=True, + persistence_type="memory", + ), + html.Span(label, style={"marginLeft": "6px", "minWidth": "110px"}), + + dcc.Input( + id=input_id, + type="number", + placeholder=placeholder, + style={"width": "80px", "marginLeft": "10px"}, + disabled=True, + persistence=True, + persistence_type="memory", + ), + ], + style={"display": "flex", "alignItems": "center", "gap": "6px", + "marginLeft": "10px", "marginBottom": "6px"}, + ) + + + def ellipsoid_figure(ell: EllipsoidTriaxial, title="Dreiachsiges Ellipsoid"): fig = go.Figure() @@ -171,6 +230,107 @@ def figure_lines(fig, line, color): )) return fig +pane_gha1 = html.Div( + [ + inputfeld("β₀", "input-GHA1-beta0", "°", min=-90, max=90), + inputfeld("λ₀", "input-GHA1-lamb0", "°", min=-180, max=180), + inputfeld("s", "input-GHA1-s", "m", min=0), + inputfeld("α₀", "input-GHA1-a", "°", min=0, max=360), + + dcc.Checklist( + id="method-checklist-1", + options=[ + {"label": "Analytisch", "value": "analytisch"}, + {"label": "Numerisch", "value": "numerisch"}, + {"label": "Approximiert", "value": "approx"}, + ], + value=[], + style={"marginBottom": "10px", "marginLeft": "10px"}, + persistence=True, + persistence_type="memory", + persisted_props=["value"], + ), + method_row("Analytisch", "cb-ana-1", "input-ana1", ""), + method_row("Numerisch", "cb-num-1", "input-num-n-1", "n"), + method_row("Stochastisch", "cb-stoch-1", "input-stoch-n-1", "n"), + method_row("Approximiert", "cb-approx-1", "input-approx-ds-1", "ds"), + + + html.Div( + [ + html.Button( + "Berechnen", + id="button-calc-gha1", + n_clicks=0, + className="btn btn-secondary btn-lg shadow", + style={"marginRight": "10px", "marginLeft": "10px", "marginTop": "30px"}, + ), + ], + style={"marginBottom": "20px"}, + ), + + html.Div(id="tabs-GHA1-out", style={"marginBottom": "10px"}), + + dcc.Loading(html.Div(id="output-gha1-ana")), + dcc.Loading(html.Div(id="output-gha1-num")), + dcc.Loading(html.Div(id="output-gha1-stoch")), + dcc.Loading(html.Div(id="output-gha1-approx")), + + dcc.Store(id="store-gha1-ana"), + dcc.Store(id="store-gha1-num"), + dcc.Store(id="store-gha1-stoch"), + dcc.Store(id="store-gha1-approx"), + ], + id="pane-gha1", + style={"display": "block"}, +) + +pane_gha2 = html.Div( + [ + inputfeld("β₀", "input-GHA2-beta0", "°", min=-90, max=90), + inputfeld("λ₀", "input-GHA2-lamb0", "°", min=-180, max=180), + inputfeld("β₁", "input-GHA2-beta1", "°", min=-90, max=90), + inputfeld("λ₁", "input-GHA2-lamb1", "°", min=-180, max=180), + + dcc.Checklist( + id="method-checklist-2", + options=[ + {"label": "Numerisch", "value": "numerisch"}, + {"label": "Stochastisch (ES)", "value": "stochastisch"}, + {"label": "Approximiert", "value": "approx"}, + ], + value=[], + style={"marginBottom": "10px", "marginLeft": "10px"}, + persistence=True, + persistence_type="memory", + persisted_props=["value"], + ), + html.Div( + [ + html.Button( + "Berechnen", + id="button-calc-gha2", + n_clicks=0, + style={"marginRight": "10px", "marginLeft": "10px"}, + ), + ], + style={"marginBottom": "20px"}, + ), + + html.Div(id="tabs-GHA2-out", style={"marginBottom": "10px"}), + + dcc.Loading(html.Div(id="output-gha2-num")), + dcc.Loading(html.Div(id="output-gha2-stoch")), + dcc.Loading(html.Div(id="output-gha2-approx")), + + dcc.Store(id="store-gha2-num"), + dcc.Store(id="store-gha2-stoch"), + dcc.Store(id="store-gha2-approx"), + ], + id="pane-gha2", + style={"display": "none"}, +) + # HTML-Elemente app.layout = html.Div( @@ -226,23 +386,8 @@ app.layout = html.Div( ], ), - html.Div(id="tabs-GHA-out", style={"marginBottom": "10px"}), + html.Div([pane_gha1, pane_gha2], id="tabs-GHA-out", style={"marginBottom": "10px"}), - dcc.Loading(html.Div(id="output-gha1-ana")), - dcc.Loading(html.Div(id="output-gha1-num")), - dcc.Loading(html.Div(id="output-gha1-stoch")), - dcc.Loading(html.Div(id="output-gha1-approx")), - dcc.Loading(html.Div(id="output-gha2-num")), - dcc.Loading(html.Div(id="output-gha2-stoch")), - dcc.Loading(html.Div(id="output-gha2-approx")), - - dcc.Store(id="store-gha1-ana"), - dcc.Store(id="store-gha1-num"), - dcc.Store(id="store-gha1-stoch"), - dcc.Store(id="store-gha1-approx"), - dcc.Store(id="store-gha2-num"), - dcc.Store(id="store-gha2-stoch"), - dcc.Store(id="store-gha2-approx"), ], ), @@ -255,7 +400,7 @@ app.layout = html.Div( "marginTop": "-50px", }, children=[ - html.Label("Koordinatenart wählen:"), + html.Label("Koordinatenart wählen:", style={"marginLeft": "80px"},), dcc.Dropdown( id="dropdown-coors-type", options=[ @@ -266,7 +411,7 @@ app.layout = html.Div( ], value="ell", clearable=False, - style={"width": "300px"}, + style={"width": "200px", "marginLeft": "80px"}, ), dcc.Graph( id="ellipsoid-plot", @@ -302,81 +447,22 @@ def fill_inputs_from_dropdown(selected_ell): # Funktion zur Generierung der Tab-Inhalte @app.callback( - Output("tabs-GHA-out", "children"), + Output("pane-gha1", "style"), + Output("pane-gha2", "style"), Input("tabs-GHA", "value"), ) -def render_content(tab): +def switch_tabs(tab): show1 = {"display": "block"} if tab == "tab-GHA1" else {"display": "none"} show2 = {"display": "block"} if tab == "tab-GHA2" else {"display": "none"} + return show1, show2 - pane_gha1 = html.Div( - [ - inputfeld("β₀", "input-GHA1-beta0", "°", min=-90, max=90), - inputfeld("λ₀", "input-GHA1-lamb0", "°", min=-180, max=180), - inputfeld("s", "input-GHA1-s", "m", min=0), - inputfeld("α₀", "input-GHA1-a", "°", min=0, max=360), +@app.callback( + Output("input-approx-ds-1", "disabled"), + Input("cb-approx-1", "value"), +) +def toggle_ds(v): + return "on" not in (v or []) - dcc.Checklist( - id="method-checklist-1", - options=[ - {"label": "Analytisch", "value": "analytisch"}, - {"label": "Numerisch", "value": "numerisch"}, - #{"label": "Stochastisch (ES)", "value": "stochastisch"}, - {"label": "Approximiert", "value": "approx"}, - ], - value=[], - style={"marginBottom": "10px", "marginLeft": "10px"}, - ), - html.Div( - [ - html.Button( - "Berechnen", - id="button-calc-gha1", - n_clicks=0, - style={"marginRight": "10px", "marginLeft": "10px"}, - ), - ], - style={"marginBottom": "20px"}, - ), - ], - id="pane-gha1", - style=show1, - ) - - pane_gha2 = html.Div( - [ - inputfeld("β₀", "input-GHA2-beta0", "°", min=-90, max=90), - inputfeld("λ₀", "input-GHA2-lamb0", "°", min=-180, max=180), - inputfeld("β₁", "input-GHA2-beta1", "°", min=-90, max=90), - inputfeld("λ₁", "input-GHA2-lamb1", "°", min=-180, max=180), - - dcc.Checklist( - id="method-checklist-2", - options=[ - {"label": "Numerisch", "value": "numerisch"}, - {"label": "Stochastisch (ES)", "value": "stochastisch"}, - {"label": "Approximiert", "value": "approx"}, - ], - value=[], - style={"marginBottom": "10px", "marginLeft": "10px"}, - ), - html.Div( - [ - html.Button( - "Berechnen", - id="button-calc-gha2", - n_clicks=0, - style={"marginRight": "10px", "marginLeft": "10px"}, - ), - ], - style={"marginBottom": "20px"}, - ), - ], - id="pane-gha2", - style=show2, - ) - - return html.Div([pane_gha1, pane_gha2]) # -- GHA 1 --- @@ -426,14 +512,14 @@ def compute_gha1_ana(n1, beta0, lamb0, s, a0, ax, ay, b, method1): html.Br(), html.Span(f"kartesisch: x₁={P1_ana[0]:.4f} m, y₁={P1_ana[1]:.4f} m, z₁={P1_ana[2]:.4f} m"), html.Br(), - html.Span(f"elliptisch: {aus.gms('β₁', beta2_ana, 4)}, {aus.gms('λ₁', lamb2_ana, 4)}"), + html.Span(f"ellipsoidisch: {aus.gms('β₁', beta2_ana, 4)}, {aus.gms('λ₁', lamb2_ana, 4)}"), html.Br(), ]) store = { - "points": [("P0", P0, "black"), ("P1", P1_ana, "red")], + "points": [("P0", P0, "black"), ("P1 (ana)", P1_ana, "red")], "polyline": None, - "color": "#d62728" + "color": "red" } return out, store @@ -473,14 +559,14 @@ def compute_gha1_num(n1, beta0, lamb0, s, a0, ax, ay, b, method1): html.Br(), html.Span(f"kartesisch: x₁={P1_num[0]:.4f} m, y₁={P1_num[1]:.4f} m, z₁={P1_num[2]:.4f} m"), html.Br(), - html.Span(f"elliptisch: {aus.gms('β₁', beta2_num, 4)}, {aus.gms('λ₁', lamb2_num, 4)}"), + html.Span(f"ellipsoidisch: {aus.gms('β₁', beta2_num, 4)}, {aus.gms('λ₁', lamb2_num, 4)}"), html.Br(), ]) polyline = [[x1, y1, z1] for x1, _, y1, _, z1, _ in werte] store = { - "points": [("P0", P0, "black"), ("P1", P1_num, "#ff8c00")], + "points": [("P0", P0, "black"), ("P1 (num)", P1_num, "#ff8c00")], "polyline": polyline, "color": "#ff8c00" } @@ -529,11 +615,11 @@ def compute_gha1_stoch(n1, beta0, lamb0, s, a0, ax, ay, b, method1): html.Br(), html.Span(f"kartesisch: x₁={P1_stoch[0]:.4f} m, y₁={P1_stoch[1]:.4f} m, z₁={P1_stoch[2]:.4f} m"), html.Br(), - html.Span(f"elliptisch: {aus.gms('β₁', beta1_stoch, 4)}, {aus.gms('λ₁', lamb1_stoch, 4)}"), + html.Span(f"ellipsoidisch: {aus.gms('β₁', beta1_stoch, 4)}, {aus.gms('λ₁', lamb1_stoch, 4)}"), ]) store = { - "points": [("P0", P0, "black"), ("P1", P1_stoch, "red")], + "points": [("P0", P0, "black"), ("P1 (ES)", P1_stoch, "#d62728")], "polyline": None, "color": "#d62728" } @@ -566,7 +652,7 @@ def compute_gha1_approx(n1, beta0, lamb0, s, a0, ax, ay, b, method1): s_val = float(s) P0 = ell.ell2cart(beta_rad, lamb_rad) - P1_app, alpha1_app, points = gha1_approx(ell, P0, alpha_rad, s_val, ds=5000, all_points=True) + P1_app, alpha1_app, points, alphas = gha1_approx(ell, P0, alpha_rad, s_val, ds=5000, all_points=True) beta1_app, lamb1_app = ell.cart2ell(P1_app) @@ -575,13 +661,13 @@ def compute_gha1_approx(n1, beta0, lamb0, s, a0, ax, ay, b, method1): html.Br(), html.Span(f"kartesisch: x₁={P1_app[0]:.4f} m, y₁={P1_app[1]:.4f} m, z₁={P1_app[2]:.4f} m"), html.Br(), - html.Span(f"elliptisch: {aus.gms('β₁', beta1_app, 4)}, {aus.gms('λ₁', lamb1_app, 4)}"), + html.Span(f"ellipsoidisch: {aus.gms('β₁', beta1_app, 4)}, {aus.gms('λ₁', lamb1_app, 4)}"), ]) store = { - "points": [("P0", P0, "black"), ("P1", P1_app, "red")], - "polyline": None, - "color": "#d62728" + "points": [("P0", P0, "black"), ("P1 (approx)", P1_app, "#00c2fc")], + "polyline": points, + "color": "#00c2fc" } return out, store @@ -640,9 +726,9 @@ def compute_gha2_num(n2, beta0, lamb0, beta1, lamb1, ax, ay, b, method2): ]) store = { - "points": [("P0", P0, "black"), ("P1", P1, "#1f77b4")], + "points": [("P0", P0, "black"), ("P1", P1, "black")], "polyline": polyline, - "color": "#1f77b4", + "color": "#ff8c00", } return out, store @@ -683,7 +769,8 @@ def compute_gha2_stoch(n2, beta0, lamb0, beta1, lamb1, ax, ay, b, method2): html.Span(f"{aus.gms('α₀', a0_stoch, 4)}, α₁ = {a1_stoch}, s = {s_stoch:.4f} m"), ]) - return out, {"points": None, "polyline": None, "color": "#9467bd"} + + return out, {"points": None, "polyline": None, "color": "#d62728"} @app.callback( Output("output-gha2-approx", "children"), @@ -722,7 +809,13 @@ def compute_gha2_approx(n2, beta0, lamb0, beta1, lamb1, ax, ay, b, method2): html.Span(f"{aus.gms('α₀', a0_app, 4)}, {aus.gms('α₁', a1_app, 4)}, s = {s_app:.4f} m"), ]) - return out, {"points": None, "polyline": None, "color": "#94cccc"} + store = { + "points": [("P0", P0, "black"), ("P1", P1, "black")], + "polyline": points, + "color": "#00c2fc", + } + + return out, store # --- Plot --- @app.callback( @@ -764,4 +857,9 @@ def render_all(ax, ay, b, coords_type, store_gha1_ana, store_gha1_num, store_gha return fig if __name__ == "__main__": - app.run(debug=False) + HOST = "127.0.0.1" + PORT = 8050 + + Timer(1.0, webbrowser.open_new_tab(f"http://{HOST}:{PORT}/")).start + + app.run(host=HOST, port=PORT, debug=False)