From bf1e26c98ad805f7a1dfa43bccbade9db27887cf Mon Sep 17 00:00:00 2001 From: Hendrik Date: Mon, 15 Dec 2025 12:33:12 +0100 Subject: [PATCH] =?UTF-8?q?Code-Versch=C3=B6nerung=20Dashboard?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dashboard.py | 232 ++++++++++++++++++++++++++---------------- ellipsoide.py | 53 ++++++---- winkelumrechnungen.py | 37 +++---- 3 files changed, 195 insertions(+), 127 deletions(-) diff --git a/dashboard.py b/dashboard.py index bea0934..4d5f26d 100644 --- a/dashboard.py +++ b/dashboard.py @@ -16,67 +16,11 @@ 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) - ell = EllipsoidTriaxial(ax, ay, b) - X, Y, Z = ell.para2cart(U, V) - +def ellipsoid_figure(ell: EllipsoidTriaxial, title="Dreiachsiges Ellipsoid"): 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: - um = np.deg2rad(lon_deg) - vm = lat_line - xm, ym, zm = ell.para2cart(um, vm) - 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: - vp = np.deg2rad(lat_deg) - up = lon_line - xp, yp, zp = ell.para2cart(up, vp) - 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, 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 - )) - - rx, ry, rz = 1.05*ax, 1.05*ay, 1.05*b + # Darstellung + rx, ry, rz = 1.05*ell.ax, 1.05*ell.ay, 1.05*ell.b fig.update_layout( title=title, scene=dict( @@ -87,6 +31,121 @@ def ellipsoid_figure(ax, ay, b, pts=None, lines=None, title="Dreiachsiges Ellips ), 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 @@ -98,7 +157,7 @@ app.layout = html.Div( html.Label("Ellipsoid wählen:"), dcc.Dropdown( - id="my-dropdown", + id="dropdown_ellispoid", options=[ {"label": "BursaFialova1993", "value": "BursaFialova1993"}, {"label": "BursaSima1980", "value": "BursaSima1980"}, @@ -107,7 +166,8 @@ app.layout = html.Div( {"label": "Bursa1972", "value": "Bursa1972"}, {"label": "Bursa1970", "value": "Bursa1970"}, {"label": "Bessel-biaxial", "value": "Bessel-biaxial"}, - #{"label": "Ei", "value": "Ei"}, + {"label": "Fiction", "value": "Fiction"}, + # {"label": "Ei", "value": "Ei"}, ], value="", style={"width": "300px", "marginBottom": "20px"}, @@ -115,21 +175,21 @@ app.layout = html.Div( html.Label("Halbachsen:"), dcc.Input( - id="input-1", + id="input_ax", type="number", - placeholder="ax...", + placeholder="ax...[m]", style={"marginBottom": "10px", "display": "block", "width": "300px"}, ), dcc.Input( - id="input-2", + id="input_ay", type="number", - placeholder="ay...", + placeholder="ay...[m]", style={"marginBottom": "10px", "display": "block", "width": "300px"}, ), dcc.Input( - id="input-3", + id="input_b", type="number", - placeholder="b...", + placeholder="b...[m]", style={"marginBottom": "20px", "display": "block", "width": "300px"}, ), @@ -181,16 +241,15 @@ app.layout = html.Div( @app.callback( - Output("input-1", "value"), - Output("input-2", "value"), - Output("input-3", "value"), - Input("my-dropdown", "value"), + Output("input_ax", "value"), + Output("input_ay", "value"), + Output("input_b", "value"), + Input("dropdown_ellispoid", "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 @@ -201,8 +260,8 @@ def fill_inputs_from_dropdown(selected_ell): @app.callback( Output("output-area", "children"), Input("calc-ell", "n_clicks"), - State("input-1", "value"), - State("input-3", "value"), + State("input_ax", "value"), + State("input_b", "value"), ) def update_output(n_clicks, ax, b): if not n_clicks or ax is None or b is None: @@ -310,7 +369,7 @@ def render_content(tab): State("input-GHA2-lamb1", "value"), State("input-GHA2-beta2", "value"), State("input-GHA2-lamb2", "value"), - State("my-dropdown", "value"), + State("dropdown_ellispoid", "value"), prevent_initial_call=True, ) def calc_and_plot(n1, n2, @@ -340,12 +399,12 @@ def calc_and_plot(n1, n2, 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, "red")], - title="Erste Hauptaufgabe - analystisch" - ) + 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, "red")]) + fig = figure_lines(fig, [(p1, p2, "red")]) out1 = f"x₂={p2[0]:.3f}, y₂={p2[1]:.3f}, z₂={p2[2]:.3f}" return out1, "", fig @@ -363,12 +422,11 @@ def calc_and_plot(n1, n2, 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, "red")], - title=f"Zweite Hauptaufgabe - numerisch" - ) + fig = ellipsoid_figure(ell, title="Zweite Hauptaufgabe - numerisch") + fig = figure_constant_lines(fig, ell, "para") + fig = figure_points(fig, [("P1", p1, "black"), ("P2", p2, "red")]) + fig = figure_lines(fig, [(p1, p2, "red")]) + out2 = f"a₁₂={np.rad2deg(alpha_1):.6f}°, a₂₁={np.rad2deg(alpha_2):.6f}°, s={s12:.4f} m" return "", out2, fig diff --git a/ellipsoide.py b/ellipsoide.py index 8d6631c..399f12c 100644 --- a/ellipsoide.py +++ b/ellipsoide.py @@ -202,32 +202,41 @@ class EllipsoidTriaxial: return np.array([x, y, z]) - def ell2cart(self, beta: float, lamb: float) -> np.ndarray: + def ell2cart(self, beta: float | np.ndarray, lamb: float | np.ndarray) -> np.ndarray: """ Panou, Korakitis 2019 2 :param beta: elliptische Breite [rad] :param lamb: elliptische Länge [rad] :return: Punkt in kartesischen Koordinaten """ - if beta == -np.pi/2: - return np.array([0, 0, -self.b]) - elif beta == np.pi/2: - return np.array([0, 0, self.b]) - elif beta == 0 and lamb == -np.pi/2: - return np.array([0, -self.ay, 0]) - elif beta == 0 and lamb == np.pi/2: - return np.array([0, self.ay, 0]) - elif beta == 0 and lamb == 0: - return np.array([self.ax, 0, 0]) - elif beta == 0 and lamb == np.pi: - return np.array([-self.ax, 0, 0]) - else: - B = self.Ex**2 * np.cos(beta)**2 + self.Ee**2 * np.sin(beta)**2 - L = self.Ex**2 - self.Ee**2 * np.cos(lamb)**2 - x = self.ax / self.Ex * np.sqrt(B) * np.cos(lamb) - y = self.ay * np.cos(beta) * np.sin(lamb) - z = self.b / self.Ex * np.sin(beta) * np.sqrt(L) - return np.array([x, y, z]) + beta = np.asarray(beta, dtype=float) + lamb = np.asarray(lamb, dtype=float) + + beta, lamb = np.broadcast_arrays(beta, lamb) + + B = self.Ex ** 2 * np.cos(beta) ** 2 + self.Ee ** 2 * np.sin(beta) ** 2 + L = self.Ex ** 2 - self.Ee ** 2 * np.cos(lamb) ** 2 + + x = self.ax / self.Ex * np.sqrt(B) * np.cos(lamb) + y = self.ay * np.cos(beta) * np.sin(lamb) + z = self.b / self.Ex * np.sin(beta) * np.sqrt(L) + + xyz = np.stack((x, y, z), axis=-1) + + # Pole + mask_south = beta == -np.pi / 2 + mask_north = beta == np.pi / 2 + xyz[mask_south] = np.array([0, 0, -self.b]) + xyz[mask_north] = np.array([0, 0, self.b]) + + # Äquator + mask_eq = beta == 0 + xyz[mask_eq & (lamb == -np.pi / 2)] = np.array([0, -self.ay, 0]) + xyz[mask_eq & (lamb == np.pi / 2)] = np.array([0, self.ay, 0]) + xyz[mask_eq & (lamb == 0)] = np.array([self.ax, 0, 0]) + xyz[mask_eq & (lamb == np.pi)] = np.array([-self.ax, 0, 0]) + + return xyz def cart2ellu(self, point: np.ndarray) -> tuple[float, float, float]: """ @@ -390,7 +399,7 @@ class EllipsoidTriaxial: return phi, lamb, h - def geod2cart(self, phi: float, lamb: float, h: float) -> np.ndarray: + def geod2cart(self, phi: float | np.ndarray, lamb: float | np.ndarray, h: float) -> np.ndarray: """ Ligas 2012, 250 :param phi: geodätische Breite [rad] @@ -425,7 +434,7 @@ class EllipsoidTriaxial: pointH = self. geod2cart(phi, lamb, h) return pointH - def para2cart(self, u: float, v: float) -> np.ndarray: + def para2cart(self, u: float | np.ndarray, v: float | np.ndarray) -> np.ndarray: """ Panou, Korakitits 2020, 4 :param u: Parameter u diff --git a/winkelumrechnungen.py b/winkelumrechnungen.py index 0b0b7f8..8985c21 100644 --- a/winkelumrechnungen.py +++ b/winkelumrechnungen.py @@ -1,4 +1,5 @@ from numpy import * +import numpy as np def deg2gms(deg: float) -> list: @@ -10,13 +11,13 @@ def deg2gms(deg: float) -> list: :rtype: list """ gra = deg // 1 - min = gra % 1 + minu = gra % 1 gra = gra // 1 - min *= 60 - sek = min % 1 - min = min // 1 + minu *= 60 + sek = minu % 1 + minu = minu // 1 sek *= 60 - return [gra, min, sek] + return [gra, minu, sek] def deg2gra(deg: float) -> float: @@ -30,13 +31,13 @@ def deg2gra(deg: float) -> float: return deg * 10/9 -def deg2rad(deg: float) -> float: +def deg2rad(deg: float | np.ndarray) -> float | np.ndarray: """ Umrechnung von Grad in Radiant :param deg: Winkel in Grad - :type deg: float + :type deg: float or np.ndarray :return: Winkel in Radiant - :rtype: float + :rtype: float or np.ndarray """ return deg * pi / 180 @@ -51,13 +52,13 @@ def gra2gms(gra: float) -> list: """ deg = gra2deg(gra) gra = deg // 1 - min = gra % 1 + minu = gra % 1 gra = gra // 1 - min *= 60 - sek = min % 1 - min = min // 1 + minu *= 60 + sek = minu % 1 + minu = minu // 1 sek *= 60 - return [gra, min, sek] + return [gra, minu, sek] def gra2rad(gra: float) -> float: @@ -113,13 +114,13 @@ def rad2gms(rad: float) -> list: :rtype: list """ deg = rad2deg(rad) - min = deg % 1 + minu = deg % 1 gra = deg // 1 - min *= 60 - sek = min % 1 - min = min // 1 + minu *= 60 + sek = minu % 1 + minu = minu // 1 sek *= 60 - return [gra, min, sek] + return [gra, minu, sek] def gms2rad(gms: list) -> float: