diff --git a/dashboard.py b/dashboard.py index 2392f92..8b1e9f9 100644 --- a/dashboard.py +++ b/dashboard.py @@ -10,13 +10,27 @@ import ausgaben as aus from GHA_triaxial.panou import gha1_ana from GHA_triaxial.panou import gha1_num from es_cma_gha1 import gha1_es -from GHA_triaxial.ES_gha2 import gha2_ES +from GHA_triaxial.approx_gha1 import gha1_approx + from GHA_triaxial.panou_2013_2GHA_num import gha2_num +from GHA_triaxial.ES_gha2 import gha2_ES +from GHA_triaxial.approx_gha2 import gha2_approx + app = Dash(__name__, suppress_callback_exceptions=True) app.title = "Geodätische Hauptaufgaben" +def inputfeld(left_text, input_id, right_text="", width=200, min=None, max=None): + return html.Div( + [ + 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",}), + html.Span(right_text, style={"marginLeft": 5}) if right_text else html.Span() + ], + style={"display": "flex", "alignItems": "center", "marginBottom": "10px"} + ) + def ellipsoid_figure(ell: EllipsoidTriaxial, title="Dreiachsiges Ellipsoid"): fig = go.Figure() @@ -30,7 +44,7 @@ def ellipsoid_figure(ell: EllipsoidTriaxial, title="Dreiachsiges Ellipsoid"): zaxis=dict(range=[-rz, rz], title="Z [m]"), aspectmode="data" ), - margin=dict(l=0, r=0, t=40, b=0), + margin=dict(l=0, r=0, t=10, b=0), ) # Ellipsoid @@ -152,101 +166,92 @@ def figure_lines(fig, line, color): # HTML-Elemente app.layout = html.Div( - style={"fontFamily": "Arial", "padding": "5px", "width": "70%", "margin-left": "auto"}, + style={"fontFamily": "Arial", "padding": "10px", "width": "95%", "margin": "0 auto"}, children=[ html.H1("Geodätische Hauptaufgaben"), html.H2("für dreiachsige Ellipsoide"), - - # Dropdown und Eingabefelder Ellipsoid - 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"}, - ), - - # Tabs für beide Hauptaufgaben - Inhalt wird per Funktion generiert s.u. - 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%"}, - ), - - 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.Store(id="store-gha1-ana"), - dcc.Store(id="store-gha1-num"), - dcc.Store(id="store-gha1-stoch"), - - dcc.Loading(html.Div(id="output-gha2-num")), - dcc.Loading(html.Div(id="output-gha2-stoch")), - - dcc.Store(id="store-gha2-num"), - dcc.Store(id="store-gha2-stoch"), - - - # Ausgabebereich für den Plot - dcc.Graph( - id="ellipsoid-plot", - style={"height": "500px", "width": "700px"}, - ), - - # Fussleiste - html.P( - "© 2025", style={ - "fontSize": "12px", - "color": "gray", - "textAlign": "center", + "display": "flex", + "alignItems": "flex-start", + "gap": "24px", + "flexWrap": "nowrap" }, + children=[ + + html.Div( + style={"flex": "1 1 420px", "maxWidth": "640px"}, + children=[ + + 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"}, + ], + value="", + style={"width": "300px", "marginBottom": "16px"}, + ), + + html.P("Halbachsen:", style={"marginBottom": "10px"}), + inputfeld("aₓ", "input-ax", "m", min=0), + inputfeld("aᵧ", "input-ay", "m", min=0), + inputfeld("b", "input-b", "m", min=0), + + dcc.Tabs( + id="tabs-GHA", + value="tab-GHA1", + style={"margin": "20px 0 15px", "width": "100%"}, + children=[ + dcc.Tab(label="Erste Hauptaufgabe", value="tab-GHA1"), + dcc.Tab(label="Zweite Hauptaufgabe", value="tab-GHA2"), + ], + ), + + html.Div(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-gha2-num")), + dcc.Loading(html.Div(id="output-gha2-stoch")), + + dcc.Store(id="store-gha1-ana"), + dcc.Store(id="store-gha1-num"), + dcc.Store(id="store-gha1-stoch"), + dcc.Store(id="store-gha2-num"), + dcc.Store(id="store-gha2-stoch"), + ], + ), + + html.Div( + style={ + "flex": "1 1 750px", + "minWidth": "520px", + "position": "sticky", + "top": "0", + "marginTop": "-150px", + }, + children=[ + dcc.Graph( + id="ellipsoid-plot", + style={"height": "90vh", "width": "100%"}, + config={"responsive": True} + ) + ], + ), + ], ), + + html.P("© 2026", style={"fontSize": "10px", "color": "gray", "textAlign": "center", "marginTop": "16px"}), ], ) @@ -279,14 +284,10 @@ def render_content(tab): 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"}), + inputfeld("β₀", "input-GHA1-beta1", "°", min=-90, max=90), + inputfeld("λ₀", "input-GHA1-lamb1", "°", 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", @@ -294,9 +295,10 @@ def render_content(tab): {"label": "Analytisch", "value": "analytisch"}, {"label": "Numerisch", "value": "numerisch"}, {"label": "Stochastisch (ES)", "value": "stochastisch"}, + {"label": "Approximiert", "value": "approx"}, ], value=[], - style={"marginBottom": "20px"}, + style={"marginBottom": "10px", "marginLeft": "10px"}, ), html.Div( [ @@ -304,12 +306,11 @@ def render_content(tab): "Berechnen", id="button-calc-gha1", n_clicks=0, - style={"marginRight": "10px"}, + style={"marginRight": "10px", "marginLeft": "10px"}, ), ], style={"marginBottom": "20px"}, ), - html.H4("Erste Hauptaufgabe"), ], id="pane-gha1", style=show1, @@ -317,23 +318,20 @@ def render_content(tab): 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"}), + inputfeld("β₀", "input-GHA2-beta1", "°", min=-90, max=90), + inputfeld("λ₀", "input-GHA2-lamb1", "°", min=-180, max=180), + inputfeld("β₁", "input-GHA2-beta2", "°", min=-90, max=90), + inputfeld("λ₁", "input-GHA2-lamb2", "°", 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": "20px"}, + style={"marginBottom": "10px", "marginLeft": "10px"}, ), html.Div( [ @@ -341,12 +339,11 @@ def render_content(tab): "Berechnen", id="button-calc-gha2", n_clicks=0, - style={"marginRight": "10px"}, + style={"marginRight": "10px", "marginLeft": "10px"}, ), ], style={"marginBottom": "20px"}, ), - html.H4("Zweite Hauptaufgabe"), ], id="pane-gha2", style=show2, @@ -354,173 +351,6 @@ def render_content(tab): return html.Div([pane_gha1, pane_gha2]) -# Funktion zur Berechnung der Hauptaufgaben -# @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 -# x1, y1, z1, werte = gha1_num(ell, p1, alpha_rad, s_val, 10000) -# p2_num = x1, y1, z1 -# 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)}") -# ]) -# ) -# -# geo_line_num1 = [] -# for x1, _, y1, _, z1, _ in werte: -# geo_line_num1.append([x1, y1, z1]) -# -# -# 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 - analytisch") -# #fig = figure_constant_lines(fig, ell, "geod") -# fig = figure_constant_lines(fig, ell, "ell") -# #fig = figure_constant_lines(fig, ell, "para") -# if "analytisch" in method1: -# fig = figure_points(fig, [("P1", p1, "black"), ("P2", p2_ana, "red")]) -# if "numerisch" in method1: -# fig = figure_lines(fig, geo_line_num1, "#ff8c00") -# -# #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, beta_arr, lamb_arr = gha2_num( -# ell, -# np.deg2rad(float(beta21)), np.deg2rad(float(lamb21)), -# np.deg2rad(float(beta22)), np.deg2rad(float(lamb22)) -# ) -# geo_line_num = [] -# for beta, lamb in zip(beta_arr, lamb_arr): -# point = ell.ell2cart(beta, lamb) -# geo_line_num.append(point) -# -# 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") -# if "numerisch" in method2: -# fig = figure_lines(fig, geo_line_num, "#ff8c00") -# fig = figure_points(fig, [("P1", p1, "black"), ("P2", p2, "red")]) -# -# -# -# return "", out2, fig -# -# return no_update, no_update, no_update # -- GHA 1 --- @app.callback( @@ -538,16 +368,19 @@ def render_content(tab): prevent_initial_call=True, ) def compute_gha1_ana(n1, beta11, lamb11, s, a_deg, ax, ay, b, method1): + out = html.Div([ + html.H4("Erste Hauptaufgabe"), + ]) if not n1: return no_update, no_update if None in (ax, ay, b): return html.Span("Bitte Ellipsoid wählen.", style={"color": "red"}), None if None in (beta11, lamb11, s, a_deg): - return html.Span("Bitte β₁, λ₁, s und α eingeben.", style={"color": "red"}), None + return html.Span("Bitte β₀, λ₀, s und α₀ eingeben.", style={"color": "red"}), None if not method1: return html.Span("Bitte Berechnungsverfahren wählen.", style={"color": "red"}), None if "analytisch" not in (method1 or []): - return no_update, no_update + return out, no_update ell = EllipsoidTriaxial(ax, ay, b) @@ -562,11 +395,12 @@ def compute_gha1_ana(n1, beta11, lamb11, s, a_deg, ax, ay, b, method1): beta2, lamb2 = ell.cart2ell(p2_ana) out = html.Div([ + html.H4("Erste Hauptaufgabe"), html.Strong("Analytisch: "), html.Br(), - html.Span(f"kartesisch: x₂={x2:.4f} m, y₂={y2:.4f} m, z₂={z2:.4f} m"), + html.Span(f"kartesisch: x₁={x2:.4f} m, y₁={y2:.4f} m, z₁={z2:.4f} m"), html.Br(), - html.Span(f"elliptisch: {aus.gms('β₂', beta2, 4)}, {aus.gms('λ₂', lamb2, 4)}"), + html.Span(f"elliptisch: {aus.gms('β₁', beta2, 4)}, {aus.gms('λ₁', lamb2, 4)}"), html.Br(), ]) @@ -611,9 +445,9 @@ def compute_gha1_num(n1, beta11, lamb11, s, a_deg, ax, ay, b, method1): out = 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.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)}"), + html.Span(f"elliptisch: {aus.gms('β₁', beta2_num, 4)}, {aus.gms('λ₁', lamb2_num, 4)}"), html.Br(), ]) @@ -656,8 +490,7 @@ def compute_gha1_stoch(n1, beta11, lamb11, s, a_deg, ax, ay, b, method1): beta_rad, lamb_rad, alpha_rad, s_val, 10000, - ell, - sigma0=1e-10 + ell ) beta2 = betas[-1] lamb2 = lambs[-1] @@ -670,9 +503,9 @@ def compute_gha1_stoch(n1, beta11, lamb11, s, a_deg, ax, ay, b, method1): out = html.Div([ html.Strong("Stochastisch: "), html.Br(), - html.Span(f"kartesisch: x₂={x2:.4f} m, y₂={y2:.4f} m, z₂={z2:.4f} m"), + 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)}"), + html.Span(f"elliptisch: {aus.gms('β₁', beta2, 4)}, {aus.gms('λ₁', lamb2, 4)}"), ]) store = { @@ -699,16 +532,19 @@ def compute_gha1_stoch(n1, beta11, lamb11, s, a_deg, ax, ay, b, method1): prevent_initial_call=True, ) def compute_gha2_num(n2, beta1, lamb1, beta2, lamb2, ax, ay, b, method2): + out = html.Div([ + html.H4("Zweite Hauptaufgabe"), + ]) if not n2: return no_update, no_update if None in (ax, ay, b): return html.Span("Bitte Ellipsoid wählen.", style={"color": "red"}), None if None in (beta1, lamb1, beta2, lamb2): - return html.Span("Bitte β₁, λ₁, β₂ und λ₂ eingeben.", style={"color": "red"}), None + return html.Span("Bitte β₀, λ₀, β₁ und λ₁ eingeben.", style={"color": "red"}), None if not method2: return html.Span("Bitte Berechnungsverfahren wählen.", style={"color": "red"}), None if "numerisch" not in (method2 or []): - return no_update, no_update + return out, no_update ell = EllipsoidTriaxial(ax, ay, b) @@ -720,15 +556,15 @@ def compute_gha2_num(n2, beta1, lamb1, beta2, lamb2, ax, ay, b, method2): alpha_1, alpha_2, s12, beta_arr, lamb_arr = gha2_num(ell, b1, l1, b2, l2) - # Polyline der Geodäte: [[x,y,z], ...] polyline = [] for b_rad, l_rad in zip(beta_arr, lamb_arr): x, y, z = ell.ell2cart(b_rad, l_rad) polyline.append([float(x), float(y), float(z)]) out = html.Div([ - html.Strong("Zweite Hauptaufgabe – Numerisch: "), - html.Span(f"{aus.gms('α₁₂', alpha_1, 4)}, {aus.gms('α₂₁', alpha_2, 4)}, s = {s12:.4f} m"), + html.H4("Zweite Hauptaufgabe"), + html.Strong("Numerisch: "), + html.Span(f"{aus.gms('α₀', alpha_1, 4)}, {aus.gms('α₁', alpha_2, 4)}, s = {s12:.4f} m"), ]) store = { @@ -759,7 +595,7 @@ def compute_gha2_num(n2, beta1, lamb1, beta2, lamb2, ax, ay, b, method2): # return no_update, no_update # # out = html.Div([ -# html.Strong("Zweite Hauptaufgabe – Stochastisch (ES): "), +# html.Strong("Stochastisch (ES): "), # html.Span("noch nicht implementiert...") # ]) #