zusammenfügen 02.2.
This commit is contained in:
388
Berechnungen.py
388
Berechnungen.py
@@ -1,67 +1,243 @@
|
|||||||
from typing import Any
|
|
||||||
|
|
||||||
import sympy as sp
|
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
import sympy as sp
|
||||||
|
from typing import Any
|
||||||
import math
|
import math
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from numpy import ndarray, dtype
|
|
||||||
|
|
||||||
import Datenbank
|
import Datenbank
|
||||||
|
|
||||||
class Berechnungen:
|
class Berechnungen:
|
||||||
|
"""Geodätische Hilfsberechnungen auf einem Rotationsellipsoid.
|
||||||
|
|
||||||
|
Die Klasse stellt Methoden zur Verfügung für:
|
||||||
|
|
||||||
|
- erste numerische Exzentrizität e² und zweite numerische Exzentrizität e'²,
|
||||||
|
- Umrechnung ECEF (X, Y, Z) → geodätische Breite B, geodätische Länge L und ellipsoidische Höhe H,
|
||||||
|
- lokale ENU-Komponenten (E, N, U),
|
||||||
|
- Horizontalstrecke, Zenitwinkel, Azimut und Richtung,
|
||||||
|
- Berechnung von Azimut, Richtung und Zenitwinkel aus Tachymeterbeobachtungen.
|
||||||
|
|
||||||
|
:ivar a_wert: Große Halbachse a in Meter.
|
||||||
|
:vartype a_wert: float
|
||||||
|
:ivar b_wert: Kleine Halbachse b in Meter.
|
||||||
|
:vartype b_wert: float
|
||||||
|
:ivar e_quadrat_wert: Quadrat der ersten numerischen Exzentrizität e² (einheitenlos).
|
||||||
|
:vartype e_quadrat_wert: float
|
||||||
|
:ivar e_strich_quadrat_wert: Quadrat der zweiten numerischen Exzentrizität e'² (einheitenlos).
|
||||||
|
:vartype e_strich_quadrat_wert: float
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, a: float, b: float) -> None:
|
def __init__(self, a: float, b: float) -> None:
|
||||||
|
"""Initialisiert die Ellipsoidparameter.
|
||||||
|
|
||||||
|
Berechnet die erste und zweite numerische Exzentrizität.
|
||||||
|
|
||||||
|
:param a: Große Halbachse a des Rotationsellipsoids in Meter.
|
||||||
|
:type a: float
|
||||||
|
:param b: Kleine Halbachse b des Rotationsellipsoids in Meter.
|
||||||
|
:type b: float
|
||||||
|
"""
|
||||||
self.a_wert = a
|
self.a_wert = a
|
||||||
self.b_wert = b
|
self.b_wert = b
|
||||||
self.e_quadrat_wert = self.e_quadrat()
|
self.e_quadrat_wert = self.e_quadrat()
|
||||||
self.e_strich_quadrat_wert = self.e_strich_quadrat()
|
self.e_strich_quadrat_wert = self.e_strich_quadrat()
|
||||||
|
|
||||||
def e_quadrat(self) -> float:
|
def e_quadrat(self) -> float:
|
||||||
|
"""Berechnet das Quadrat der ersten numerischen Exzentrizität e².
|
||||||
|
|
||||||
|
Es gilt: e² = (a² − b²) / a², wobei a die große und b die kleine Halbachse ist.
|
||||||
|
|
||||||
|
:return: Quadrat der ersten numerischen Exzentrizität e² (einheitenlos).
|
||||||
|
:rtype: float
|
||||||
|
"""
|
||||||
return (self.a_wert**2 - self.b_wert**2) / self.a_wert **2
|
return (self.a_wert**2 - self.b_wert**2) / self.a_wert **2
|
||||||
|
|
||||||
def e_strich_quadrat(self) -> float:
|
def e_strich_quadrat(self) -> float:
|
||||||
|
"""Berechnet das Quadrat der zweiten numerischen Exzentrizität e'².
|
||||||
|
|
||||||
|
Es gilt: e'² = (a² − b²) / b², wobei a die große und b die kleine Halbachse ist
|
||||||
|
|
||||||
|
:return: Quadrat der zweiten numerischen Exzentrizität e'² (einheitenlos).
|
||||||
|
:rtype: float
|
||||||
|
"""
|
||||||
return (self.a_wert**2 - self.b_wert**2) / self.b_wert **2
|
return (self.a_wert**2 - self.b_wert**2) / self.b_wert **2
|
||||||
|
|
||||||
def P(self, x: float, y: float) -> float:
|
def P(self, x: float, y: float) -> float:
|
||||||
|
"""Berechnet den Hilfswert P aus geozentrischen Koordinaten.
|
||||||
|
|
||||||
|
Es gilt: P = sqrt(x² + y²).
|
||||||
|
|
||||||
|
:param x: Geozentrische kartesische X-Koordinate (ECEF) in Meter.
|
||||||
|
:type x: float
|
||||||
|
:param y: Geozentrische kartesische Y-Koordinate (ECEF) in Meter.
|
||||||
|
:type y: float
|
||||||
|
:return: Hilfswert P in Meter.
|
||||||
|
:rtype: float
|
||||||
|
"""
|
||||||
return np.sqrt(x**2 + y**2)
|
return np.sqrt(x**2 + y**2)
|
||||||
|
|
||||||
def hilfswinkel(self, x: float, y: float, z: float) -> float:
|
def hilfswinkel(self, x: float, y: float, z: float) -> float:
|
||||||
|
"""Berechnet den Hilfswinkel für die Bestimmung der geodätischen Breite.
|
||||||
|
|
||||||
|
Es gilt: hw = atan2(z * a, P(x, y) * b).
|
||||||
|
|
||||||
|
:param x: Geozentrische kartesische X-Koordinate (ECEF) in Meter.
|
||||||
|
:type x: float
|
||||||
|
:param y: Geozentrische kartesische Y-Koordinate (ECEF) in Meter.
|
||||||
|
:type y: float
|
||||||
|
:param z: Geozentrische kartesische Z-Koordinate (ECEF) in Meter.
|
||||||
|
:type z: float
|
||||||
|
:return: Hilfswinkel in Radiant.
|
||||||
|
:rtype: float
|
||||||
|
"""
|
||||||
hw = np.atan2(z * self.a_wert, self.P(x, y) * self.b_wert)
|
hw = np.atan2(z * self.a_wert, self.P(x, y) * self.b_wert)
|
||||||
return hw
|
return hw
|
||||||
|
|
||||||
def B(self, x: float, y: float, z: float) -> float:
|
def B(self, x: float, y: float, z: float) -> float:
|
||||||
|
"""Berechnet die geodätische Breite B aus geozentrischen Koordinaten.
|
||||||
|
|
||||||
|
Verwendet den Hilfswinkel, e² und e'² zur Berechnung.
|
||||||
|
|
||||||
|
:param x: Geozentrische kartesische X-Koordinate (ECEF) in Meter.
|
||||||
|
:type x: float
|
||||||
|
:param y: Geozentrische kartesische Y-Koordinate (ECEF) in Meter.
|
||||||
|
:type y: float
|
||||||
|
:param z: Geozentrische kartesische Z-Koordinate (ECEF) in Meter.
|
||||||
|
:type z: float
|
||||||
|
:return: Geodätische Breite B in Radiant.
|
||||||
|
:rtype: float
|
||||||
|
"""
|
||||||
hilfswinkel = self.hilfswinkel(x, y, z)
|
hilfswinkel = self.hilfswinkel(x, y, z)
|
||||||
B = np.atan2((z + self.e_strich_quadrat_wert * self.b_wert * np.sin(hilfswinkel) ** 3), (self.P(x, y) - self.e_quadrat_wert * self.a_wert * np.cos(hilfswinkel) ** 3))
|
B = np.atan2((z + self.e_strich_quadrat_wert * self.b_wert * np.sin(hilfswinkel) ** 3), (self.P(x, y) - self.e_quadrat_wert * self.a_wert * np.cos(hilfswinkel) ** 3))
|
||||||
return B
|
return B
|
||||||
|
|
||||||
def L(self, x: float, y: float) -> float:
|
def L(self, x: float, y: float) -> float:
|
||||||
|
"""Berechnet die geodätische Länge L aus geozentrischen Koordinaten.
|
||||||
|
|
||||||
|
Es gilt: L = atan2(y, x).
|
||||||
|
|
||||||
|
:param x: Geozentrische kartesische X-Koordinate (ECEF) in Meter.
|
||||||
|
:type x: float
|
||||||
|
:param y: Geozentrische kartesische Y-Koordinate (ECEF) in Meter.
|
||||||
|
:type y: float
|
||||||
|
:return: Geodätische Länge L in Radiant.
|
||||||
|
:rtype: float
|
||||||
|
"""
|
||||||
return np.atan2(y, x)
|
return np.atan2(y, x)
|
||||||
|
|
||||||
def H(self, x: float, y: float, z: float) -> float:
|
def H(self, x: float, y: float, z: float) -> float:
|
||||||
|
"""Berechnet die ellipsoidische Höhe H aus geozentrisch kartesischen Koordinaten.
|
||||||
|
|
||||||
|
Die ellipsoidische Höhe wird mithilfe der geodätischen Breite B und der Ellipsoidparameter berechnet.
|
||||||
|
|
||||||
|
:param x: Geozentrische kartesische X-Koordinate (ECEF) in Meter.
|
||||||
|
:type x: float
|
||||||
|
:param y: Geozentrische kartesische Y-Koordinate (ECEF) in Meter.
|
||||||
|
:type y: float
|
||||||
|
:param z: Geozentrische kartesische Z-Koordinate (ECEF) in Meter.
|
||||||
|
:type z: float
|
||||||
|
:return: Ellipsoidische Höhe H in Meter.
|
||||||
|
:rtype: float
|
||||||
|
"""
|
||||||
B = self.B(x, y, z)
|
B = self.B(x, y, z)
|
||||||
H = (self.P(x, y) / np.cos(B)) - self.a_wert / (np.sqrt(1 - self.e_quadrat_wert * np.sin(B) ** 2))
|
H = (self.P(x, y) / np.cos(B)) - self.a_wert / (np.sqrt(1 - self.e_quadrat_wert * np.sin(B) ** 2))
|
||||||
return H
|
return H
|
||||||
|
|
||||||
def E(self, L: float, dX: float, dY: float) -> float:
|
def E(self, L: float, dX: float, dY: float) -> float:
|
||||||
|
"""Berechnet die Ostkomponente E im lokalen ENU-System.
|
||||||
|
|
||||||
|
:param L: Geodätische Länge in Radiant.
|
||||||
|
:type L: float
|
||||||
|
:param dX: Differenz dX = X2 − X1 in Meter.
|
||||||
|
:type dX: float
|
||||||
|
:param dY: Differenz dY = Y2 − Y1 in Meter.
|
||||||
|
:type dY: float
|
||||||
|
:return: Ostkomponente E in Meter.
|
||||||
|
:rtype: float
|
||||||
|
"""
|
||||||
E = -np.sin(L) * dX + np.cos(L) * dY
|
E = -np.sin(L) * dX + np.cos(L) * dY
|
||||||
return E
|
return E
|
||||||
|
|
||||||
def N(self, B: float, L: float, dX: float, dY: float, dZ: float) -> float:
|
def N(self, B: float, L: float, dX: float, dY: float, dZ: float) -> float:
|
||||||
|
"""Berechnet die Nordkomponente N im lokalen ENU-System.
|
||||||
|
|
||||||
|
:param B: Geodätische Breite in Radiant.
|
||||||
|
:type B: float
|
||||||
|
:param L: Geodätische Länge in Radiant.
|
||||||
|
:type L: float
|
||||||
|
:param dX: Differenz dX = X2 − X1 in Meter.
|
||||||
|
:type dX: float
|
||||||
|
:param dY: Differenz dY = Y2 − Y1 in Meter.
|
||||||
|
:type dY: float
|
||||||
|
:param dZ: Differenz dZ = Z2 − Z1 in Meter.
|
||||||
|
:type dZ: float
|
||||||
|
:return: Nordkomponente N in Meter.
|
||||||
|
:rtype: float
|
||||||
|
"""
|
||||||
N = -np.sin(B) * np.cos(L) * dX - np.sin(B) * np.sin(L) * dY + np.cos(B) * dZ
|
N = -np.sin(B) * np.cos(L) * dX - np.sin(B) * np.sin(L) * dY + np.cos(B) * dZ
|
||||||
return N
|
return N
|
||||||
|
|
||||||
def U(self, B: float, L: float, dX: float, dY: float, dZ: float) -> float:
|
def U(self, B: float, L: float, dX: float, dY: float, dZ: float) -> float:
|
||||||
|
"""Berechnet die Up-Komponente U im lokalen ENU-System.
|
||||||
|
|
||||||
|
:param B: Geodätischee Breite in Radiant.
|
||||||
|
:type B: float
|
||||||
|
:param L: Geodätische Länge in Radiant.
|
||||||
|
:type L: float
|
||||||
|
:param dX: Differenz dX = X2 − X1 in Meter.
|
||||||
|
:type dX: float
|
||||||
|
:param dY: Differenz dY = Y2 − Y1 in Meter.
|
||||||
|
:type dY: float
|
||||||
|
:param dZ: Differenz dZ = Z2 − Z1 in Meter.
|
||||||
|
:type dZ: float
|
||||||
|
:return: Up-Komponente U in Meter.
|
||||||
|
:rtype: float
|
||||||
|
"""
|
||||||
U = np.cos(B) * np.cos(L) * dX + np.cos(B) * np.sin(L) *dY + np.sin(B) * dZ
|
U = np.cos(B) * np.cos(L) * dX + np.cos(B) * np.sin(L) *dY + np.sin(B) * dZ
|
||||||
return U
|
return U
|
||||||
|
|
||||||
def horizontalstrecke_ENU(self, E: float, N: float) -> float:
|
def horizontalstrecke_ENU(self, E: float, N: float) -> float:
|
||||||
|
"""Berechnet die Horizontalstrecke aus den ENU-Komponenten E und N.
|
||||||
|
|
||||||
|
Es gilt: s = sqrt(E² + N²).
|
||||||
|
|
||||||
|
:param E: Ostkomponente E in Meter.
|
||||||
|
:type E: float
|
||||||
|
:param N: Nordkomponente N in Meter.
|
||||||
|
:type N: float
|
||||||
|
:return: Horizontalstrecke in Meter.
|
||||||
|
:rtype: float
|
||||||
|
"""
|
||||||
s = np.sqrt(E**2 + N**2)
|
s = np.sqrt(E**2 + N**2)
|
||||||
return s
|
return s
|
||||||
|
|
||||||
def Zenitwinkel(self, horizontalstrecke_ENU: float, U: float) -> float:
|
def Zenitwinkel(self, horizontalstrecke_ENU: float, U: float) -> float:
|
||||||
|
"""Berechnet den Zenitwinkel aus Horizontalstrecke und Up-Komponente.
|
||||||
|
|
||||||
|
Es gilt: zw = atan2(horizontalstrecke_ENU, U).
|
||||||
|
|
||||||
|
:param horizontalstrecke_ENU: Horizontalstrecke in Meter.
|
||||||
|
:type horizontalstrecke_ENU: float
|
||||||
|
:param U: Up-Komponente U in Meter.
|
||||||
|
:type U: float
|
||||||
|
:return: Zenitwinkel in Radiant.
|
||||||
|
:rtype: float
|
||||||
|
"""
|
||||||
zw = np.atan2(horizontalstrecke_ENU, U)
|
zw = np.atan2(horizontalstrecke_ENU, U)
|
||||||
return zw
|
return zw
|
||||||
|
|
||||||
def Azimut(self, E: float, N: float) -> float:
|
def Azimut(self, E: float, N: float) -> float:
|
||||||
|
"""Berechnet den Azimut aus den ENU-Komponenten E und N.
|
||||||
|
|
||||||
|
Der Azimut wird auf [0, 2π) normiert.
|
||||||
|
|
||||||
|
:param E: Ostkomponente E in Meter.
|
||||||
|
:type E: float
|
||||||
|
:param N: Nordkomponente N in Meter.
|
||||||
|
:type N: float
|
||||||
|
:return: Azimut in Radiant im Bereich [0, 2π).
|
||||||
|
:rtype: float
|
||||||
|
"""
|
||||||
Azimut = np.atan2(E, N)
|
Azimut = np.atan2(E, N)
|
||||||
if Azimut < 0:
|
if Azimut < 0:
|
||||||
Azimut += 2 * np.pi
|
Azimut += 2 * np.pi
|
||||||
@@ -70,6 +246,17 @@ class Berechnungen:
|
|||||||
return Azimut
|
return Azimut
|
||||||
|
|
||||||
def Richtung(self, Azimut: float, Orientierung: float) -> float:
|
def Richtung(self, Azimut: float, Orientierung: float) -> float:
|
||||||
|
"""Berechnet die Richtung aus Azimut und Orientierung.
|
||||||
|
|
||||||
|
Die Richtung wird auf [0, 2π) normiert.
|
||||||
|
|
||||||
|
:param Azimut: Azimut in Radiant.
|
||||||
|
:type Azimut: float
|
||||||
|
:param Orientierung: Orientierung in Radiant.
|
||||||
|
:type Orientierung: float
|
||||||
|
:return: Richtung in Radiant im Bereich [0, 2π).
|
||||||
|
:rtype: float
|
||||||
|
"""
|
||||||
Richtung = Azimut - Orientierung
|
Richtung = Azimut - Orientierung
|
||||||
if Richtung < 0:
|
if Richtung < 0:
|
||||||
Richtung += 2 * np.pi
|
Richtung += 2 * np.pi
|
||||||
@@ -77,13 +264,37 @@ class Berechnungen:
|
|||||||
Richtung -= 2 * np.pi
|
Richtung -= 2 * np.pi
|
||||||
return Richtung
|
return Richtung
|
||||||
|
|
||||||
def geometrische_breite_laenge(self, dict_koordinaten: dict) -> dict:
|
def geodätische_breite_laenge(self, dict_koordinaten: dict) -> dict:
|
||||||
|
"""Berechnet geodätische Breite und Länge für alle Punkte eines Koordinatendictionaries.
|
||||||
|
|
||||||
|
Die Einträge des Dictionaries werden in einer Schleife erweitert zu [Koordinatenmatrix, B, L], wobei Koordinatenmatrix = (X, Y, Z) ist.
|
||||||
|
|
||||||
|
:param dict_koordinaten: Dictionary mit geozentrischen Koordinaten je Punkt.
|
||||||
|
:type dict_koordinaten: dict
|
||||||
|
:return: Das übergebene Dictionary mit erweiterten Einträgen.
|
||||||
|
:rtype: dict
|
||||||
|
"""
|
||||||
for punktnummer, matrix in dict_koordinaten.items():
|
for punktnummer, matrix in dict_koordinaten.items():
|
||||||
|
|
||||||
dict_koordinaten[punktnummer] = [matrix, self.B(matrix[0], matrix[1], matrix[2]), self.L(matrix[0], matrix[1])]
|
dict_koordinaten[punktnummer] = [matrix, self.B(matrix[0], matrix[1], matrix[2]), self.L(matrix[0], matrix[1])]
|
||||||
return dict_koordinaten
|
return dict_koordinaten
|
||||||
|
|
||||||
def berechnung_richtung_azimut_zenitwinkel(self, pfad_datenbank: str, dict_koordinaten: dict) -> tuple[list[Any], dict[Any, Any]]:
|
def berechnung_richtung_azimut_zenitwinkel(self, pfad_datenbank: str, dict_koordinaten: dict) -> tuple[list[Any], dict[Any, Any]]:
|
||||||
|
"""Berechnet Azimut, Richtung und Zenitwinkel aus Tachymeterbeobachtungen.
|
||||||
|
|
||||||
|
Die Tachymeterbeobachtungen werden aus der Datenbank gelesen. Für jede Beobachtung
|
||||||
|
(Standpunkt → Zielpunkt) werden aus den Koordinatendifferenzen die lokalen ENU-Komponenten
|
||||||
|
am Standpunkt berechnet und daraus Azimut, Richtung und Zenitwinkel berechnet.
|
||||||
|
|
||||||
|
Die Orientierung wird pro Beobachtungsgruppe durch den ersten Azimut der Gruppe gesetzt.
|
||||||
|
|
||||||
|
:param pfad_datenbank: Pfad zur Datenbank.
|
||||||
|
:type pfad_datenbank: str
|
||||||
|
:param dict_koordinaten: Dictionary mit geozentrisch kartesischen Koordinaten je Punkt (ECEF).
|
||||||
|
:type dict_koordinaten: dict
|
||||||
|
:return: Tupel aus Ergebnisliste mit (beobachtungsgruppeID, standpunkt, zielpunkt, Azimut, richtung, Zenitwinkel, schraegstrecke, orientierung) und Dictionary der Orientierungen je Beobachtungsgruppe. Winkel in Radiant, Strecken in Meter.
|
||||||
|
:rtype: tuple[list[Any], dict[Any, Any]]
|
||||||
|
"""
|
||||||
dict_koordinaten_erweitert = {}
|
dict_koordinaten_erweitert = {}
|
||||||
dict_orientierungen = {}
|
dict_orientierungen = {}
|
||||||
liste_azimut_richtungen = []
|
liste_azimut_richtungen = []
|
||||||
@@ -149,6 +360,21 @@ class Berechnungen:
|
|||||||
|
|
||||||
def berechne_zenitwinkel_distanz_bodenbezogen(self, zenitwinkel_messung: float, schraegdistanz_messung: float,
|
def berechne_zenitwinkel_distanz_bodenbezogen(self, zenitwinkel_messung: float, schraegdistanz_messung: float,
|
||||||
instrumentenhoehe: float, prismenhoehe: float):
|
instrumentenhoehe: float, prismenhoehe: float):
|
||||||
|
"""Berechnet bodenbezogene Schrägdistanz und bodenbezogenen Zenitwinkel.
|
||||||
|
|
||||||
|
Aus gemessener Schrägdistanz und gemessenem Zenitwinkel werden die Horizontalstrecke, der bodenbezogene Höhenunterschied sowie die bodenbezogenen Größen abgeleitet.
|
||||||
|
|
||||||
|
:param zenitwinkel_messung: Gemessener Zenitwinkel in Radiant.
|
||||||
|
:type zenitwinkel_messung: float
|
||||||
|
:param schraegdistanz_messung: Gemessene Schrägdistanz in Meter.
|
||||||
|
:type schraegdistanz_messung: float
|
||||||
|
:param instrumentenhoehe: Instrumentenhöhe in Meter.
|
||||||
|
:type instrumentenhoehe: float
|
||||||
|
:param prismenhoehe: Prismen-/Zielhöhe in Meter.
|
||||||
|
:type prismenhoehe: float
|
||||||
|
:return: Bodenbezogene Schrägdistanz in Meter und bodenbezogener Zenitwinkel in Radiant.
|
||||||
|
:rtype: tuple[float, float]
|
||||||
|
"""
|
||||||
HD = np.sin(np.pi - zenitwinkel_messung) * schraegdistanz_messung
|
HD = np.sin(np.pi - zenitwinkel_messung) * schraegdistanz_messung
|
||||||
delta_h_ihzh = schraegdistanz_messung * np.cos(zenitwinkel_messung)
|
delta_h_ihzh = schraegdistanz_messung * np.cos(zenitwinkel_messung)
|
||||||
delta_h_boden = delta_h_ihzh + instrumentenhoehe - prismenhoehe
|
delta_h_boden = delta_h_ihzh + instrumentenhoehe - prismenhoehe
|
||||||
@@ -158,30 +384,166 @@ class Berechnungen:
|
|||||||
|
|
||||||
|
|
||||||
class Einheitenumrechnung:
|
class Einheitenumrechnung:
|
||||||
def __init__(self) -> None:
|
"""Einheitenumrechnungen für Winkel- und Längeneinheiten.
|
||||||
pass
|
|
||||||
|
|
||||||
|
Die Klasse stellt Methoden zur Verfügung für:
|
||||||
|
|
||||||
|
- Umrechnung von Millibogensekunden (mas) in Radiant,
|
||||||
|
- Umrechnung von Millimetern (mm) in Meter,
|
||||||
|
- Umrechnung von Gon und Milligon (mgon) in Radiant (Decimal-basiert).
|
||||||
|
"""
|
||||||
def mas_to_rad(mas: float) -> float:
|
def mas_to_rad(mas: float) -> float:
|
||||||
|
"""Rechnet Millibogensekunden (mas) in Radiant um.
|
||||||
|
|
||||||
|
Es gilt: rad = mas * (pi / (180 * 3600 * 1000)).
|
||||||
|
|
||||||
|
:param mas: Winkel in Millibogensekunden (mas).
|
||||||
|
:type mas: float
|
||||||
|
:return: Winkel in Radiant.
|
||||||
|
:rtype: float
|
||||||
|
"""
|
||||||
umrechnungsfaktor = 1 / 1000 * 1 / 3600 * sp.pi / 180
|
umrechnungsfaktor = 1 / 1000 * 1 / 3600 * sp.pi / 180
|
||||||
grad = mas * umrechnungsfaktor
|
grad = mas * umrechnungsfaktor
|
||||||
return grad
|
return grad
|
||||||
|
|
||||||
def mm_to_m(mm: float) -> float:
|
def mm_to_m(mm: float) -> float:
|
||||||
|
"""Rechnet Millimeter in Meter um.
|
||||||
|
|
||||||
|
Es gilt: m = mm / 1000.
|
||||||
|
|
||||||
|
:param mm: Länge in Millimeter.
|
||||||
|
:type mm: float
|
||||||
|
:return: Länge in Meter.
|
||||||
|
:rtype: float
|
||||||
|
"""
|
||||||
m = mm / 1000
|
m = mm / 1000
|
||||||
return m
|
return m
|
||||||
|
|
||||||
def ppb(ppb: float) -> float:
|
def gon_to_rad_Decimal(gon: float) -> Decimal:
|
||||||
ppb *= 10 ** (-9)
|
"""Rechnet Gon in Radiant um (Decimal-basiert).
|
||||||
return ppb
|
|
||||||
|
|
||||||
def gon_to_rad_Decimal(gon: float) -> float:
|
Es gilt: 400 gon = 2*pi und damit rad = (gon / 200) * pi.
|
||||||
|
|
||||||
|
:param gon: Winkel in Gon.
|
||||||
|
:type gon: float
|
||||||
|
:return: Winkel in Radiant als Decimal.
|
||||||
|
:rtype: Decimal
|
||||||
|
"""
|
||||||
gon = Decimal(gon)
|
gon = Decimal(gon)
|
||||||
pi = Decimal(str(math.pi))
|
pi = Decimal(str(math.pi))
|
||||||
rad = (gon / Decimal(200)) * pi
|
rad = (gon / Decimal(200)) * pi
|
||||||
return rad
|
return rad
|
||||||
|
|
||||||
def mgon_to_rad_Decimal(gon: float) -> float:
|
def mgon_to_rad_Decimal(gon: float) -> Decimal:
|
||||||
|
"""Rechnet Milligon (mgon) in Radiant um (Decimal-basiert).
|
||||||
|
|
||||||
|
Es gilt: 1 mgon = 0.001 gon und damit rad = (mgon / 200000) * pi.
|
||||||
|
|
||||||
|
:param gon: Winkel in Milligon (mgon).
|
||||||
|
:type gon: float
|
||||||
|
:return: Winkel in Radiant als Decimal.
|
||||||
|
:rtype: Decimal
|
||||||
|
"""
|
||||||
gon = Decimal(gon)
|
gon = Decimal(gon)
|
||||||
pi = Decimal(str(math.pi))
|
pi = Decimal(str(math.pi))
|
||||||
rad = (gon / Decimal(200000)) * pi
|
rad = (gon / Decimal(200000)) * pi
|
||||||
return rad
|
return rad
|
||||||
|
|
||||||
|
def rad_to_gon_Decimal(rad: float) -> Decimal:
|
||||||
|
"""Rechnet Radiant in Gon um (Decimal-basiert).
|
||||||
|
|
||||||
|
Es gilt: 400 gon = 2*pi und damit rad = (gon / 200) * pi.
|
||||||
|
|
||||||
|
:param rad: Winkel in Rad.
|
||||||
|
:type rad: float
|
||||||
|
:return: Winkel in Gon als Decimal.
|
||||||
|
:rtype: Decimal
|
||||||
|
"""
|
||||||
|
rad = Decimal(rad)
|
||||||
|
pi = Decimal(str(math.pi))
|
||||||
|
gon = (rad / pi) * Decimal(200)
|
||||||
|
return gon
|
||||||
|
|
||||||
|
|
||||||
|
class ENU:
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def berechne_schwerpunkt_fuer_enu(berechnungen, dict_xyz):
|
||||||
|
XYZ = np.array(list(dict_xyz.values()), dtype=float)
|
||||||
|
X0, Y0, Z0 = XYZ.mean(axis=0)
|
||||||
|
B0 = float(berechnungen.B(X0, Y0, Z0))
|
||||||
|
L0 = float(berechnungen.L(X0, Y0))
|
||||||
|
return B0, L0
|
||||||
|
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def berechne_R0_ENU(berechnungen, B, L):
|
||||||
|
# East
|
||||||
|
r11 = berechnungen.E(L, 1, 0)
|
||||||
|
r12 = berechnungen.E(L, 0, 1)
|
||||||
|
r13 = berechnungen.E(L, 0, 0)
|
||||||
|
|
||||||
|
# North
|
||||||
|
r21 = berechnungen.N(B, L, 1, 0, 0)
|
||||||
|
r22 = berechnungen.N(B, L, 0, 1, 0)
|
||||||
|
r23 = berechnungen.N(B, L, 0, 0, 1)
|
||||||
|
|
||||||
|
# Up
|
||||||
|
r31 = berechnungen.U(B, L, 1, 0, 0)
|
||||||
|
r32 = berechnungen.U(B, L, 0, 1, 0)
|
||||||
|
r33 = berechnungen.U(B, L, 0, 0, 1)
|
||||||
|
|
||||||
|
R0 = np.array([
|
||||||
|
[r11, r12, r13],
|
||||||
|
[r21, r22, r23],
|
||||||
|
[r31, r32, r33]
|
||||||
|
], dtype=float)
|
||||||
|
|
||||||
|
return R0
|
||||||
|
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def berechne_R_ENU(unbekannten_liste, R0):
|
||||||
|
names = [str(s) for s in unbekannten_liste]
|
||||||
|
n = len(names)
|
||||||
|
R = np.eye(n, dtype=float)
|
||||||
|
|
||||||
|
punkt_ids = [nm[1:] for nm in names if nm and nm[0].upper() == "X"]
|
||||||
|
|
||||||
|
for pid in punkt_ids:
|
||||||
|
try:
|
||||||
|
ix = next(i for i, nm in enumerate(names) if nm.upper() == f"X{pid}".upper())
|
||||||
|
iy = next(i for i, nm in enumerate(names) if nm.upper() == f"Y{pid}".upper())
|
||||||
|
iz = next(i for i, nm in enumerate(names) if nm.upper() == f"Z{pid}".upper())
|
||||||
|
except StopIteration:
|
||||||
|
continue
|
||||||
|
|
||||||
|
I = [ix, iy, iz]
|
||||||
|
R[np.ix_(I, I)] = R0
|
||||||
|
|
||||||
|
return R
|
||||||
|
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def transform_Qxx_zu_QxxENU(Qxx, unbekannten_liste, berechnungen, dict_xyz):
|
||||||
|
B0, L0 = ENU.berechne_schwerpunkt_fuer_enu(berechnungen, dict_xyz)
|
||||||
|
R0 = ENU.berechne_R0_ENU(berechnungen, B0, L0)
|
||||||
|
R_ENU = ENU.berechne_R_ENU(unbekannten_liste, R0)
|
||||||
|
|
||||||
|
Qenu = R_ENU @ Qxx @ R_ENU.T
|
||||||
|
Qenu = 0.5 * (Qenu + Qenu.T)
|
||||||
|
|
||||||
|
return Qenu, (B0, L0), R0
|
||||||
|
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def transform_Koord_zu_KoordENU(dict_xyz, R0):
|
||||||
|
XYZ = np.asarray(list(dict_xyz.values()), dtype=float).reshape(-1, 3)
|
||||||
|
XYZ0 = XYZ.mean(axis=0).reshape(3, )
|
||||||
|
|
||||||
|
Koord_ENU = {}
|
||||||
|
for pid, xyz in dict_xyz.items():
|
||||||
|
xyz = np.asarray(xyz, dtype=float).reshape(3, )
|
||||||
|
enu = (R0 @ (xyz - XYZ0)).reshape(3, )
|
||||||
|
Koord_ENU[str(pid)] = (float(enu[0]), float(enu[1]), float(enu[2]))
|
||||||
|
return Koord_ENU
|
||||||
61418
Campusnetz.ipynb
61418
Campusnetz.ipynb
File diff suppressed because it is too large
Load Diff
473
Datenbank.py
473
Datenbank.py
@@ -1,78 +1,109 @@
|
|||||||
|
from decimal import Decimal
|
||||||
import os
|
import os
|
||||||
import sqlite3
|
import sqlite3
|
||||||
from typing import Any
|
|
||||||
|
|
||||||
import sympy as sp
|
import sympy as sp
|
||||||
from sympy import MutableDenseMatrix
|
from sympy import MutableDenseMatrix
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from Berechnungen import Einheitenumrechnung
|
from Berechnungen import Einheitenumrechnung
|
||||||
from decimal import Decimal
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Datenbank_anlegen:
|
class Datenbank_anlegen:
|
||||||
|
"""Legt die SQLite-Datenbank für die Ausgleichungsrechnung an.
|
||||||
|
|
||||||
|
Die Klasse erstellt die Datenbankdatei nur dann, wenn am angegebenen Pfad noch
|
||||||
|
keine Datenbank existiert. In diesem Fall werden die Tabellen und Constraints
|
||||||
|
per SQL-Skript angelegt.
|
||||||
|
|
||||||
|
Angelegte Tabellen:
|
||||||
|
- Netzpunkte,
|
||||||
|
- Beobachtungen,
|
||||||
|
- Instrumente,
|
||||||
|
- Genauigkeiten,
|
||||||
|
- Varianzkomponentenschaetzung.
|
||||||
|
|
||||||
|
:ivar pfad_datenbank: Pfad zur SQLite-Datenbankdatei.
|
||||||
|
:vartype pfad_datenbank: str
|
||||||
|
"""
|
||||||
def __init__(self, pfad_datenbank: str) -> None:
|
def __init__(self, pfad_datenbank: str) -> None:
|
||||||
|
"""Initialisiert den Datenbankpfad und legt die Datenbank bei Bedarf an.
|
||||||
|
|
||||||
|
Beim Erstellen der Instanz wird db_anlegen() aufgerufen. Existiert die
|
||||||
|
Datenbankdatei unter dem übergebenen Pfad bereits, erfolgt keine Änderung.
|
||||||
|
|
||||||
|
:param pfad_datenbank: Pfad zur SQLite-Datenbankdatei.
|
||||||
|
:type pfad_datenbank: str
|
||||||
|
:return: None
|
||||||
|
:rtype: None
|
||||||
|
"""
|
||||||
self.pfad_datenbank = pfad_datenbank
|
self.pfad_datenbank = pfad_datenbank
|
||||||
self.db_anlegen()
|
self.db_anlegen()
|
||||||
|
|
||||||
def db_anlegen(self) -> None:
|
def db_anlegen(self) -> None:
|
||||||
# pfad = r"C:\Users\fabia\OneDrive\Jade HS\Master\MGW2\Masterprojekt_allgemein\Masterprojekt\Programmierung\Campusnetz\Campusnetz.db"
|
"""Legt die SQLite-Datenbank an, sofern die Datei nicht existiert.
|
||||||
|
|
||||||
|
Prüft, ob die Datenbankdatei am Pfad vorhanden ist. Falls nicht, wird eine neue
|
||||||
|
SQLite-Datenbank erstellt.
|
||||||
|
Anschließend werden die Änderungen committet und die Verbindung geschlossen.
|
||||||
|
|
||||||
|
:return: None
|
||||||
|
:rtype: None
|
||||||
|
"""
|
||||||
if not os.path.exists(self.pfad_datenbank):
|
if not os.path.exists(self.pfad_datenbank):
|
||||||
con = sqlite3.connect(self.pfad_datenbank)
|
con = sqlite3.connect(self.pfad_datenbank)
|
||||||
cursor = con.cursor()
|
cursor = con.cursor()
|
||||||
cursor.executescript("""CREATE TABLE Netzpunkte (
|
cursor.executescript("""CREATE TABLE Netzpunkte (
|
||||||
punktnummer TEXT(10),
|
punktnummer TEXT(10),
|
||||||
naeherungx_lh NUMERIC(9,3),
|
naeherungx_lh NUMERIC(9,3),
|
||||||
naeherungy_lh NUMERIC(7,3),
|
naeherungy_lh NUMERIC(7,3),
|
||||||
naeherungz_lh NUMERIC(8,3),
|
naeherungz_lh NUMERIC(8,3),
|
||||||
naeherungz_lh_niv NUMERIC(8,3),
|
naeherungz_lh_niv NUMERIC(8,3),
|
||||||
naeherungx_us NUMERIC(9,3),
|
naeherungx_us NUMERIC(9,3),
|
||||||
naeherungy_us NUMERIC(7,3),
|
naeherungy_us NUMERIC(7,3),
|
||||||
naeherungz_us NUMERIC(8,3),
|
naeherungz_us NUMERIC(8,3),
|
||||||
normalhoehe_hfp NUMERIC(5,3),
|
normalhoehe_hfp NUMERIC(5,3),
|
||||||
datumskoordinate_x INTEGER DEFAULT 0,
|
datumskoordinate_x INTEGER DEFAULT 0,
|
||||||
datumskoordinate_y INTEGER DEFAULT 0,
|
datumskoordinate_y INTEGER DEFAULT 0,
|
||||||
datumskoordinate_z INTEGER DEFAULT 0,
|
datumskoordinate_z INTEGER DEFAULT 0,
|
||||||
stabw_vorinfo_x NUMERIC(3, 8),
|
stabw_vorinfo_x NUMERIC(3, 8),
|
||||||
stabw_vorinfo_y NUMERIC(3, 8),
|
stabw_vorinfo_y NUMERIC(3, 8),
|
||||||
stabw_vorinfo_z NUMERIC(3, 8),
|
stabw_vorinfo_z NUMERIC(3, 8),
|
||||||
CONSTRAINT pk_Netzpunkte PRIMARY KEY (punktnummer)
|
CONSTRAINT pk_Netzpunkte PRIMARY KEY (punktnummer)
|
||||||
);
|
);
|
||||||
""");
|
""");
|
||||||
cursor.executescript("""CREATE TABLE Beobachtungen(
|
cursor.executescript("""CREATE TABLE Beobachtungen(
|
||||||
beobachtungenID INTEGER,
|
beobachtungenID INTEGER,
|
||||||
beobachtungsgruppeID INTEGER,
|
beobachtungsgruppeID INTEGER,
|
||||||
punktnummer_sp TEXT(10),
|
punktnummer_sp TEXT(10),
|
||||||
punktnummer_zp TEXT(10),
|
punktnummer_zp TEXT(10),
|
||||||
instrumenteID INTEGER,
|
instrumenteID INTEGER,
|
||||||
tachymeter_richtung NUMERIC(8, 6),
|
tachymeter_richtung NUMERIC(8, 6),
|
||||||
tachymeter_richtung_ausschalten INTEGER DEFAULT 0,
|
tachymeter_richtung_ausschalten INTEGER DEFAULT 0,
|
||||||
tachymeter_zenitwinkel_roh NUMERIC(8, 6),
|
tachymeter_zenitwinkel_roh NUMERIC(8, 6),
|
||||||
tachymeter_zenitwinkel NUMERIC(8, 6),
|
tachymeter_zenitwinkel NUMERIC(8, 6),
|
||||||
tachymeter_zenitwinkel_ausschalten INTEGER DEFAULT 0,
|
tachymeter_zenitwinkel_ausschalten INTEGER DEFAULT 0,
|
||||||
tachymeter_distanz_roh NUMERIC(8, 4),
|
tachymeter_distanz_roh NUMERIC(8, 4),
|
||||||
tachymeter_distanz NUMERIC(8, 4),
|
tachymeter_distanz NUMERIC(8, 4),
|
||||||
tachymeter_distanz_auschalten INTEGER DEFAULT 0,
|
tachymeter_distanz_auschalten INTEGER DEFAULT 0,
|
||||||
tachymeter_instrumentenhoehe NUMERIC(3, 8),
|
tachymeter_instrumentenhoehe NUMERIC(3, 8),
|
||||||
tachymeter_prismenhoehe NUMERIC(3, 8),
|
tachymeter_prismenhoehe NUMERIC(3, 8),
|
||||||
gnss_bx NUMERIC(7, 4),
|
gnss_bx NUMERIC(7, 4),
|
||||||
gnss_bx_ausschalten INTEGER DEFAULT 0,
|
gnss_bx_ausschalten INTEGER DEFAULT 0,
|
||||||
gnss_by NUMERIC(7, 4),
|
gnss_by NUMERIC(7, 4),
|
||||||
gnss_by_ausschalten INTEGER DEFAULT 0,
|
gnss_by_ausschalten INTEGER DEFAULT 0,
|
||||||
gnss_bz NUMERIC(7, 4),
|
gnss_bz NUMERIC(7, 4),
|
||||||
gnss_bz_ausschalten INTEGER DEFAULT 0,
|
gnss_bz_ausschalten INTEGER DEFAULT 0,
|
||||||
gnss_s0 NUMERIC(1, 8),
|
gnss_s0 NUMERIC(1, 8),
|
||||||
gnss_cxx NUMERIC(1, 8),
|
gnss_cxx NUMERIC(1, 8),
|
||||||
gnss_cxy NUMERIC(1, 8),
|
gnss_cxy NUMERIC(1, 8),
|
||||||
gnss_cxz NUMERIC(1, 8),
|
gnss_cxz NUMERIC(1, 8),
|
||||||
gnss_cyy NUMERIC(1, 8),
|
gnss_cyy NUMERIC(1, 8),
|
||||||
gnss_cyz NUMERIC(1, 8),
|
gnss_cyz NUMERIC(1, 8),
|
||||||
gnss_czz NUMERIC(1, 8),
|
gnss_czz NUMERIC(1, 8),
|
||||||
niv_dh NUMERIC(8, 6),
|
niv_dh NUMERIC(8, 6),
|
||||||
niv_strecke NUMERIC(8, 6),
|
niv_strecke NUMERIC(8, 6),
|
||||||
niv_anz_standpkte INTEGER,
|
niv_anz_standpkte INTEGER,
|
||||||
niv_ausschalten INTEGER DEFAULT 0,
|
niv_ausschalten INTEGER DEFAULT 0,
|
||||||
dateiname TEXT(200),
|
dateiname TEXT(200),
|
||||||
CONSTRAINT pk_Beobachtungen PRIMARY KEY (beobachtungenID),
|
CONSTRAINT pk_Beobachtungen PRIMARY KEY (beobachtungenID),
|
||||||
CONSTRAINT fk_Beobachtungen_Netzpunktesp FOREIGN KEY (punktnummer_sp) REFERENCES Netzpunkte(punktnummer),
|
CONSTRAINT fk_Beobachtungen_Netzpunktesp FOREIGN KEY (punktnummer_sp) REFERENCES Netzpunkte(punktnummer),
|
||||||
CONSTRAINT fk_Beobachtungen_Netzpunktezp FOREIGN KEY (punktnummer_zp) REFERENCES Netzpunkte(punktnummer),
|
CONSTRAINT fk_Beobachtungen_Netzpunktezp FOREIGN KEY (punktnummer_zp) REFERENCES Netzpunkte(punktnummer),
|
||||||
@@ -80,41 +111,68 @@ class Datenbank_anlegen:
|
|||||||
);
|
);
|
||||||
""");
|
""");
|
||||||
cursor.executescript("""CREATE TABLE Instrumente(
|
cursor.executescript("""CREATE TABLE Instrumente(
|
||||||
instrumenteID INTEGER,
|
instrumenteID INTEGER,
|
||||||
typ TEXT(200),
|
typ TEXT(200),
|
||||||
name TEXT(200),
|
name TEXT(200),
|
||||||
CONSTRAINT pk_Instrumente PRIMARY KEY (instrumenteID)
|
CONSTRAINT pk_Instrumente PRIMARY KEY (instrumenteID)
|
||||||
);
|
);
|
||||||
""");
|
""");
|
||||||
cursor.executescript("""CREATE TABLE Genauigkeiten(
|
cursor.executescript("""CREATE TABLE Genauigkeiten(
|
||||||
genauigkeitenID INTEGER,
|
genauigkeitenID INTEGER,
|
||||||
instrumenteID INTEGER,
|
instrumenteID INTEGER,
|
||||||
beobachtungsart TEXT(25),
|
beobachtungsart TEXT(25),
|
||||||
stabw_apriori_konstant NUMERIC(3, 8),
|
stabw_apriori_konstant NUMERIC(3, 8),
|
||||||
stabw_apriori_streckenprop NUMERIC(3, 8),
|
stabw_apriori_streckenprop NUMERIC(3, 8),
|
||||||
CONSTRAINT pk_Genauigkeiten PRIMARY KEY (genauigkeitenID),
|
CONSTRAINT pk_Genauigkeiten PRIMARY KEY (genauigkeitenID),
|
||||||
CONSTRAINT fk_Genauigkeiten_Instrumente FOREIGN KEY (instrumenteID) REFERENCES Instrumente(instrumenteID)
|
CONSTRAINT fk_Genauigkeiten_Instrumente FOREIGN KEY (instrumenteID) REFERENCES Instrumente(instrumenteID)
|
||||||
);
|
);
|
||||||
""");
|
""");
|
||||||
cursor.executescript("""CREATE TABLE Varianzkomponentenschaetzung(
|
cursor.executescript("""CREATE TABLE Varianzkomponentenschaetzung(
|
||||||
varianzkomponenteID INTEGER,
|
varianzkomponenteID INTEGER,
|
||||||
instrumenteID INTEGER,
|
instrumenteID INTEGER,
|
||||||
beobachtungsgruppe TEXT(50),
|
beobachtungsgruppe TEXT(50),
|
||||||
varianz_varianzkomponentenschaetzung NUMERIC(3, 16) DEFAULT 1,
|
varianz_varianzkomponentenschaetzung NUMERIC(3, 16) DEFAULT 1,
|
||||||
CONSTRAINT pk_Varianzkomponentenschaetzung PRIMARY KEY (varianzkomponenteID),
|
CONSTRAINT pk_Varianzkomponentenschaetzung PRIMARY KEY (varianzkomponenteID),
|
||||||
CONSTRAINT fk_Varianzkomponentenschaetzung_Instrumente FOREIGN KEY (instrumenteID) REFERENCES Instrumente(instrumenteID)
|
CONSTRAINT fk_Varianzkomponentenschaetzung_Instrumente FOREIGN KEY (instrumenteID) REFERENCES Instrumente(instrumenteID)
|
||||||
);
|
);
|
||||||
""")
|
""")
|
||||||
|
|
||||||
con.commit()
|
con.commit()
|
||||||
cursor.close()
|
cursor.close()
|
||||||
con.close()
|
con.close()
|
||||||
|
|
||||||
class Datenbankzugriff:
|
class Datenbankzugriff:
|
||||||
|
"""Zugriff auf die SQLite-Datenbank des Ausgleichungsprogramms.
|
||||||
|
|
||||||
|
Die Klasse stellt Methoden zur Verfügung für:
|
||||||
|
|
||||||
|
- Schreiben in die Datenbank durch alle set_* Methoden
|
||||||
|
- Lesen aus der Datenbank durch alle get_* Methoden
|
||||||
|
|
||||||
|
:ivar pfad_datenbank: Pfad zur SQLite-Datenbankdatei.
|
||||||
|
:vartype pfad_datenbank: str
|
||||||
|
"""
|
||||||
def __init__(self, pfad_datenbank: str) -> None:
|
def __init__(self, pfad_datenbank: str) -> None:
|
||||||
|
"""Initialisiert den Datenbankzugriff.
|
||||||
|
|
||||||
|
:param pfad_datenbank: Pfad zur SQLite-Datenbankdatei.
|
||||||
|
:type pfad_datenbank: str
|
||||||
|
"""
|
||||||
self.pfad_datenbank = pfad_datenbank
|
self.pfad_datenbank = pfad_datenbank
|
||||||
|
|
||||||
def set_koordinaten(self, dict_koordinaten: dict, koordinatenart: str) -> None:
|
def set_koordinaten(self, dict_koordinaten: dict, koordinatenart: str) -> None:
|
||||||
|
"""Schreibt Koordinaten in die Tabelle Netzpunkte.
|
||||||
|
|
||||||
|
Die Koordinaten werden aus `dict_koordinaten` gelesen und abhängig von der koordinatenart
|
||||||
|
in die entsprechenden Felder geschrieben. Es werden nur Einträge aktualisiert, deren
|
||||||
|
Zielspalten aktuell ``NULL`` sind.
|
||||||
|
|
||||||
|
:param dict_koordinaten: Dictionary mit Punktnummer → (X, Y, Z).
|
||||||
|
:type dict_koordinaten: dict
|
||||||
|
:param koordinatenart: Art der Koordinaten ("naeherung_lh" für die Koordinaten im lokalen Horizontsystem aus der Tachymetermessung oder "naeherung_us" für die Näherungskoordinaten der Netzpunkte im geozentrisch-kartesischen-system).
|
||||||
|
:type koordinatenart: str
|
||||||
|
:return: None
|
||||||
|
:rtype: None
|
||||||
|
"""
|
||||||
con = sqlite3.connect(self.pfad_datenbank)
|
con = sqlite3.connect(self.pfad_datenbank)
|
||||||
cursor = con.cursor()
|
cursor = con.cursor()
|
||||||
|
|
||||||
@@ -127,18 +185,33 @@ class Datenbankzugriff:
|
|||||||
float(wert[2]),
|
float(wert[2]),
|
||||||
str(punktnummer)
|
str(punktnummer)
|
||||||
))
|
))
|
||||||
|
# Der Import für die Koordinatenart "naeherung_lh" ist im Zuge möglicher Erweiterungen zu implementieren.
|
||||||
if koordinatenart == "naeherung_lh":
|
if koordinatenart == "naeherung_lh":
|
||||||
pass
|
pass
|
||||||
elif koordinatenart == "naeherung_us":
|
elif koordinatenart == "naeherung_us":
|
||||||
cursor.executemany(f"""UPDATE Netzpunkte SET naeherungx_us = ?, naeherungy_us = ?, naeherungz_us = ? WHERE punktnummer = ? AND naeherungx_us IS NULL AND naeherungy_us IS NULL AND naeherungz_us IS NULL""", daten)
|
cursor.executemany(f"""UPDATE Netzpunkte SET naeherungx_us = ?, naeherungy_us = ?, naeherungz_us = ? WHERE punktnummer = ? AND naeherungx_us IS NULL AND naeherungy_us IS NULL AND naeherungz_us IS NULL""", daten)
|
||||||
|
|
||||||
|
|
||||||
con.commit()
|
con.commit()
|
||||||
cursor.close()
|
cursor.close()
|
||||||
con.close()
|
con.close()
|
||||||
|
|
||||||
def set_instrument(self, typ: str, name: str, liste_beobachtungsarten: list) -> None:
|
def set_instrument(self, typ: str, name: str, liste_beobachtungsarten: list) -> None:
|
||||||
|
"""Fügt ein Instrument hinzu und initialisiert Varianzkomponenten.
|
||||||
|
|
||||||
|
Ist das Instrument (typ, name) noch nicht in der Datenbank vorhanden, wird es in die Tabelle Instrumente eingefügt.
|
||||||
|
Zusätzlich werden Einträge in Varianzkomponentenschaetzung für die angegebenen
|
||||||
|
Beobachtungsarten erzeugt. Falls die Varianzkomponentenschätzung noch leer ist, wird
|
||||||
|
ein zusätzlicher Eintrag für die Beobachtungsgruppe "Anschlusspunkte" angelegt.
|
||||||
|
|
||||||
|
:param typ: Instrumenttyp.
|
||||||
|
:type typ: str
|
||||||
|
:param name: Instrumentname.
|
||||||
|
:type name: str
|
||||||
|
:param liste_beobachtungsarten: Liste der Beobachtungsgruppen zur Initialisierung.
|
||||||
|
:type liste_beobachtungsarten: list
|
||||||
|
:return: None
|
||||||
|
:rtype: None
|
||||||
|
"""
|
||||||
con = sqlite3.connect(self.pfad_datenbank)
|
con = sqlite3.connect(self.pfad_datenbank)
|
||||||
cursor = con.cursor()
|
cursor = con.cursor()
|
||||||
liste_instrumente = cursor.execute("SELECT * FROM Instrumente WHERE typ = ? AND name =?", (typ, name)).fetchall()
|
liste_instrumente = cursor.execute("SELECT * FROM Instrumente WHERE typ = ? AND name =?", (typ, name)).fetchall()
|
||||||
@@ -175,9 +248,27 @@ class Datenbankzugriff:
|
|||||||
|
|
||||||
def set_genauigkeiten(self, instrumenteID: int, beobachtungsart: str, stabw_apriori_konstant: float = None,
|
def set_genauigkeiten(self, instrumenteID: int, beobachtungsart: str, stabw_apriori_konstant: float = None,
|
||||||
stabw_apriori_streckenprop: float = None) -> None:
|
stabw_apriori_streckenprop: float = None) -> None:
|
||||||
|
"""Speichert a-priori Genauigkeiten für die Beobachtungsgruppen pro Instrument.
|
||||||
|
|
||||||
|
Prüft, ob instrumenteID existiert und ob mindestens eine Genauigkeitsangabe übergeben wurde.
|
||||||
|
Je nach Beobachtungsart werden Einheitenumrechnungen durchgeführt (z. B. mgon → rad bzw. mm → m).
|
||||||
|
Der Eintrag wird nur ergänzt, wenn in Genauigkeiten kein identischer Datensatz vorhanden ist.
|
||||||
|
|
||||||
|
:param instrumenteID: ID des Instruments in der Tabelle Instrumente.
|
||||||
|
:type instrumenteID: int
|
||||||
|
:param beobachtungsart: Bezeichnung der Beobachtungsart.
|
||||||
|
:type beobachtungsart: str
|
||||||
|
:param stabw_apriori_konstant: Konstanter Anteil der Standardabweichung (optional).
|
||||||
|
:type stabw_apriori_konstant: float | None
|
||||||
|
:param stabw_apriori_streckenprop: Streckenproportionaler Anteil der Standardabweichung (optional).
|
||||||
|
:type stabw_apriori_streckenprop: float | None
|
||||||
|
:return: None
|
||||||
|
:rtype: None
|
||||||
|
"""
|
||||||
con = sqlite3.connect(self.pfad_datenbank)
|
con = sqlite3.connect(self.pfad_datenbank)
|
||||||
cursor = con.cursor()
|
cursor = con.cursor()
|
||||||
|
|
||||||
|
# Überprüfung auf vorhandensein der Instrumentes in der Tabelle Instrumente
|
||||||
instrumentenname = cursor.execute("SELECT name FROM Instrumente WHERE instrumenteID = ?",
|
instrumentenname = cursor.execute("SELECT name FROM Instrumente WHERE instrumenteID = ?",
|
||||||
(instrumenteID, )).fetchone()
|
(instrumenteID, )).fetchone()
|
||||||
if instrumentenname is None:
|
if instrumentenname is None:
|
||||||
@@ -188,6 +279,7 @@ class Datenbankzugriff:
|
|||||||
return
|
return
|
||||||
instrumentenname = instrumentenname[0]
|
instrumentenname = instrumentenname[0]
|
||||||
|
|
||||||
|
# Überprüfung, ob Genauigkeitsinformationen vom Benutzer übergeben wurden.
|
||||||
if stabw_apriori_konstant is None and stabw_apriori_streckenprop is None:
|
if stabw_apriori_konstant is None and stabw_apriori_streckenprop is None:
|
||||||
print(
|
print(
|
||||||
"Es wurden keine Genauigkeiten importiert. Bitte stabw_apriori_konstant und / oder stabw_apriori_streckenprop angeben.")
|
"Es wurden keine Genauigkeiten importiert. Bitte stabw_apriori_konstant und / oder stabw_apriori_streckenprop angeben.")
|
||||||
@@ -195,6 +287,7 @@ class Datenbankzugriff:
|
|||||||
con.close()
|
con.close()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Umrechnen der Einheiten in Radiant und Meter
|
||||||
if beobachtungsart == "Tachymeter_Richtung" or beobachtungsart == "Tachymeter_Zenitwinkel" :
|
if beobachtungsart == "Tachymeter_Richtung" or beobachtungsart == "Tachymeter_Zenitwinkel" :
|
||||||
stabw_apriori_konstant = Einheitenumrechnung.mgon_to_rad_Decimal(stabw_apriori_konstant)
|
stabw_apriori_konstant = Einheitenumrechnung.mgon_to_rad_Decimal(stabw_apriori_konstant)
|
||||||
|
|
||||||
@@ -207,6 +300,7 @@ class Datenbankzugriff:
|
|||||||
if isinstance(stabw_apriori_streckenprop, Decimal):
|
if isinstance(stabw_apriori_streckenprop, Decimal):
|
||||||
stabw_apriori_streckenprop = float(stabw_apriori_streckenprop)
|
stabw_apriori_streckenprop = float(stabw_apriori_streckenprop)
|
||||||
|
|
||||||
|
# Überprüfen, ob die Genauigkeitsinformation für die jeweilige Beobachtungsart des Instruments bereits vorhanden ist. Wenn nein, wird die Benutzereingabe in die Datenbank gespeichert.
|
||||||
sql = "SELECT 1 FROM Genauigkeiten WHERE instrumenteID = ? AND beobachtungsart = ?"
|
sql = "SELECT 1 FROM Genauigkeiten WHERE instrumenteID = ? AND beobachtungsart = ?"
|
||||||
params = [instrumenteID, beobachtungsart]
|
params = [instrumenteID, beobachtungsart]
|
||||||
|
|
||||||
@@ -248,6 +342,7 @@ class Datenbankzugriff:
|
|||||||
)
|
)
|
||||||
print(
|
print(
|
||||||
f"Die Genauigkeitsangabe für die Beobachtungsart {beobachtungsart} des Instrumentes {instrumentenname} wurde erfolgreich hinzugefügt.")
|
f"Die Genauigkeitsangabe für die Beobachtungsart {beobachtungsart} des Instrumentes {instrumentenname} wurde erfolgreich hinzugefügt.")
|
||||||
|
|
||||||
else:
|
else:
|
||||||
print("Die Genauigkeitsangabe ist bereits in der Datenbank vorhanden.")
|
print("Die Genauigkeitsangabe ist bereits in der Datenbank vorhanden.")
|
||||||
|
|
||||||
@@ -258,9 +353,28 @@ class Datenbankzugriff:
|
|||||||
def set_datumskoordinaten(self, liste_datumskoordinaten_x: list, liste_datumskoordinaten_y: list,
|
def set_datumskoordinaten(self, liste_datumskoordinaten_x: list, liste_datumskoordinaten_y: list,
|
||||||
liste_datumskoordinaten_z: list,
|
liste_datumskoordinaten_z: list,
|
||||||
liste_datumskoordinaten_x_y_z: list) -> None:
|
liste_datumskoordinaten_x_y_z: list) -> None:
|
||||||
|
"""Setzt Datumskoordinaten für Netzpunkte unter Berücksichtigung von Vorinformationen.
|
||||||
|
|
||||||
|
Für die übergebenen Punktlisten werden die Felder datumskoordinate_x, datumskoordinate_y
|
||||||
|
und / oder datumskoordinate_z auf 1 gesetzt. Ein Setzen erfolgt nur, wenn zu der jeweiligen Koordinate
|
||||||
|
eine Vorinformation zur Standardabweichung (stabw_vorinfo_*) vorhanden ist. Eine 1 bedeutet,
|
||||||
|
dass dieser Punkt für die Datumdefinition verwendet wird.
|
||||||
|
|
||||||
|
:param liste_datumskoordinaten_x: Liste der Punktnummern mit Datumsdefinition in X.
|
||||||
|
:type liste_datumskoordinaten_x: list
|
||||||
|
:param liste_datumskoordinaten_y: Liste der Punktnummern mit Datumsdefinition in Y.
|
||||||
|
:type liste_datumskoordinaten_y: list
|
||||||
|
:param liste_datumskoordinaten_z: Liste der Punktnummern mit Datumsdefinition in Z.
|
||||||
|
:type liste_datumskoordinaten_z: list
|
||||||
|
:param liste_datumskoordinaten_x_y_z: Liste der Punktnummern mit Datumsdefinition in X, Y und Z.
|
||||||
|
:type liste_datumskoordinaten_x_y_z: list
|
||||||
|
:return: None
|
||||||
|
:rtype: None
|
||||||
|
"""
|
||||||
con = sqlite3.connect(self.pfad_datenbank)
|
con = sqlite3.connect(self.pfad_datenbank)
|
||||||
cursor = con.cursor()
|
cursor = con.cursor()
|
||||||
|
|
||||||
|
# Überprüfen, ob Genauigkeitsinformationen zu den Punkten in der Datenbank gespeichert sind. Ist dies der Fall, wird eine 1 in das entsprechende Tupel geschrieben.
|
||||||
liste_stabw_vorinfo_x = [str(row[0]).strip() for row in cursor.execute(
|
liste_stabw_vorinfo_x = [str(row[0]).strip() for row in cursor.execute(
|
||||||
"SELECT punktnummer FROM Netzpunkte WHERE stabw_vorinfo_x IS NOT NULL").fetchall()]
|
"SELECT punktnummer FROM Netzpunkte WHERE stabw_vorinfo_x IS NOT NULL").fetchall()]
|
||||||
liste_stabw_vorinfo_y = [str(row[0]).strip() for row in cursor.execute(
|
liste_stabw_vorinfo_y = [str(row[0]).strip() for row in cursor.execute(
|
||||||
@@ -277,6 +391,7 @@ class Datenbankzugriff:
|
|||||||
cursor.execute(f"UPDATE Netzpunkte SET datumskoordinate_x = 1 WHERE punktnummer = ? AND stabw_vorinfo_x IS NOT NULL", (str(punktnummer),))
|
cursor.execute(f"UPDATE Netzpunkte SET datumskoordinate_x = 1 WHERE punktnummer = ? AND stabw_vorinfo_x IS NOT NULL", (str(punktnummer),))
|
||||||
else:
|
else:
|
||||||
print(f"Die X-Koordinate des Punktes {punktnummer} wurde nicht in eine Datumskoordinate geändert, weil keine Vorinformationen zur Standardabweichung der X-Koordinate des Punktes vorliegen. Diese bitte zuerst erfassen und Datumsdefinition wiederholen.")
|
print(f"Die X-Koordinate des Punktes {punktnummer} wurde nicht in eine Datumskoordinate geändert, weil keine Vorinformationen zur Standardabweichung der X-Koordinate des Punktes vorliegen. Diese bitte zuerst erfassen und Datumsdefinition wiederholen.")
|
||||||
|
|
||||||
if liste_datumskoordinaten_y != []:
|
if liste_datumskoordinaten_y != []:
|
||||||
for punktnummer in liste_datumskoordinaten_y:
|
for punktnummer in liste_datumskoordinaten_y:
|
||||||
punktnummer = str(punktnummer).strip()
|
punktnummer = str(punktnummer).strip()
|
||||||
@@ -308,26 +423,48 @@ class Datenbankzugriff:
|
|||||||
def set_datumskoordinaten_to_neupunkte(self, liste_datumskoordinaten_x: list, liste_datumskoordinaten_y: list,
|
def set_datumskoordinaten_to_neupunkte(self, liste_datumskoordinaten_x: list, liste_datumskoordinaten_y: list,
|
||||||
liste_datumskoordinaten_z: list,
|
liste_datumskoordinaten_z: list,
|
||||||
liste_datumskoordinaten_x_y_z: list) -> None:
|
liste_datumskoordinaten_x_y_z: list) -> None:
|
||||||
|
"""Setzt Datumskoordinaten für angegebene Punkte wieder zurück.
|
||||||
|
|
||||||
|
Für die übergebenen Punktlisten werden die Felder datumskoordinate_x, datumskoordinate_y
|
||||||
|
und / oder datumskoordinate_z auf 0 gesetzt. Dadurch werden diese in der Ausgleichung wieder
|
||||||
|
als Neupunkte behandelt und werden nicht für die Datumsdefinition verwendet.
|
||||||
|
|
||||||
|
:param liste_datumskoordinaten_x: Liste der Punktnummern, deren X-Datumsfestlegung zurückgesetzt wird.
|
||||||
|
:type liste_datumskoordinaten_x: list
|
||||||
|
:param liste_datumskoordinaten_y: Liste der Punktnummern, deren Y-Datumsfestlegung zurückgesetzt wird.
|
||||||
|
:type liste_datumskoordinaten_y: list
|
||||||
|
:param liste_datumskoordinaten_z: Liste der Punktnummern, deren Z-Datumsfestlegung zurückgesetzt wird.
|
||||||
|
:type liste_datumskoordinaten_z: list
|
||||||
|
:param liste_datumskoordinaten_x_y_z: Liste der Punktnummern, deren X-, Y- und Z-Datumsfestlegung zurückgesetzt werden.
|
||||||
|
:type liste_datumskoordinaten_x_y_z: list
|
||||||
|
:return: None
|
||||||
|
:rtype: None
|
||||||
|
"""
|
||||||
con = sqlite3.connect(self.pfad_datenbank)
|
con = sqlite3.connect(self.pfad_datenbank)
|
||||||
cursor = con.cursor()
|
cursor = con.cursor()
|
||||||
|
|
||||||
|
# Überprüfung, ob eine Benutzereingabe in der entsprechenden Liste vorgenommen wurde. Wenn ja, wird ein UPDATE in der Datenbank durchgeführt.
|
||||||
if liste_datumskoordinaten_x != []:
|
if liste_datumskoordinaten_x != []:
|
||||||
for punktnummer in liste_datumskoordinaten_x:
|
for punktnummer in liste_datumskoordinaten_x:
|
||||||
punktnummer = str(punktnummer).strip()
|
punktnummer = str(punktnummer).strip()
|
||||||
cursor.execute(
|
cursor.execute(
|
||||||
f"UPDATE Netzpunkte SET datumskoordinate_x = 0 WHERE punktnummer = ?",
|
f"UPDATE Netzpunkte SET datumskoordinate_x = 0 WHERE punktnummer = ?",
|
||||||
(str(punktnummer),))
|
(str(punktnummer),))
|
||||||
|
|
||||||
if liste_datumskoordinaten_y != []:
|
if liste_datumskoordinaten_y != []:
|
||||||
for punktnummer in liste_datumskoordinaten_y:
|
for punktnummer in liste_datumskoordinaten_y:
|
||||||
punktnummer = str(punktnummer).strip()
|
punktnummer = str(punktnummer).strip()
|
||||||
cursor.execute(
|
cursor.execute(
|
||||||
f"UPDATE Netzpunkte SET datumskoordinate_y = 0 WHERE punktnummer = ?",
|
f"UPDATE Netzpunkte SET datumskoordinate_y = 0 WHERE punktnummer = ?",
|
||||||
(str(punktnummer),))
|
(str(punktnummer),))
|
||||||
|
|
||||||
if liste_datumskoordinaten_z != []:
|
if liste_datumskoordinaten_z != []:
|
||||||
for punktnummer in liste_datumskoordinaten_z:
|
for punktnummer in liste_datumskoordinaten_z:
|
||||||
punktnummer = str(punktnummer).strip()
|
punktnummer = str(punktnummer).strip()
|
||||||
cursor.execute(
|
cursor.execute(
|
||||||
f"UPDATE Netzpunkte SET datumskoordinate_z = 0 WHERE punktnummer = ?",
|
f"UPDATE Netzpunkte SET datumskoordinate_z = 0 WHERE punktnummer = ?",
|
||||||
(str(punktnummer),))
|
(str(punktnummer),))
|
||||||
|
|
||||||
if liste_datumskoordinaten_x_y_z != []:
|
if liste_datumskoordinaten_x_y_z != []:
|
||||||
for punktnummer in liste_datumskoordinaten_x_y_z:
|
for punktnummer in liste_datumskoordinaten_x_y_z:
|
||||||
punktnummer = str(punktnummer).strip()
|
punktnummer = str(punktnummer).strip()
|
||||||
@@ -340,6 +477,16 @@ class Datenbankzugriff:
|
|||||||
con.close()
|
con.close()
|
||||||
|
|
||||||
def set_normalhoehe_hfp(self, liste_normalhoehe_hfp: list) -> LiteralString | str:
|
def set_normalhoehe_hfp(self, liste_normalhoehe_hfp: list) -> LiteralString | str:
|
||||||
|
"""Speichert Normalhöhen von amtlichen Höhenfestpunkten (HFP) in Netzpunkte.
|
||||||
|
|
||||||
|
Existiert die Punktnummer bereits in der Tabelle Netzpunkte, wird das Attribut normalhoehe_hfp aktualisiert.
|
||||||
|
Andernfalls wird ein neuer Datensatz mit Punktnummer und Normalhöhe eingefügt.
|
||||||
|
|
||||||
|
:param liste_normalhoehe_hfp: Liste mit HFP-Informationen (u. a. Punktnummer und Normalhöhe).
|
||||||
|
:type liste_normalhoehe_hfp: list
|
||||||
|
:return: Zusammenfassung der durchgeführten Aktualisierungen bzw. Hinweis, falls keine Daten übergeben wurden.
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
liste_hfp_in_db = self.get_normalhoehe_hfp()
|
liste_hfp_in_db = self.get_normalhoehe_hfp()
|
||||||
if liste_normalhoehe_hfp != []:
|
if liste_normalhoehe_hfp != []:
|
||||||
con = sqlite3.connect(self.pfad_datenbank)
|
con = sqlite3.connect(self.pfad_datenbank)
|
||||||
@@ -369,22 +516,36 @@ class Datenbankzugriff:
|
|||||||
return f"Es wurden keine neuen Normalhöhen übergeben. Folgende Normalhöhen sind in der Datenbank enthalten: {liste_hfp_in_db}"
|
return f"Es wurden keine neuen Normalhöhen übergeben. Folgende Normalhöhen sind in der Datenbank enthalten: {liste_hfp_in_db}"
|
||||||
|
|
||||||
def set_beobachtung_ausschalten(self, dict_beobachtung_ausschalten: dict) -> None:
|
def set_beobachtung_ausschalten(self, dict_beobachtung_ausschalten: dict) -> None:
|
||||||
|
"""Schaltet ausgewählte Beobachtungen für die Ausgleichung aus.
|
||||||
|
|
||||||
|
Die zu deaktivierenden Beobachtungen werden über dict_beobachtung_ausschalten übergeben.
|
||||||
|
Der Schlüssel wird ausgewertet und das zugehörige *_ausschalten-Tupel in der Datenbank
|
||||||
|
auf 1 gesetzt (z. B. Richtungen, Zenitwinkel, Strecken, GNSS-Komponenten, Geometrisches Nivellement).
|
||||||
|
Dadurch wird die jeweilige Beobachtung in allen folgenden Iterationen nicht mehr berücksichtigt.
|
||||||
|
|
||||||
|
:param dict_beobachtung_ausschalten: Dictionary zur Steuerung, welche Beobachtungen deaktiviert werden, in der Form: {Beobachtung: "beobachtung_ausschalten"} zum ausschalten und {Beobachtung: ""} fürs beibehalten.
|
||||||
|
:type dict_beobachtung_ausschalten: dict
|
||||||
|
:return: None
|
||||||
|
:rtype: None
|
||||||
|
"""
|
||||||
con = sqlite3.connect(self.pfad_datenbank)
|
con = sqlite3.connect(self.pfad_datenbank)
|
||||||
cursor = con.cursor()
|
cursor = con.cursor()
|
||||||
|
|
||||||
for beobachtung, ausschalten in dict_beobachtung_ausschalten.items():
|
for beobachtung, ausschalten in dict_beobachtung_ausschalten.items():
|
||||||
|
# Nur in die Schleife gehen, wenn im Value des Dicts "beobachtung_ausschalten" enthalten ist.
|
||||||
if ausschalten == "beobachtung_ausschalten":
|
if ausschalten == "beobachtung_ausschalten":
|
||||||
beobachtung_gesplittet = beobachtung.split("_")
|
beobachtung_gesplittet = beobachtung.split("_")
|
||||||
|
|
||||||
|
# Distanz
|
||||||
if beobachtung_gesplittet[1] == "SD":
|
if beobachtung_gesplittet[1] == "SD":
|
||||||
#print(f"SD: {beobachtung_gesplittet}")
|
|
||||||
try:
|
try:
|
||||||
cursor.execute(f"""UPDATE Beobachtungen SET tachymeter_distanz_auschalten = 1
|
cursor.execute(f"""UPDATE Beobachtungen SET tachymeter_distanz_auschalten = 1
|
||||||
WHERE beobachtungenID = ? AND beobachtungsgruppeID = ? AND punktnummer_sp = ? AND punktnummer_zp = ?""",
|
WHERE beobachtungenID = ? AND beobachtungsgruppeID = ? AND punktnummer_sp = ? AND punktnummer_zp = ?""",
|
||||||
(beobachtung_gesplittet[0], beobachtung_gesplittet[2], beobachtung_gesplittet[3], beobachtung_gesplittet[4]))
|
(beobachtung_gesplittet[0], beobachtung_gesplittet[2], beobachtung_gesplittet[3], beobachtung_gesplittet[4]))
|
||||||
except:
|
except:
|
||||||
print(f"Die Beobachtung {beobachtung} konnte aufgrund eines Fehlers nicht ausgeschaltet werden.")
|
print(f"Die Beobachtung {beobachtung} konnte aufgrund eines Fehlers nicht ausgeschaltet werden.")
|
||||||
|
# Richtungen
|
||||||
if beobachtung_gesplittet[1] == "R":
|
if beobachtung_gesplittet[1] == "R":
|
||||||
#print(f"R: {beobachtung_gesplittet}")
|
|
||||||
try:
|
try:
|
||||||
cursor.execute(f"""UPDATE Beobachtungen SET tachymeter_richtung_ausschalten = 1
|
cursor.execute(f"""UPDATE Beobachtungen SET tachymeter_richtung_ausschalten = 1
|
||||||
WHERE beobachtungenID = ? AND beobachtungsgruppeID = ? AND punktnummer_sp = ? AND punktnummer_zp = ?""",
|
WHERE beobachtungenID = ? AND beobachtungsgruppeID = ? AND punktnummer_sp = ? AND punktnummer_zp = ?""",
|
||||||
@@ -392,8 +553,8 @@ class Datenbankzugriff:
|
|||||||
except:
|
except:
|
||||||
print(f"Die Beobachtung {beobachtung} konnte aufgrund eines Fehlers nicht ausgeschaltet werden.")
|
print(f"Die Beobachtung {beobachtung} konnte aufgrund eines Fehlers nicht ausgeschaltet werden.")
|
||||||
|
|
||||||
|
# Zenitwinkel
|
||||||
if beobachtung_gesplittet[1] == "ZW":
|
if beobachtung_gesplittet[1] == "ZW":
|
||||||
#print(f"ZW: {beobachtung_gesplittet}")
|
|
||||||
try:
|
try:
|
||||||
cursor.execute(f"""UPDATE Beobachtungen SET tachymeter_zenitwinkel_ausschalten = 1
|
cursor.execute(f"""UPDATE Beobachtungen SET tachymeter_zenitwinkel_ausschalten = 1
|
||||||
WHERE beobachtungenID = ? AND beobachtungsgruppeID = ? AND punktnummer_sp = ? AND punktnummer_zp = ?""",
|
WHERE beobachtungenID = ? AND beobachtungsgruppeID = ? AND punktnummer_sp = ? AND punktnummer_zp = ?""",
|
||||||
@@ -402,7 +563,6 @@ class Datenbankzugriff:
|
|||||||
print(f"Die Beobachtung {beobachtung} konnte aufgrund eines Fehlers nicht ausgeschaltet werden.")
|
print(f"Die Beobachtung {beobachtung} konnte aufgrund eines Fehlers nicht ausgeschaltet werden.")
|
||||||
|
|
||||||
if beobachtung_gesplittet[1] == "gnssbx":
|
if beobachtung_gesplittet[1] == "gnssbx":
|
||||||
#print(f"gnssbx: {beobachtung_gesplittet}")
|
|
||||||
try:
|
try:
|
||||||
cursor.execute(f"""UPDATE Beobachtungen SET gnss_bx_ausschalten = 1
|
cursor.execute(f"""UPDATE Beobachtungen SET gnss_bx_ausschalten = 1
|
||||||
WHERE beobachtungenID = ? AND punktnummer_sp = ? AND punktnummer_zp = ?""",
|
WHERE beobachtungenID = ? AND punktnummer_sp = ? AND punktnummer_zp = ?""",
|
||||||
@@ -410,9 +570,8 @@ class Datenbankzugriff:
|
|||||||
except:
|
except:
|
||||||
print(f"Die Beobachtung {beobachtung} konnte aufgrund eines Fehlers nicht ausgeschaltet werden.")
|
print(f"Die Beobachtung {beobachtung} konnte aufgrund eines Fehlers nicht ausgeschaltet werden.")
|
||||||
|
|
||||||
|
#GNSS-Basislinien
|
||||||
if beobachtung_gesplittet[1] == "gnssby":
|
if beobachtung_gesplittet[1] == "gnssby":
|
||||||
#print(f"gnssby: {beobachtung_gesplittet}")
|
|
||||||
try:
|
try:
|
||||||
cursor.execute(f"""UPDATE Beobachtungen SET gnss_by_ausschalten = 1
|
cursor.execute(f"""UPDATE Beobachtungen SET gnss_by_ausschalten = 1
|
||||||
WHERE beobachtungenID = ? AND punktnummer_sp = ? AND punktnummer_zp = ?""",
|
WHERE beobachtungenID = ? AND punktnummer_sp = ? AND punktnummer_zp = ?""",
|
||||||
@@ -421,7 +580,6 @@ class Datenbankzugriff:
|
|||||||
print(f"Die Beobachtung {beobachtung} konnte aufgrund eines Fehlers nicht ausgeschaltet werden.")
|
print(f"Die Beobachtung {beobachtung} konnte aufgrund eines Fehlers nicht ausgeschaltet werden.")
|
||||||
|
|
||||||
if beobachtung_gesplittet[1] == "gnssbz":
|
if beobachtung_gesplittet[1] == "gnssbz":
|
||||||
#print(f"gnssbz: {beobachtung_gesplittet}")
|
|
||||||
try:
|
try:
|
||||||
cursor.execute(f"""UPDATE Beobachtungen SET gnss_bz_ausschalten = 1
|
cursor.execute(f"""UPDATE Beobachtungen SET gnss_bz_ausschalten = 1
|
||||||
WHERE beobachtungenID = ? AND punktnummer_sp = ? AND punktnummer_zp = ?""",
|
WHERE beobachtungenID = ? AND punktnummer_sp = ? AND punktnummer_zp = ?""",
|
||||||
@@ -429,8 +587,8 @@ class Datenbankzugriff:
|
|||||||
except:
|
except:
|
||||||
print(f"Die Beobachtung {beobachtung} konnte aufgrund eines Fehlers nicht ausgeschaltet werden.")
|
print(f"Die Beobachtung {beobachtung} konnte aufgrund eines Fehlers nicht ausgeschaltet werden.")
|
||||||
|
|
||||||
|
# Geometrisches Nivellement
|
||||||
if beobachtung_gesplittet[1] == "niv":
|
if beobachtung_gesplittet[1] == "niv":
|
||||||
#print(f"niv: {beobachtung_gesplittet}")
|
|
||||||
try:
|
try:
|
||||||
cursor.execute(f"""UPDATE Beobachtungen SET niv_ausschalten = 1
|
cursor.execute(f"""UPDATE Beobachtungen SET niv_ausschalten = 1
|
||||||
WHERE beobachtungenID = ? AND punktnummer_sp = ? AND punktnummer_zp = ?""",
|
WHERE beobachtungenID = ? AND punktnummer_sp = ? AND punktnummer_zp = ?""",
|
||||||
@@ -443,12 +601,25 @@ class Datenbankzugriff:
|
|||||||
con.close()
|
con.close()
|
||||||
|
|
||||||
def set_varianzkomponente(self, liste_varianzkomponten_anpassen: list) -> None:
|
def set_varianzkomponente(self, liste_varianzkomponten_anpassen: list) -> None:
|
||||||
|
"""Passt Varianzkomponenten für die nächste Iteration an.
|
||||||
|
|
||||||
|
Für jede angegebene Varianzkomponente wird der aktuelle Datenbankwert mit einem vom Benutzer übergebenen
|
||||||
|
Faktor multipliziert und zurückgeschrieben.
|
||||||
|
|
||||||
|
:param liste_varianzkomponten_anpassen: Liste mit Einträgen (instrumenteID, beobachtungsgruppe, faktor).
|
||||||
|
:type liste_varianzkomponten_anpassen: list
|
||||||
|
:return: None
|
||||||
|
:rtype: None
|
||||||
|
"""
|
||||||
con = sqlite3.connect(self.pfad_datenbank)
|
con = sqlite3.connect(self.pfad_datenbank)
|
||||||
cursor = con.cursor()
|
cursor = con.cursor()
|
||||||
if liste_varianzkomponten_anpassen != []:
|
if liste_varianzkomponten_anpassen != []:
|
||||||
for varianzkomponente in liste_varianzkomponten_anpassen:
|
for varianzkomponente in liste_varianzkomponten_anpassen:
|
||||||
|
# Faktor für die Varianzkomponentenschätzung der aktuellen Iteration aus der Datenbank abfragen
|
||||||
varianz_db = cursor.execute(f"SELECT varianz_varianzkomponentenschaetzung FROM Varianzkomponentenschaetzung WHERE instrumenteID = ? AND beobachtungsgruppe = ?",
|
varianz_db = cursor.execute(f"SELECT varianz_varianzkomponentenschaetzung FROM Varianzkomponentenschaetzung WHERE instrumenteID = ? AND beobachtungsgruppe = ?",
|
||||||
(varianzkomponente[0], varianzkomponente[1])).fetchone()[0]
|
(varianzkomponente[0], varianzkomponente[1])).fetchone()[0]
|
||||||
|
|
||||||
|
# Faktor für die Varianzkompontenschätzung aus der Datenbank mit der Benutzereingabe multiplizieren und das Produkt in der Datenbank für die nächste Iteration speichern.
|
||||||
cursor.execute(
|
cursor.execute(
|
||||||
f"UPDATE Varianzkomponentenschaetzung SET varianz_varianzkomponentenschaetzung = ? WHERE instrumenteID = ? AND beobachtungsgruppe = ?",
|
f"UPDATE Varianzkomponentenschaetzung SET varianz_varianzkomponentenschaetzung = ? WHERE instrumenteID = ? AND beobachtungsgruppe = ?",
|
||||||
(varianz_db * varianzkomponente[2], varianzkomponente[0], varianzkomponente[1]))
|
(varianz_db * varianzkomponente[2], varianzkomponente[0], varianzkomponente[1]))
|
||||||
@@ -457,38 +628,43 @@ class Datenbankzugriff:
|
|||||||
cursor.close()
|
cursor.close()
|
||||||
con.close()
|
con.close()
|
||||||
|
|
||||||
def set_s0_apriori(self, liste_s0_apriori_anpassen: list) -> None:
|
|
||||||
con = sqlite3.connect(self.pfad_datenbank)
|
|
||||||
cursor = con.cursor()
|
|
||||||
if liste_s0_apriori_anpassen != []:
|
|
||||||
for s0_apriori in liste_s0_apriori_anpassen:
|
|
||||||
cursor.execute(
|
|
||||||
f"UPDATE Varianzkomponentenschaetzung SET s0_apriori_vorherige_iteration = ? WHERE instrumenteID = ? AND beobachtungsgruppe = ?",
|
|
||||||
(s0_apriori[2], s0_apriori[0], s0_apriori[1]))
|
|
||||||
print(f"Folgende S0_apriori wurde für die nächste Iteration gespeichert: {liste_s0_apriori_anpassen}.")
|
|
||||||
con.commit()
|
|
||||||
cursor.close()
|
|
||||||
con.close()
|
|
||||||
|
|
||||||
|
|
||||||
def get_koordinaten(self, koordinatenart: str, ausgabeart: str = "Dict") -> dict[Any, MutableDenseMatrix] | None:
|
def get_koordinaten(self, koordinatenart: str, ausgabeart: str = "Dict") -> dict[Any, MutableDenseMatrix] | None:
|
||||||
|
"""Liest Koordinaten aus der Tabelle Netzpunkte.
|
||||||
|
|
||||||
|
Abhängig von der koordinatenart werden die entsprechenden Koordinatenspalten gelesen.
|
||||||
|
Es werden nur Datensätze berücksichtigt, bei denen X, Y und Z nicht NULL sind.
|
||||||
|
Bei ausgabeart="Dict" wird ein Dictionary in der Form {Punktnummer : sympy.Matrix([X, Y, Z])} zurückgegeben.
|
||||||
|
|
||||||
|
:param koordinatenart: Art der Koordinaten ("naeherung_lh" für Koordinaten im lokalen Horizontsystem aus dem Tachymeterimport oder "naeherung_us" für Näherungskoordinaten im geozentrisch-kartesischen System).
|
||||||
|
:type koordinatenart: str
|
||||||
|
:param ausgabeart: Ausgabeformat (Standard: "Dict").
|
||||||
|
:type ausgabeart: str
|
||||||
|
:return: Dictionary mit Punktnummer → Koordinatenmatrix oder None (abhängig von ausgabeart).
|
||||||
|
:rtype: dict | None
|
||||||
|
"""
|
||||||
con = sqlite3.connect(self.pfad_datenbank)
|
con = sqlite3.connect(self.pfad_datenbank)
|
||||||
cursor = con.cursor()
|
cursor = con.cursor()
|
||||||
|
|
||||||
|
# Festlegen der Attribute, welche je nach Koordinatenart aus der Datenbank abgefragt werden sollen
|
||||||
if koordinatenart == "naeherung_lh":
|
if koordinatenart == "naeherung_lh":
|
||||||
values = "punktnummer, naeherungx_lh, naeherungy_lh, naeherungz_lh"
|
values = "punktnummer, naeherungx_lh, naeherungy_lh, naeherungz_lh"
|
||||||
elif koordinatenart == "naeherung_us":
|
elif koordinatenart == "naeherung_us":
|
||||||
values = "punktnummer, naeherungx_us, naeherungy_us, naeherungz_us"
|
values = "punktnummer, naeherungx_us, naeherungy_us, naeherungz_us"
|
||||||
|
|
||||||
|
# SQL-Abfrage
|
||||||
liste_koordinaten = cursor.execute(f"""
|
liste_koordinaten = cursor.execute(f"""
|
||||||
SELECT {values} FROM Netzpunkte;
|
SELECT {values} FROM Netzpunkte;
|
||||||
""").fetchall()
|
""").fetchall()
|
||||||
cursor.close()
|
cursor.close()
|
||||||
con.close()
|
con.close()
|
||||||
|
|
||||||
|
# Nur ausgeben, wenn für einen Punkt X, Y und Z Koordinaten vorhanden sind
|
||||||
liste_koordinaten = [
|
liste_koordinaten = [
|
||||||
koordinate for koordinate in liste_koordinaten
|
koordinate for koordinate in liste_koordinaten
|
||||||
if koordinate[1] is not None and koordinate[2] is not None and koordinate[3] is not None
|
if koordinate[1] is not None and koordinate[2] is not None and koordinate[3] is not None
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Aktuell ist nur die Ausgabeart "Dict" implementiert. Weitere Ausgabearten sind im Zuge möglicher erweiterungen zu implementieren.
|
||||||
if ausgabeart == "Dict":
|
if ausgabeart == "Dict":
|
||||||
return {
|
return {
|
||||||
koordinate[0]: sp.Matrix([
|
koordinate[0]: sp.Matrix([
|
||||||
@@ -500,6 +676,11 @@ class Datenbankzugriff:
|
|||||||
}
|
}
|
||||||
|
|
||||||
def get_normalhoehe_hfp(self) -> list[Any]:
|
def get_normalhoehe_hfp(self) -> list[Any]:
|
||||||
|
"""Liest alle Normalhöhen von amtlichen Höhenfestpunkten (HFP) aus der Tabelle Netzpunkte.
|
||||||
|
|
||||||
|
:return: Liste mit Tupeln (punktnummer, normalhoehe_hfp) für alle Punkte mit gesetzter Normalhöhe.
|
||||||
|
:rtype: list
|
||||||
|
"""
|
||||||
con = sqlite3.connect(self.pfad_datenbank)
|
con = sqlite3.connect(self.pfad_datenbank)
|
||||||
cursor = con.cursor()
|
cursor = con.cursor()
|
||||||
liste_hfp = cursor.execute("SELECT punktnummer, normalhoehe_hfp FROM Netzpunkte WHERE normalhoehe_hfp IS NOT NULL").fetchall()
|
liste_hfp = cursor.execute("SELECT punktnummer, normalhoehe_hfp FROM Netzpunkte WHERE normalhoehe_hfp IS NOT NULL").fetchall()
|
||||||
@@ -509,6 +690,16 @@ class Datenbankzugriff:
|
|||||||
|
|
||||||
|
|
||||||
def get_instrument_liste(self, typ: str) -> list:
|
def get_instrument_liste(self, typ: str) -> list:
|
||||||
|
"""Liest Instrumente eines gegebenen Typs aus der Tabelle Instrumente.
|
||||||
|
|
||||||
|
Gibt eine Liste der gefundenen Instrumente zurück. Falls keine Instrumente vorhanden sind,
|
||||||
|
wird eine Textausgabe mit verfügbaren Typen zurückgegeben.
|
||||||
|
|
||||||
|
:param typ: Instrumenttyp.
|
||||||
|
:type typ: str
|
||||||
|
:return: Liste der Instrumente oder Hinweistext, falls keine Instrumente gefunden wurden.
|
||||||
|
:rtype: list | str
|
||||||
|
"""
|
||||||
con = sqlite3.connect(self.pfad_datenbank)
|
con = sqlite3.connect(self.pfad_datenbank)
|
||||||
cursor = con.cursor()
|
cursor = con.cursor()
|
||||||
liste_instrumente = cursor.execute("SELECT * FROM Instrumente WHERE typ = ?", (typ,)).fetchall()
|
liste_instrumente = cursor.execute("SELECT * FROM Instrumente WHERE typ = ?", (typ,)).fetchall()
|
||||||
@@ -520,6 +711,11 @@ class Datenbankzugriff:
|
|||||||
return liste_instrumente
|
return liste_instrumente
|
||||||
|
|
||||||
def get_genauigkeiten_dict(self) -> dict[Any, Any]:
|
def get_genauigkeiten_dict(self) -> dict[Any, Any]:
|
||||||
|
"""Liest alle Genauigkeiten aus der Tabelle Genauigkeiten und gibt diese als Dictionary zurück.
|
||||||
|
|
||||||
|
:return: Dictionary {genauigkeitenID : restliche Tabellenwerte}.
|
||||||
|
:rtype: dict
|
||||||
|
"""
|
||||||
dict = {}
|
dict = {}
|
||||||
con = sqlite3.connect(self.pfad_datenbank)
|
con = sqlite3.connect(self.pfad_datenbank)
|
||||||
cursor = con.cursor()
|
cursor = con.cursor()
|
||||||
@@ -531,6 +727,11 @@ class Datenbankzugriff:
|
|||||||
return dict
|
return dict
|
||||||
|
|
||||||
def get_instrumenteID_beobachtungenID_dict(self) -> dict[Any, Any]:
|
def get_instrumenteID_beobachtungenID_dict(self) -> dict[Any, Any]:
|
||||||
|
"""Liest die Zuordnung BeobachtungenID → InstrumenteID aus der Tabelle Beobachtungen.
|
||||||
|
|
||||||
|
:return: Dictionary {beobachtungenID : instrumenteID}.
|
||||||
|
:rtype: dict
|
||||||
|
"""
|
||||||
dict = {}
|
dict = {}
|
||||||
con = sqlite3.connect(self.pfad_datenbank)
|
con = sqlite3.connect(self.pfad_datenbank)
|
||||||
cursor = con.cursor()
|
cursor = con.cursor()
|
||||||
@@ -541,7 +742,18 @@ class Datenbankzugriff:
|
|||||||
con.close()
|
con.close()
|
||||||
return dict
|
return dict
|
||||||
|
|
||||||
def get_beobachtungen_id_beobachtungsgruppe_standpunkt_zielpunkt(self, beobachtungsart: str) -> list[Any]:
|
def get_puntknummern_beobachtungen_tachymeter(self, beobachtungsart: str) -> list[Any]:
|
||||||
|
"""Liest Beobachtungen (ID, Gruppe, Standpunkt, Zielpunkt) für Tachymeterbeobachtungen.
|
||||||
|
|
||||||
|
Es werden nur Beobachtungen berücksichtigt, bei denen die angegebene Beobachtungsart
|
||||||
|
nicht NULL ist. Zudem werden zusätzlich werden nur Beobachtungen übergeben, die nicht
|
||||||
|
ausgeschaltet werden.
|
||||||
|
|
||||||
|
:param beobachtungsart: Name der Beobachtungsspalte.
|
||||||
|
:type beobachtungsart: str
|
||||||
|
:return: Liste von Tupeln (beobachtungenID, beobachtungsgruppeID, punktnummer_sp, punktnummer_zp).
|
||||||
|
:rtype: list
|
||||||
|
"""
|
||||||
con = sqlite3.connect(self.pfad_datenbank)
|
con = sqlite3.connect(self.pfad_datenbank)
|
||||||
cursor = con.cursor()
|
cursor = con.cursor()
|
||||||
sql_ausdruck = f"SELECT beobachtungenID, beobachtungsgruppeID, punktnummer_sp, punktnummer_zp FROM Beobachtungen WHERE {beobachtungsart} IS NOT NULL"
|
sql_ausdruck = f"SELECT beobachtungenID, beobachtungsgruppeID, punktnummer_sp, punktnummer_zp FROM Beobachtungen WHERE {beobachtungsart} IS NOT NULL"
|
||||||
@@ -557,6 +769,13 @@ class Datenbankzugriff:
|
|||||||
return liste_beobachtungen
|
return liste_beobachtungen
|
||||||
|
|
||||||
def get_beobachtungen_from_beobachtungenid(self) -> list[Any]:
|
def get_beobachtungen_from_beobachtungenid(self) -> list[Any]:
|
||||||
|
"""Liest Tachymeterbeobachtungen aus der Tabelle ``Beobachtungen``.
|
||||||
|
|
||||||
|
Es werden nur Datensätze mit gesetzter Tachymeter-Richtung berücksichtigt.
|
||||||
|
|
||||||
|
:return: Liste von Tupeln (punktnummer_sp, punktnummer_zp, beobachtungenID, beobachtungsgruppeID, tachymeter_richtung, tachymeter_zenitwinkel, tachymeter_distanz).
|
||||||
|
:rtype: list
|
||||||
|
"""
|
||||||
con = sqlite3.connect(self.pfad_datenbank)
|
con = sqlite3.connect(self.pfad_datenbank)
|
||||||
cursor = con.cursor()
|
cursor = con.cursor()
|
||||||
liste_beobachtungen = cursor.execute(f"SELECT punktnummer_sp, punktnummer_zp, beobachtungenID, beobachtungsgruppeID, tachymeter_richtung, tachymeter_zenitwinkel, tachymeter_distanz FROM Beobachtungen WHERE tachymeter_richtung IS NOT NULL").fetchall()
|
liste_beobachtungen = cursor.execute(f"SELECT punktnummer_sp, punktnummer_zp, beobachtungenID, beobachtungsgruppeID, tachymeter_richtung, tachymeter_zenitwinkel, tachymeter_distanz FROM Beobachtungen WHERE tachymeter_richtung IS NOT NULL").fetchall()
|
||||||
@@ -565,6 +784,14 @@ class Datenbankzugriff:
|
|||||||
return liste_beobachtungen
|
return liste_beobachtungen
|
||||||
|
|
||||||
def get_beobachtungen_gnssbasislinien(self) -> list[Any]:
|
def get_beobachtungen_gnssbasislinien(self) -> list[Any]:
|
||||||
|
"""Liest GNSS-Basislinienbeobachtungen inklusive Kovarianzangaben.
|
||||||
|
|
||||||
|
Es werden nur Datensätze zurückgegeben, bei denen alle benötigten GNSS-Felder (bx, by, bz, s0
|
||||||
|
und Kovarianzelemente) nicht NULL sind.
|
||||||
|
|
||||||
|
:return: Liste von Tupeln mit GNSS-Basisliniendaten und Kovarianzelementen.
|
||||||
|
:rtype: list
|
||||||
|
"""
|
||||||
con = sqlite3.connect(self.pfad_datenbank)
|
con = sqlite3.connect(self.pfad_datenbank)
|
||||||
cursor = con.cursor()
|
cursor = con.cursor()
|
||||||
liste_beobachtungen = cursor.execute(f"SELECT beobachtungenID, punktnummer_sp, punktnummer_zp, gnss_bx, gnss_by, gnss_bz, gnss_s0, gnss_cxx, gnss_cxy, gnss_cxz, gnss_cyy, gnss_cyz, gnss_czz FROM Beobachtungen WHERE gnss_bx IS NOT NULL AND gnss_by IS NOT NULL AND gnss_bz IS NOT NULL AND gnss_s0 IS NOT NULL AND gnss_cxx IS NOT NULL AND gnss_cxy IS NOT NULL AND gnss_cxz IS NOT NULL AND gnss_cyy IS NOT NULL AND gnss_cyz IS NOT NULL AND gnss_czz IS NOT NULL").fetchall()
|
liste_beobachtungen = cursor.execute(f"SELECT beobachtungenID, punktnummer_sp, punktnummer_zp, gnss_bx, gnss_by, gnss_bz, gnss_s0, gnss_cxx, gnss_cxy, gnss_cxz, gnss_cyy, gnss_cyz, gnss_czz FROM Beobachtungen WHERE gnss_bx IS NOT NULL AND gnss_by IS NOT NULL AND gnss_bz IS NOT NULL AND gnss_s0 IS NOT NULL AND gnss_cxx IS NOT NULL AND gnss_cxy IS NOT NULL AND gnss_cxz IS NOT NULL AND gnss_cyy IS NOT NULL AND gnss_cyz IS NOT NULL AND gnss_czz IS NOT NULL").fetchall()
|
||||||
@@ -573,6 +800,15 @@ class Datenbankzugriff:
|
|||||||
return liste_beobachtungen
|
return liste_beobachtungen
|
||||||
|
|
||||||
def get_beobachtungen_nivellement(self) -> list[Any]:
|
def get_beobachtungen_nivellement(self) -> list[Any]:
|
||||||
|
"""Liest Nivellementbeobachtungen aus der Tabelle Beobachtungen.
|
||||||
|
|
||||||
|
Es werden nur Datensätze zurückgegeben, bei denen alle Nivellementfelder Daten enthalten
|
||||||
|
und niv_ausschalten = 0 ist, also diese Beobachtung in der Ausgleichung verwendet wird.
|
||||||
|
|
||||||
|
:return: Liste von Tupeln (beobachtungenID, punktnummer_sp, punktnummer_zp, niv_dh, niv_strecke, niv_anz_standpkte).
|
||||||
|
:rtype: list
|
||||||
|
"""
|
||||||
|
|
||||||
con = sqlite3.connect(self.pfad_datenbank)
|
con = sqlite3.connect(self.pfad_datenbank)
|
||||||
cursor = con.cursor()
|
cursor = con.cursor()
|
||||||
liste_beobachtungen = cursor.execute(f"SELECT beobachtungenID, punktnummer_sp, punktnummer_zp, niv_dh, niv_strecke, niv_anz_standpkte FROM Beobachtungen WHERE niv_dh IS NOT NULL AND niv_strecke IS NOT NULL AND niv_anz_standpkte IS NOT NULL AND niv_ausschalten = 0").fetchall()
|
liste_beobachtungen = cursor.execute(f"SELECT beobachtungenID, punktnummer_sp, punktnummer_zp, niv_dh, niv_strecke, niv_anz_standpkte FROM Beobachtungen WHERE niv_dh IS NOT NULL AND niv_strecke IS NOT NULL AND niv_anz_standpkte IS NOT NULL AND niv_ausschalten = 0").fetchall()
|
||||||
@@ -581,6 +817,15 @@ class Datenbankzugriff:
|
|||||||
return liste_beobachtungen
|
return liste_beobachtungen
|
||||||
|
|
||||||
def get_datumskoordinate(self) -> list[Any]:
|
def get_datumskoordinate(self) -> list[Any]:
|
||||||
|
"""Liest die aktuell gesetzten Datumskoordinaten aus der Tabelle Netzpunkte.
|
||||||
|
|
||||||
|
Die Rückgabe erfolgt als Liste von Symbolnamen der Form "X<punktnummer>", "Y<punktnummer>"
|
||||||
|
und "Z<punktnummer>".
|
||||||
|
|
||||||
|
:return: Liste der Datumskoordinatenbezeichner.
|
||||||
|
:rtype: list
|
||||||
|
"""
|
||||||
|
# Abragen der Koordinaten mit einer 1 im Attribut datumskoordinate_*, da diese für die Lagerung verwendet werden sollen.
|
||||||
con = sqlite3.connect(self.pfad_datenbank)
|
con = sqlite3.connect(self.pfad_datenbank)
|
||||||
cursor = con.cursor()
|
cursor = con.cursor()
|
||||||
liste_datumskoordinate_x = cursor.execute(
|
liste_datumskoordinate_x = cursor.execute(
|
||||||
@@ -593,6 +838,7 @@ class Datenbankzugriff:
|
|||||||
cursor.close()
|
cursor.close()
|
||||||
con.close()
|
con.close()
|
||||||
|
|
||||||
|
# Hinzufügen der jeweiligen Punktnummer als Symbol mit X, Y oder Z-Präfix zur Rückgabeliste
|
||||||
liste_datumskoordinaten = []
|
liste_datumskoordinaten = []
|
||||||
if liste_datumskoordinate_x != []:
|
if liste_datumskoordinate_x != []:
|
||||||
for datumskoordinate in liste_datumskoordinate_x:
|
for datumskoordinate in liste_datumskoordinate_x:
|
||||||
@@ -610,6 +856,15 @@ class Datenbankzugriff:
|
|||||||
return liste_datumskoordinaten
|
return liste_datumskoordinaten
|
||||||
|
|
||||||
def get_stabw_AA_Netzpunkte(self) -> dict[Any, Any]:
|
def get_stabw_AA_Netzpunkte(self) -> dict[Any, Any]:
|
||||||
|
"""Liest Vorinformationen zur Standardabweichung der Netzpunkte aus der Tabelle Netzpunkte.
|
||||||
|
|
||||||
|
Die Rückgabe erfolgt als Dictionary mit Schlüsseln der Form "StabwAA_X<punktnummer>",
|
||||||
|
"StabwAA_Y<punktnummer>" und "StabwAA_Z<punktnummer>".
|
||||||
|
|
||||||
|
:return: Dictionary der Vorinformationen zur Standardabweichung je Koordinate.
|
||||||
|
:rtype: dict
|
||||||
|
"""
|
||||||
|
# Abfragen aller Punktnummern und s0 apriori Vorinformationen aus der Datenbank, wenn die Vorinformationen vorliegen.
|
||||||
con = sqlite3.connect(self.pfad_datenbank)
|
con = sqlite3.connect(self.pfad_datenbank)
|
||||||
cursor = con.cursor()
|
cursor = con.cursor()
|
||||||
liste_stabwAA_x = cursor.execute(f"SELECT punktnummer, stabw_vorinfo_x FROM Netzpunkte WHERE stabw_vorinfo_x IS NOT NULL").fetchall()
|
liste_stabwAA_x = cursor.execute(f"SELECT punktnummer, stabw_vorinfo_x FROM Netzpunkte WHERE stabw_vorinfo_x IS NOT NULL").fetchall()
|
||||||
@@ -621,6 +876,7 @@ class Datenbankzugriff:
|
|||||||
cursor.close()
|
cursor.close()
|
||||||
con.close()
|
con.close()
|
||||||
|
|
||||||
|
# Erstellen des Rückgabedictionaries.
|
||||||
dict_stabwAA = {}
|
dict_stabwAA = {}
|
||||||
for stabwAA_x in liste_stabwAA_x:
|
for stabwAA_x in liste_stabwAA_x:
|
||||||
punktnummer = str(stabwAA_x[0]).strip()
|
punktnummer = str(stabwAA_x[0]).strip()
|
||||||
@@ -640,6 +896,17 @@ class Datenbankzugriff:
|
|||||||
return dict_stabwAA
|
return dict_stabwAA
|
||||||
|
|
||||||
def get_gnss_beobachtungen_punktnummern(self, gnss_komponente: str) -> list[Any]:
|
def get_gnss_beobachtungen_punktnummern(self, gnss_komponente: str) -> list[Any]:
|
||||||
|
"""Liest GNSS-Beobachtungen (ID, Standpunkt, Zielpunkt) für eine GNSS-Komponente.
|
||||||
|
|
||||||
|
Es werden nur Datensätze berücksichtigt, bei denen die geforderte Komponente sowie alle
|
||||||
|
zugehörigen GNSS-Genauigkeitsangaben (s0 und Kovarianzelemente) vorhanden sind.
|
||||||
|
Zusätzlich wird das jeweilige Ausschalt-Attribut berücksichtigt.
|
||||||
|
|
||||||
|
:param gnss_komponente: Name der GNSS-Basislinienkomponente (z. B. "gnss_bx", "gnss_by", "gnss_bz").
|
||||||
|
:type gnss_komponente: str
|
||||||
|
:return: Liste von Tupeln (beobachtungenID, punktnummer_sp, punktnummer_zp).
|
||||||
|
:rtype: list
|
||||||
|
"""
|
||||||
con = sqlite3.connect(self.pfad_datenbank)
|
con = sqlite3.connect(self.pfad_datenbank)
|
||||||
cursor = con.cursor()
|
cursor = con.cursor()
|
||||||
sql = f"""SELECT beobachtungenID, punktnummer_sp, punktnummer_zp
|
sql = f"""SELECT beobachtungenID, punktnummer_sp, punktnummer_zp
|
||||||
@@ -662,6 +929,15 @@ class Datenbankzugriff:
|
|||||||
return liste_gnss_beobachtungen
|
return liste_gnss_beobachtungen
|
||||||
|
|
||||||
def get_nivellement_beobachtungen_punktnummern(self) -> list[Any]:
|
def get_nivellement_beobachtungen_punktnummern(self) -> list[Any]:
|
||||||
|
"""Liest Beobachtungen vom geometrischen Nivellement (ID, Standpunkt, Zielpunkt) aus der Tabelle Beobachtungen.
|
||||||
|
|
||||||
|
Es werden nur Datensätze berücksichtigt, bei denen alle Attribute gesetzt sind
|
||||||
|
und niv_ausschalten = 0 ist.
|
||||||
|
|
||||||
|
:return: Liste von Tupeln (beobachtungenID, punktnummer_sp, punktnummer_zp).
|
||||||
|
:rtype: list
|
||||||
|
"""
|
||||||
|
|
||||||
con = sqlite3.connect(self.pfad_datenbank)
|
con = sqlite3.connect(self.pfad_datenbank)
|
||||||
cursor = con.cursor()
|
cursor = con.cursor()
|
||||||
liste_nivellement_beobachtungen = cursor.execute(f"SELECT beobachtungenID, punktnummer_sp, punktnummer_zp FROM Beobachtungen WHERE niv_dh IS NOT NULL AND niv_strecke IS NOT NULL AND niv_anz_standpkte IS NOT NULL AND niv_ausschalten = 0").fetchall()
|
liste_nivellement_beobachtungen = cursor.execute(f"SELECT beobachtungenID, punktnummer_sp, punktnummer_zp FROM Beobachtungen WHERE niv_dh IS NOT NULL AND niv_strecke IS NOT NULL AND niv_anz_standpkte IS NOT NULL AND niv_ausschalten = 0").fetchall()
|
||||||
@@ -670,6 +946,11 @@ class Datenbankzugriff:
|
|||||||
return liste_nivellement_beobachtungen
|
return liste_nivellement_beobachtungen
|
||||||
|
|
||||||
def get_varianzkomponentenschaetzung(self) -> list[Any]:
|
def get_varianzkomponentenschaetzung(self) -> list[Any]:
|
||||||
|
"""Liest alle Einträge der Varianzkomponentenschätzung aus der Datenbank.
|
||||||
|
|
||||||
|
:return: Liste von Tupeln (varianzkomponenteID, instrumenteID, beobachtungsgruppe, varianz_varianzkomponentenschaetzung).
|
||||||
|
:rtype: list
|
||||||
|
"""
|
||||||
con = sqlite3.connect(self.pfad_datenbank)
|
con = sqlite3.connect(self.pfad_datenbank)
|
||||||
cursor = con.cursor()
|
cursor = con.cursor()
|
||||||
liste_varianzkomponenten = cursor.execute(f"SELECT varianzkomponenteID, instrumenteID, beobachtungsgruppe, varianz_varianzkomponentenschaetzung FROM Varianzkomponentenschaetzung").fetchall()
|
liste_varianzkomponenten = cursor.execute(f"SELECT varianzkomponenteID, instrumenteID, beobachtungsgruppe, varianz_varianzkomponentenschaetzung FROM Varianzkomponentenschaetzung").fetchall()
|
||||||
@@ -677,12 +958,4 @@ class Datenbankzugriff:
|
|||||||
con.close()
|
con.close()
|
||||||
return liste_varianzkomponenten
|
return liste_varianzkomponenten
|
||||||
|
|
||||||
def get_s0_apriori(self) -> list[Any]:
|
|
||||||
con = sqlite3.connect(self.pfad_datenbank)
|
|
||||||
cursor = con.cursor()
|
|
||||||
liste_s0_apriori = cursor.execute(f"SELECT varianzkomponenteID, instrumenteID, beobachtungsgruppe, varianz_varianzkomponentenschaetzung, s0_apriori_vorherige_iteration FROM Varianzkomponentenschaetzung").fetchall()
|
|
||||||
cursor.close()
|
|
||||||
con.close()
|
|
||||||
return liste_s0_apriori
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
87
Export.py
87
Export.py
@@ -1,19 +1,41 @@
|
|||||||
import csv
|
import csv
|
||||||
|
from datetime import datetime
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import webbrowser
|
import webbrowser
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
|
|
||||||
class Export:
|
class Export:
|
||||||
def __init__(self) -> None:
|
"""Hilfsfunktionen zum Exportieren von Ergebnissen und Protokollen.
|
||||||
pass
|
|
||||||
|
Die Klasse stellt Methoden zur Verfügung für:
|
||||||
|
|
||||||
|
- Export von Matrizen in CSV-Dateien,
|
||||||
|
- Export von Ausgleichungsergebnissen (Skalare und Matrizen) in CSV-Dateien,
|
||||||
|
- Erzeugung und Speicherung eines HTML-Protokolls inklusive automatischem Öffnen im Browser.
|
||||||
|
"""
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def matrix_to_csv(dateiname: str, liste_spaltenbeschriftung: list, liste_zeilenbeschriftung: list, Matrix: np.matrix | sp.Matrix,
|
def matrix_to_csv(dateiname: str, liste_spaltenbeschriftung: list, liste_zeilenbeschriftung: list, Matrix: np.matrix | sp.Matrix,
|
||||||
beschriftung_kopfzeile: object = "") -> None:
|
beschriftung_kopfzeile: object = "") -> None:
|
||||||
|
"""Schreibt eine Matrix mit Zeilen- und Spaltenbeschriftungen in eine CSV-Datei.
|
||||||
|
|
||||||
|
Die Ausgabe erfolgt mit Semikolon als Trennzeichen. Die Kopfzeile enthält optional eine
|
||||||
|
zusätzliche Beschriftung sowie Spaltenbeschriftungen.
|
||||||
|
Zudem werden Zeilenbeschriftungen durchgeführt.
|
||||||
|
|
||||||
|
:param dateiname: Pfad zu schreibenden CSV-Datei.
|
||||||
|
:type dateiname: str
|
||||||
|
:param liste_spaltenbeschriftung: Liste der Spaltenbeschriftungen.
|
||||||
|
:type liste_spaltenbeschriftung: list
|
||||||
|
:param liste_zeilenbeschriftung: Liste der Zeilenbeschriftungen.
|
||||||
|
:type liste_zeilenbeschriftung: list
|
||||||
|
:param Matrix: Zu exportierende Matrix.
|
||||||
|
:type Matrix: np.matrix | sp.Matrix
|
||||||
|
:param beschriftung_kopfzeile: Optionaler Eintrag in der linken oberen Zelle der Kopfzeile.
|
||||||
|
:type beschriftung_kopfzeile: object
|
||||||
|
:return: None
|
||||||
|
:rtype: None
|
||||||
|
"""
|
||||||
with open(dateiname, "w", newline="", encoding="utf-8") as csvfile:
|
with open(dateiname, "w", newline="", encoding="utf-8") as csvfile:
|
||||||
writer = csv.writer(csvfile, delimiter=";")
|
writer = csv.writer(csvfile, delimiter=";")
|
||||||
|
|
||||||
@@ -26,6 +48,7 @@ class Export:
|
|||||||
zeile_als_text = [zeilenbeschriftung]
|
zeile_als_text = [zeilenbeschriftung]
|
||||||
for eintrag in zeile:
|
for eintrag in zeile:
|
||||||
try:
|
try:
|
||||||
|
# Dezimaltrenner von Punkt in Komma ändern, weil Python und Excel andere Konventionen vertreten.
|
||||||
eintrag_text = str(eintrag).replace(".", ",")
|
eintrag_text = str(eintrag).replace(".", ",")
|
||||||
try:
|
try:
|
||||||
eintrag_text = float(eintrag_text)
|
eintrag_text = float(eintrag_text)
|
||||||
@@ -36,46 +59,24 @@ class Export:
|
|||||||
zeile_als_text.append(eintrag_text)
|
zeile_als_text.append(eintrag_text)
|
||||||
writer.writerow(zeile_als_text)
|
writer.writerow(zeile_als_text)
|
||||||
|
|
||||||
@staticmethod
|
def speichere_html_protokoll(metadaten: dict, ergebnisse: dict) -> None:
|
||||||
def ausgleichung_to_datei(dateiname: str, dict_ausgleichung: dict) -> None:
|
"""Erzeugt ein HTML-Protokoll der Ausgleichungsergebnisse und speichert es als Datei.
|
||||||
with open(dateiname, "w", newline="", encoding="utf-8") as csvfile:
|
|
||||||
writer = csv.writer(csvfile, delimiter=";")
|
|
||||||
|
|
||||||
writer.writerow(["Parameter", "Wert"])
|
Es wird der Unterordner Protokolle angelegt, falls dieser noch nicht existiert.
|
||||||
|
Das Protokoll wird als HTML-Datei gespeichert und anschließend im Browser geöffnet.
|
||||||
|
|
||||||
for key, value in dict_ausgleichung.items():
|
Erwartete Schlüssel in metadaten sind u. a. "projekt", "bearbeiter" und "datum".
|
||||||
|
Erwartete Schlüssel in ergebnisse sind u. a. "df_globaltest", "df_redundanz",
|
||||||
|
"df_ellipsen", "df_konfidenzellipsen", "df_koordinaten_geozentrisch_kartesisch"
|
||||||
|
und "df_koordinaten_utm". Die zugehörigen Werte müssen die Methode to_html bereitstellen.
|
||||||
|
|
||||||
if hasattr(value, "tolist"):
|
:param metadaten: Dictionary mit Metadaten zum Protokoll.
|
||||||
rows = value.rows
|
:type metadaten: dict
|
||||||
cols = value.cols
|
:param ergebnisse: Dictionary mit Ergebnisobjekten (z. B. DataFrames) zur HTML-Ausgabe.
|
||||||
|
:type ergebnisse: dict
|
||||||
writer.writerow([key, f"Matrix {rows}x{cols}"])
|
:return: None
|
||||||
|
:rtype: None
|
||||||
for i, zeile in enumerate(value.tolist()):
|
"""
|
||||||
zeile_als_text = [f"{key}_zeile_{i+1}"]
|
|
||||||
for eintrag in zeile:
|
|
||||||
try:
|
|
||||||
eintrag_float = float(eintrag)
|
|
||||||
eintrag_text = f"{eintrag_float}".replace(".", ",")
|
|
||||||
try:
|
|
||||||
eintrag_text = float(eintrag_text)
|
|
||||||
except:
|
|
||||||
eintrag_text = eintrag_text
|
|
||||||
except Exception:
|
|
||||||
eintrag_text = str(eintrag)
|
|
||||||
zeile_als_text.append(eintrag_text)
|
|
||||||
writer.writerow(zeile_als_text)
|
|
||||||
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
value_float = float(value)
|
|
||||||
value_text = f"{value_float}".replace(".", ",")
|
|
||||||
except Exception:
|
|
||||||
value_text = str(value)
|
|
||||||
|
|
||||||
writer.writerow([key, value_text])
|
|
||||||
|
|
||||||
def speichere_html_protokoll(metadaten, ergebnisse):
|
|
||||||
# Pfad für den Ordner erstellen
|
# Pfad für den Ordner erstellen
|
||||||
ordner = "Protokolle"
|
ordner = "Protokolle"
|
||||||
if not os.path.exists(ordner):
|
if not os.path.exists(ordner):
|
||||||
|
|||||||
@@ -1,30 +1,55 @@
|
|||||||
from typing import Any
|
import numpy as np
|
||||||
|
|
||||||
from numpy import ndarray, dtype
|
from numpy import ndarray, dtype
|
||||||
|
import sympy as sp
|
||||||
from sympy import MutableDenseMatrix
|
from sympy import MutableDenseMatrix
|
||||||
from sympy.matrices.expressions.matexpr import MatrixElement
|
from sympy.matrices.expressions.matexpr import MatrixElement
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from Datenbank import Datenbankzugriff
|
|
||||||
import sympy as sp
|
|
||||||
from Export import Export
|
|
||||||
from Berechnungen import Berechnungen
|
from Berechnungen import Berechnungen
|
||||||
import numpy as np
|
from Datenbank import Datenbankzugriff
|
||||||
import importlib
|
from Export import Export
|
||||||
from Koordinatentransformationen import Transformationen
|
from Koordinatentransformationen import Transformationen
|
||||||
from pathlib import Path
|
|
||||||
import pandas as pd
|
|
||||||
import numpy as np
|
|
||||||
import sympy as sp
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class FunktionalesModell:
|
class FunktionalesModell:
|
||||||
|
"""Aufstellung von Beobachtungsgleichungen und der Jacobi-Matrix.
|
||||||
|
|
||||||
|
Die Klasse stellt Methoden zur Verfügung für:
|
||||||
|
|
||||||
|
- symbolische Aufstellung der Jacobi-Matrix für Tachymeter-, GNSS- und Nivellementbeobachtungen,
|
||||||
|
- numerische Auswertung der symbolischen Jacobi-Matrix über Substitutionen und Lambdify,
|
||||||
|
- Aufbau des Beobachtungsvektors (numerisch) und des Näherungs-Beobachtungsvektors (symbolisch/numerisch),
|
||||||
|
- Aufbau des Unbekanntenvektors (symbolisch/numerisch) inklusive Iterationsfortschreibung,
|
||||||
|
- Berechnung des Verbesserungsvektors dl = l − f(x0) mit Winkel-Normierung für Richtungen,
|
||||||
|
- Erstellung und Aktualisierung eines Substitutions-Dictionaries für alle im Modell verwendeten Symbole.
|
||||||
|
|
||||||
|
Die grundlegende Funktionsweise der Matrixdefinition lautet:
|
||||||
|
|
||||||
|
1) Einmaligen Aufbauen der Symbolischen Matrix einmalig
|
||||||
|
2) In jeder Iteration Substituieren der Symbolischen Matrizen in Numerische np.asarrays
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, pfad_datenbank: str, a: float, b: float, pfad_tif_quasigeoidundolation: str = None) -> None:
|
def __init__(self, pfad_datenbank: str, a: float, b: float, pfad_tif_quasigeoidundolation: str = None) -> None:
|
||||||
|
"""Initialisiert das funktionale Modell.
|
||||||
|
|
||||||
|
Legt die Ellipsoidparameter a und b fest, initialisiert Hilfsklassen (Berechnungen, Datenbankzugriff, Transformationen)
|
||||||
|
und erzeugt das Substitutions-Dictionary für das geozentrisch-kartesische System.
|
||||||
|
|
||||||
|
:param pfad_datenbank: Pfad zur SQLite-Datenbank.
|
||||||
|
:type pfad_datenbank: str
|
||||||
|
:param a: Große Halbachse a des Referenzellipsoids in Meter.
|
||||||
|
:type a: float
|
||||||
|
:param b: Kleine Halbachse b des Referenzellipsoids in Meter.
|
||||||
|
:type b: float
|
||||||
|
:param pfad_tif_quasigeoidundolation: Pfad zu Quasigeoidundulationsdaten als GeoTIFF vom BKG für Transformationen (optional).
|
||||||
|
:type pfad_tif_quasigeoidundolation: str | None
|
||||||
|
:return: None
|
||||||
|
:rtype: None
|
||||||
|
"""
|
||||||
self.pfad_datenbank = pfad_datenbank
|
self.pfad_datenbank = pfad_datenbank
|
||||||
self.a = a
|
self.a = a
|
||||||
self.b = b
|
self.b = b
|
||||||
self.berechnungen = Berechnungen(self.a, self.b)
|
self.berechnungen = Berechnungen(self.a, self.b)
|
||||||
|
self.db_zugriff = Datenbankzugriff(self.pfad_datenbank)
|
||||||
self.trafos = Transformationen(pfad_datenbank)
|
self.trafos = Transformationen(pfad_datenbank)
|
||||||
self.pfad_tif_quasigeoidundolation = pfad_tif_quasigeoidundolation
|
self.pfad_tif_quasigeoidundolation = pfad_tif_quasigeoidundolation
|
||||||
self.substitutionen_dict = self.dict_substitutionen_uebergeordnetes_system()
|
self.substitutionen_dict = self.dict_substitutionen_uebergeordnetes_system()
|
||||||
@@ -35,25 +60,43 @@ class FunktionalesModell:
|
|||||||
self.func_u0 = None
|
self.func_u0 = None
|
||||||
self.liste_beobachtungsvektor_symbolisch = None
|
self.liste_beobachtungsvektor_symbolisch = None
|
||||||
|
|
||||||
|
|
||||||
def jacobi_matrix_symbolisch(self, datumsfestlegung: str = None, liste_unbekannte_datumsfestlegung: list = None) -> tuple[MutableDenseMatrix | MatrixElement | list[Any] | Any, list[Any], list[Any]] | None:
|
def jacobi_matrix_symbolisch(self, datumsfestlegung: str = None, liste_unbekannte_datumsfestlegung: list = None) -> tuple[MutableDenseMatrix | MatrixElement | list[Any] | Any, list[Any], list[Any]] | None:
|
||||||
#liste_beobachtungsarten = ["tachymeter_distanz", "tachymeter_richtung", "tachymeter_zenitwinkel"]
|
"""Erstellt die symbolische Jacobi-Matrix (A-Matrix) für die Ausgleichung.
|
||||||
|
|
||||||
|
Es werden die in der Datenbank vorhandenen Beobachtungen (Tachymeter: Distanz/Richtung/Zenitwinkel,
|
||||||
|
GNSS: Basislinienkomponenten bx/by/bz, Geometrisches Nivellement: dh) eingelesen, daraus die benötigten Unbekannten
|
||||||
|
(X, Y, Z je Punkt sowie Orientierungen O je Richtungs-Beobachtungsgruppe) aufgebaut und anschließend
|
||||||
|
die symbolischen Gleichungen zur Jacobi-Matrix zusammengesetzt.
|
||||||
|
|
||||||
|
Bei datumsfestlegung == "weiche Lagerung" werden Unbekannte so umsortiert, dass die zur Lagerung
|
||||||
|
zu verwendenden Unbekannten rechts stehen. Zusätzlich werden die Gleichungen der Anschlusspunkte (lA_*) als
|
||||||
|
Zusatzzeilen an A angehängt.
|
||||||
|
|
||||||
|
Die symbolische Jacobi-Matrix wird als CSV in Zwischenergebnisse\\Jacobi_Matrix_Symbolisch.csv exportiert.
|
||||||
|
|
||||||
|
:param datumsfestlegung: Art der Datumsfestlegung (aktuell nur "weiche Lagerung").
|
||||||
|
:type datumsfestlegung: str | None
|
||||||
|
:param liste_unbekannte_datumsfestlegung: Liste der Symbolnamen der zur Lagerung zu verwendenden Unbekannten (z. B. ["X100", "Y100", "Z100"]).
|
||||||
|
:type liste_unbekannte_datumsfestlegung: list | None
|
||||||
|
:return: Tupel aus symbolischer Jacobi-Matrix, Liste der Unbekannten (Symbole) und Liste der Zeilenbeschriftungen (Beobachtungskennungen). Falls keine Beobachtungen vorliegen: None.
|
||||||
|
:rtype: tuple[MutableDenseMatrix | MatrixElement | list[Any] | Any, list[Any], list[Any]] | None
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Über die liste_beobachtungsarten wird festgelegt, welche Beobachtungsarten in der Ausgleichung verwendet werden.
|
||||||
liste_beobachtungsarten = ["tachymeter_distanz", "tachymeter_richtung", "tachymeter_zenitwinkel", "gnss_basislinien", "geometrisches_nivellement"]
|
liste_beobachtungsarten = ["tachymeter_distanz", "tachymeter_richtung", "tachymeter_zenitwinkel", "gnss_basislinien", "geometrisches_nivellement"]
|
||||||
#liste_beobachtungsarten = ["tachymeter_distanz", "tachymeter_richtung", "tachymeter_zenitwinkel", "gnss_basislinien"]
|
|
||||||
db_zugriff = Datenbankzugriff(self.pfad_datenbank)
|
|
||||||
|
|
||||||
|
# Initialisieren der zu befüllenden Listen
|
||||||
liste_beobachtungen_rohdaten_gnssbasislinien = []
|
liste_beobachtungen_rohdaten_gnssbasislinien = []
|
||||||
liste_beobachtungen_rohdaten_tachymeter = []
|
liste_beobachtungen_rohdaten_tachymeter = []
|
||||||
liste_beobachtungen_rohdaten_nivellement = []
|
liste_beobachtungen_rohdaten_nivellement = []
|
||||||
liste_punktnummern =[]
|
liste_punktnummern =[]
|
||||||
|
|
||||||
liste_orientierungsunbekannte = []
|
liste_orientierungsunbekannte = []
|
||||||
|
|
||||||
|
# Für jede Beobachtungsart wird aus der Datenbank abgefragt, welche Beobachtungen vorliegen. Von diesen werden der Stand- und Zielpunkt gespeichert, um diese im weiteren Verlauf dieser Methode zu verarbeiten.
|
||||||
for beobachtungsart in liste_beobachtungsarten:
|
for beobachtungsart in liste_beobachtungsarten:
|
||||||
#Tachymeter Block
|
#Tachymeter Block
|
||||||
if beobachtungsart.startswith("tachymeter"):
|
if beobachtungsart.startswith("tachymeter"):
|
||||||
liste_id_standpunkt_zielpunkt = db_zugriff.get_beobachtungen_id_beobachtungsgruppe_standpunkt_zielpunkt(beobachtungsart)
|
liste_id_standpunkt_zielpunkt = self.db_zugriff.get_puntknummern_beobachtungen_tachymeter(beobachtungsart)
|
||||||
|
|
||||||
for beobachtungenID, beobachtungsgruppeID, standpunkt, zielpunkt in liste_id_standpunkt_zielpunkt:
|
for beobachtungenID, beobachtungsgruppeID, standpunkt, zielpunkt in liste_id_standpunkt_zielpunkt:
|
||||||
liste_beobachtungen_rohdaten_tachymeter.append(
|
liste_beobachtungen_rohdaten_tachymeter.append(
|
||||||
@@ -68,23 +111,10 @@ class FunktionalesModell:
|
|||||||
if beobachtungsart == "tachymeter_richtung":
|
if beobachtungsart == "tachymeter_richtung":
|
||||||
if beobachtungsgruppeID not in liste_orientierungsunbekannte:
|
if beobachtungsgruppeID not in liste_orientierungsunbekannte:
|
||||||
liste_orientierungsunbekannte.append(beobachtungsgruppeID)
|
liste_orientierungsunbekannte.append(beobachtungsgruppeID)
|
||||||
#GNSS Block
|
|
||||||
#if beobachtungsart == "gnss_basislinien":
|
|
||||||
# liste_id_standpunkt_zielpunkt = db_zugriff.get_gnss_beobachtungen_punktnummern()
|
|
||||||
|
|
||||||
# for beobachtungenID, standpunkt, zielpunkt in liste_id_standpunkt_zielpunkt:
|
|
||||||
# standpunkt = str(standpunkt).strip()
|
|
||||||
# zielpunkt = str(zielpunkt).strip()
|
|
||||||
# liste_beobachtungen_rohdaten_gnssbasislinien.append((beobachtungsart, beobachtungenID, standpunkt, zielpunkt))
|
|
||||||
|
|
||||||
# if standpunkt not in liste_punktnummern:
|
|
||||||
# liste_punktnummern.append(standpunkt)
|
|
||||||
# if zielpunkt not in liste_punktnummern:
|
|
||||||
# liste_punktnummern.append(zielpunkt)
|
|
||||||
|
|
||||||
#GNSS Block
|
#GNSS Block
|
||||||
if beobachtungsart == "gnss_basislinien":
|
if beobachtungsart == "gnss_basislinien":
|
||||||
liste_id_standpunkt_zielpunkt = db_zugriff.get_gnss_beobachtungen_punktnummern("gnss_bx")
|
liste_id_standpunkt_zielpunkt = self.db_zugriff.get_gnss_beobachtungen_punktnummern("gnss_bx")
|
||||||
for beobachtungenID, standpunkt, zielpunkt in liste_id_standpunkt_zielpunkt:
|
for beobachtungenID, standpunkt, zielpunkt in liste_id_standpunkt_zielpunkt:
|
||||||
standpunkt = str(standpunkt).strip()
|
standpunkt = str(standpunkt).strip()
|
||||||
zielpunkt = str(zielpunkt).strip()
|
zielpunkt = str(zielpunkt).strip()
|
||||||
@@ -94,7 +124,7 @@ class FunktionalesModell:
|
|||||||
if zielpunkt not in liste_punktnummern:
|
if zielpunkt not in liste_punktnummern:
|
||||||
liste_punktnummern.append(zielpunkt)
|
liste_punktnummern.append(zielpunkt)
|
||||||
|
|
||||||
liste_id_standpunkt_zielpunkt = db_zugriff.get_gnss_beobachtungen_punktnummern("gnss_by")
|
liste_id_standpunkt_zielpunkt = self.db_zugriff.get_gnss_beobachtungen_punktnummern("gnss_by")
|
||||||
for beobachtungenID, standpunkt, zielpunkt in liste_id_standpunkt_zielpunkt:
|
for beobachtungenID, standpunkt, zielpunkt in liste_id_standpunkt_zielpunkt:
|
||||||
standpunkt = str(standpunkt).strip()
|
standpunkt = str(standpunkt).strip()
|
||||||
zielpunkt = str(zielpunkt).strip()
|
zielpunkt = str(zielpunkt).strip()
|
||||||
@@ -104,7 +134,7 @@ class FunktionalesModell:
|
|||||||
if zielpunkt not in liste_punktnummern:
|
if zielpunkt not in liste_punktnummern:
|
||||||
liste_punktnummern.append(zielpunkt)
|
liste_punktnummern.append(zielpunkt)
|
||||||
|
|
||||||
liste_id_standpunkt_zielpunkt = db_zugriff.get_gnss_beobachtungen_punktnummern("gnss_bz")
|
liste_id_standpunkt_zielpunkt = self.db_zugriff.get_gnss_beobachtungen_punktnummern("gnss_bz")
|
||||||
for beobachtungenID, standpunkt, zielpunkt in liste_id_standpunkt_zielpunkt:
|
for beobachtungenID, standpunkt, zielpunkt in liste_id_standpunkt_zielpunkt:
|
||||||
standpunkt = str(standpunkt).strip()
|
standpunkt = str(standpunkt).strip()
|
||||||
zielpunkt = str(zielpunkt).strip()
|
zielpunkt = str(zielpunkt).strip()
|
||||||
@@ -116,7 +146,7 @@ class FunktionalesModell:
|
|||||||
|
|
||||||
|
|
||||||
if beobachtungsart == "geometrisches_nivellement":
|
if beobachtungsart == "geometrisches_nivellement":
|
||||||
liste_id_standpunkt_zielpunkt = db_zugriff.get_nivellement_beobachtungen_punktnummern()
|
liste_id_standpunkt_zielpunkt = self.db_zugriff.get_nivellement_beobachtungen_punktnummern()
|
||||||
|
|
||||||
for beobachtungenID, standpunkt, zielpunkt in liste_id_standpunkt_zielpunkt:
|
for beobachtungenID, standpunkt, zielpunkt in liste_id_standpunkt_zielpunkt:
|
||||||
standpunkt = str(standpunkt).strip()
|
standpunkt = str(standpunkt).strip()
|
||||||
@@ -129,13 +159,7 @@ class FunktionalesModell:
|
|||||||
if zielpunkt not in liste_punktnummern:
|
if zielpunkt not in liste_punktnummern:
|
||||||
liste_punktnummern.append(zielpunkt)
|
liste_punktnummern.append(zielpunkt)
|
||||||
|
|
||||||
|
# Erstellen der Symbole für die Unbekannten (X, Y, Z und Orientierung)
|
||||||
|
|
||||||
#if liste_beobachtungen_rohdaten_tachymeter == []:
|
|
||||||
# return None
|
|
||||||
|
|
||||||
|
|
||||||
#dict_punkt_symbole = {}
|
|
||||||
liste_unbekannte = []
|
liste_unbekannte = []
|
||||||
|
|
||||||
for punkt in liste_punktnummern:
|
for punkt in liste_punktnummern:
|
||||||
@@ -151,6 +175,7 @@ class FunktionalesModell:
|
|||||||
dict_orientierung_symbole[orientierungsunbekannte] = O
|
dict_orientierung_symbole[orientierungsunbekannte] = O
|
||||||
liste_unbekannte.append(O)
|
liste_unbekannte.append(O)
|
||||||
|
|
||||||
|
# Erstellen der Symbolischen Gleichungen für die Jacobimatrix
|
||||||
liste_beobachtungsgleichungen_distanz =[]
|
liste_beobachtungsgleichungen_distanz =[]
|
||||||
liste_zeilenbeschriftungen_distanz = []
|
liste_zeilenbeschriftungen_distanz = []
|
||||||
|
|
||||||
@@ -161,39 +186,30 @@ class FunktionalesModell:
|
|||||||
liste_zeilenbeschriftungen_zenitwinkel = []
|
liste_zeilenbeschriftungen_zenitwinkel = []
|
||||||
|
|
||||||
liste_beobachtungsgleichungen_gnssbasislinien = []
|
liste_beobachtungsgleichungen_gnssbasislinien = []
|
||||||
liste_A_gnssbasislinien_zeilen = []
|
|
||||||
liste_zeilenbeschriftungen_gnssbasislinien = []
|
liste_zeilenbeschriftungen_gnssbasislinien = []
|
||||||
|
|
||||||
liste_beobachtungsgleichungen_nivellement = []
|
|
||||||
liste_A_nivellement_zeilen = []
|
liste_A_nivellement_zeilen = []
|
||||||
liste_zeilenbeschriftungen_nivellement = []
|
liste_zeilenbeschriftungen_nivellement = []
|
||||||
|
|
||||||
if liste_beobachtungen_rohdaten_tachymeter != []:
|
if liste_beobachtungen_rohdaten_tachymeter != []:
|
||||||
for beobachtungsart, beobachtungenID, beobachtungsgruppeID, standpunkt, zielpunkt in liste_beobachtungen_rohdaten_tachymeter:
|
for beobachtungsart, beobachtungenID, beobachtungsgruppeID, standpunkt, zielpunkt in liste_beobachtungen_rohdaten_tachymeter:
|
||||||
|
# Symbole erstellen
|
||||||
X_sp, Y_sp, Z_sp = self.dict_punkt_symbole[standpunkt]
|
X_sp, Y_sp, Z_sp = self.dict_punkt_symbole[standpunkt]
|
||||||
X_zp, Y_zp, Z_zp = self.dict_punkt_symbole[zielpunkt]
|
X_zp, Y_zp, Z_zp = self.dict_punkt_symbole[zielpunkt]
|
||||||
B_sp, L_sp = sp.symbols(f"B{standpunkt} L{standpunkt}")
|
B_sp, L_sp = sp.symbols(f"B{standpunkt} L{standpunkt}")
|
||||||
|
|
||||||
alpha = sp.symbols(f"{beobachtungenID}_R_{beobachtungsgruppeID}_{standpunkt}_{zielpunkt}")
|
|
||||||
zw = sp.symbols(f"{beobachtungenID}_ZW_{beobachtungsgruppeID}_{standpunkt}_{zielpunkt}")
|
|
||||||
s = sp.symbols(f"{beobachtungenID}_SD_{beobachtungsgruppeID}_{standpunkt}_{zielpunkt}")
|
|
||||||
|
|
||||||
azimut_berechnet = sp.symbols(f"azimut_berechnet_{beobachtungsgruppeID}_{standpunkt}_{zielpunkt}")
|
azimut_berechnet = sp.symbols(f"azimut_berechnet_{beobachtungsgruppeID}_{standpunkt}_{zielpunkt}")
|
||||||
zw_berechnet = sp.symbols(f"zw_berechnet_{beobachtungsgruppeID}_{standpunkt}_{zielpunkt}")
|
zw_berechnet = sp.symbols(f"zw_berechnet_{beobachtungsgruppeID}_{standpunkt}_{zielpunkt}")
|
||||||
s_berechnet = sp.symbols(f"strecke_berechnet_{beobachtungsgruppeID}_{standpunkt}_{zielpunkt}")
|
s_berechnet = sp.symbols(f"strecke_berechnet_{beobachtungsgruppeID}_{standpunkt}_{zielpunkt}")
|
||||||
|
|
||||||
|
# Symbolische Gleichungen aufstellen
|
||||||
if beobachtungsart == "tachymeter_distanz":
|
if beobachtungsart == "tachymeter_distanz":
|
||||||
beobachtungsgleichung = sp.sqrt((X_zp - X_sp) ** 2 + (Y_zp - Y_sp) ** 2 + (Z_zp - Z_sp) ** 2)
|
beobachtungsgleichung = sp.sqrt((X_zp - X_sp) ** 2 + (Y_zp - Y_sp) ** 2 + (Z_zp - Z_sp) ** 2)
|
||||||
liste_beobachtungsgleichungen_distanz.append(beobachtungsgleichung)
|
liste_beobachtungsgleichungen_distanz.append(beobachtungsgleichung)
|
||||||
liste_zeilenbeschriftungen_distanz.append(
|
liste_zeilenbeschriftungen_distanz.append(
|
||||||
f"{beobachtungenID}_SD_{beobachtungsgruppeID}_{standpunkt}_{zielpunkt}")
|
f"{beobachtungenID}_SD_{beobachtungsgruppeID}_{standpunkt}_{zielpunkt}")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
elif beobachtungsart == "tachymeter_richtung":
|
elif beobachtungsart == "tachymeter_richtung":
|
||||||
|
|
||||||
# for beobachtungenID, beobachtungsgruppeID, standpunkt, zielpunkt in liste_id_standpunkt_zielpunkt:
|
|
||||||
d_r_dX_zp = (
|
d_r_dX_zp = (
|
||||||
(sp.sin(B_sp) * sp.cos(L_sp) * sp.sin(azimut_berechnet) - sp.sin(L_sp) * sp.cos(azimut_berechnet)) / (
|
(sp.sin(B_sp) * sp.cos(L_sp) * sp.sin(azimut_berechnet) - sp.sin(L_sp) * sp.cos(azimut_berechnet)) / (
|
||||||
s_berechnet * sp.sin(zw_berechnet)))
|
s_berechnet * sp.sin(zw_berechnet)))
|
||||||
@@ -207,6 +223,8 @@ class FunktionalesModell:
|
|||||||
d_r_dO_sp = -1
|
d_r_dO_sp = -1
|
||||||
|
|
||||||
zeile_A_Matrix = []
|
zeile_A_Matrix = []
|
||||||
|
|
||||||
|
# Symbolische Gleichungen zur Jacobimatrix hinzufügen
|
||||||
for punkt in liste_punktnummern:
|
for punkt in liste_punktnummern:
|
||||||
if punkt == standpunkt:
|
if punkt == standpunkt:
|
||||||
zeile_A_Matrix.extend([d_r_dX_sp, d_r_dY_sp, d_r_dZ_sp])
|
zeile_A_Matrix.extend([d_r_dX_sp, d_r_dY_sp, d_r_dZ_sp])
|
||||||
@@ -228,7 +246,7 @@ class FunktionalesModell:
|
|||||||
|
|
||||||
|
|
||||||
elif beobachtungsart == "tachymeter_zenitwinkel":
|
elif beobachtungsart == "tachymeter_zenitwinkel":
|
||||||
|
# Symbolische Gleichungen aufstellen
|
||||||
d_r_dX_zp = ((X_zp - X_sp) * sp.cos(zw_berechnet) - s_berechnet * sp.cos(B_sp) * sp.cos(L_sp)) / (s_berechnet ** 2 * sp.sin(zw_berechnet))
|
d_r_dX_zp = ((X_zp - X_sp) * sp.cos(zw_berechnet) - s_berechnet * sp.cos(B_sp) * sp.cos(L_sp)) / (s_berechnet ** 2 * sp.sin(zw_berechnet))
|
||||||
d_r_dX_sp = - d_r_dX_zp
|
d_r_dX_sp = - d_r_dX_zp
|
||||||
d_r_dY_zp = ((Y_zp - Y_sp) * sp.cos(zw_berechnet) - s_berechnet * sp.cos(B_sp) * sp.sin(L_sp)) / (s_berechnet ** 2 * sp.sin(zw_berechnet))
|
d_r_dY_zp = ((Y_zp - Y_sp) * sp.cos(zw_berechnet) - s_berechnet * sp.cos(B_sp) * sp.sin(L_sp)) / (s_berechnet ** 2 * sp.sin(zw_berechnet))
|
||||||
@@ -237,6 +255,8 @@ class FunktionalesModell:
|
|||||||
d_r_dZ_sp = - d_r_dZ_zp
|
d_r_dZ_sp = - d_r_dZ_zp
|
||||||
|
|
||||||
zeile_A_Matrix = []
|
zeile_A_Matrix = []
|
||||||
|
|
||||||
|
# Symbolische Gleichungen zur Jacobimatrix hinzufügen
|
||||||
for punkt in liste_punktnummern:
|
for punkt in liste_punktnummern:
|
||||||
if punkt == standpunkt:
|
if punkt == standpunkt:
|
||||||
zeile_A_Matrix.extend([d_r_dX_sp, d_r_dY_sp, d_r_dZ_sp])
|
zeile_A_Matrix.extend([d_r_dX_sp, d_r_dY_sp, d_r_dZ_sp])
|
||||||
@@ -253,27 +273,10 @@ class FunktionalesModell:
|
|||||||
f"{beobachtungenID}_ZW_{beobachtungsgruppeID}_{standpunkt}_{zielpunkt}"
|
f"{beobachtungenID}_ZW_{beobachtungsgruppeID}_{standpunkt}_{zielpunkt}"
|
||||||
)
|
)
|
||||||
|
|
||||||
#if liste_beobachtungen_rohdaten_gnssbasislinien != []:
|
# GNSS-Basislinien
|
||||||
# for beobachtungsart, beobachtungenID, standpunkt, zielpunkt in liste_beobachtungen_rohdaten_gnssbasislinien:
|
|
||||||
# X_sp, Y_sp, Z_sp = self.dict_punkt_symbole[standpunkt]
|
|
||||||
# X_zp, Y_zp, Z_zp = self.dict_punkt_symbole[zielpunkt]
|
|
||||||
|
|
||||||
# if beobachtungsart == "gnss_basislinien":
|
|
||||||
# beobachtungsgleichung_bx = X_zp - X_sp
|
|
||||||
# beobachtungsgleichung_by = Y_zp - Y_sp
|
|
||||||
# beobachtungsgleichung_bz = Z_zp - Z_sp
|
|
||||||
# liste_beobachtungsgleichungen_gnssbasislinien.append(beobachtungsgleichung_bx)
|
|
||||||
# liste_beobachtungsgleichungen_gnssbasislinien.append(beobachtungsgleichung_by)
|
|
||||||
# liste_beobachtungsgleichungen_gnssbasislinien.append(beobachtungsgleichung_bz)
|
|
||||||
# liste_zeilenbeschriftungen_gnssbasislinien.append(
|
|
||||||
# f"{beobachtungenID}_gnssbx_{standpunkt}_{zielpunkt}")
|
|
||||||
# liste_zeilenbeschriftungen_gnssbasislinien.append(
|
|
||||||
# f"{beobachtungenID}_gnssby_{standpunkt}_{zielpunkt}")
|
|
||||||
# liste_zeilenbeschriftungen_gnssbasislinien.append(
|
|
||||||
# f"{beobachtungenID}_gnssbz_{standpunkt}_{zielpunkt}")
|
|
||||||
|
|
||||||
if liste_beobachtungen_rohdaten_gnssbasislinien != []:
|
if liste_beobachtungen_rohdaten_gnssbasislinien != []:
|
||||||
for beobachtungsart, beobachtungenID, standpunkt, zielpunkt in liste_beobachtungen_rohdaten_gnssbasislinien:
|
for beobachtungsart, beobachtungenID, standpunkt, zielpunkt in liste_beobachtungen_rohdaten_gnssbasislinien:
|
||||||
|
# Symbolische Gleichungen aufstellen
|
||||||
X_sp, Y_sp, Z_sp = self.dict_punkt_symbole[standpunkt]
|
X_sp, Y_sp, Z_sp = self.dict_punkt_symbole[standpunkt]
|
||||||
X_zp, Y_zp, Z_zp = self.dict_punkt_symbole[zielpunkt]
|
X_zp, Y_zp, Z_zp = self.dict_punkt_symbole[zielpunkt]
|
||||||
|
|
||||||
@@ -296,10 +299,10 @@ class FunktionalesModell:
|
|||||||
f"{beobachtungenID}_gnssbz_{standpunkt}_{zielpunkt}")
|
f"{beobachtungenID}_gnssbz_{standpunkt}_{zielpunkt}")
|
||||||
|
|
||||||
|
|
||||||
|
# Geometrisches Nivellement
|
||||||
if liste_beobachtungen_rohdaten_nivellement != []:
|
if liste_beobachtungen_rohdaten_nivellement != []:
|
||||||
for beobachtungsart, beobachtungenID, standpunkt, zielpunkt in liste_beobachtungen_rohdaten_nivellement:
|
for beobachtungsart, beobachtungenID, standpunkt, zielpunkt in liste_beobachtungen_rohdaten_nivellement:
|
||||||
X_sp, Y_sp, Z_sp = self.dict_punkt_symbole[standpunkt]
|
# Symbolische Gleichungen aufstellen
|
||||||
X_zp, Y_zp, Z_zp = self.dict_punkt_symbole[zielpunkt]
|
|
||||||
B_sp, L_sp = sp.symbols(f"B{standpunkt} L{standpunkt}")
|
B_sp, L_sp = sp.symbols(f"B{standpunkt} L{standpunkt}")
|
||||||
B_zp, L_zp = sp.symbols(f"B{zielpunkt} L{zielpunkt}")
|
B_zp, L_zp = sp.symbols(f"B{zielpunkt} L{zielpunkt}")
|
||||||
|
|
||||||
@@ -312,6 +315,7 @@ class FunktionalesModell:
|
|||||||
d_r_dZ_zp = sp.sin(B_zp)
|
d_r_dZ_zp = sp.sin(B_zp)
|
||||||
d_r_dZ_sp = -sp.sin(B_sp)
|
d_r_dZ_sp = -sp.sin(B_sp)
|
||||||
|
|
||||||
|
# Symbolische Gleichungen zur Jacobimatrix hinzufügen
|
||||||
zeile_A_Matrix = []
|
zeile_A_Matrix = []
|
||||||
for punkt in liste_punktnummern:
|
for punkt in liste_punktnummern:
|
||||||
if punkt == standpunkt:
|
if punkt == standpunkt:
|
||||||
@@ -329,6 +333,7 @@ class FunktionalesModell:
|
|||||||
f"{beobachtungenID}_niv_{standpunkt}_{zielpunkt}"
|
f"{beobachtungenID}_niv_{standpunkt}_{zielpunkt}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Jacobimatrix aus den einzelnen Listen zusammensetzen, Sy,bolischen Unbekanntenvektor erstellen
|
||||||
if liste_beobachtungsgleichungen_distanz:
|
if liste_beobachtungsgleichungen_distanz:
|
||||||
f_matrix_dist = sp.Matrix(liste_beobachtungsgleichungen_distanz)
|
f_matrix_dist = sp.Matrix(liste_beobachtungsgleichungen_distanz)
|
||||||
unbekanntenvektor = sp.Matrix(liste_unbekannte)
|
unbekanntenvektor = sp.Matrix(liste_unbekannte)
|
||||||
@@ -354,15 +359,13 @@ class FunktionalesModell:
|
|||||||
A_gnssbasislinien = None
|
A_gnssbasislinien = None
|
||||||
|
|
||||||
if liste_A_nivellement_zeilen:
|
if liste_A_nivellement_zeilen:
|
||||||
#f_matrix_nivellement = sp.Matrix(liste_beobachtungsgleichungen_nivellement)
|
|
||||||
#unbekanntenvektor = sp.Matrix(liste_unbekannte)
|
|
||||||
#A_nivellement = f_matrix_nivellement.jacobian(unbekanntenvektor)
|
|
||||||
A_nivellement = sp.Matrix(liste_A_nivellement_zeilen)
|
A_nivellement = sp.Matrix(liste_A_nivellement_zeilen)
|
||||||
else:
|
else:
|
||||||
A_nivellement = None
|
A_nivellement = None
|
||||||
|
|
||||||
|
|
||||||
A_gesamt = None
|
A_gesamt = None
|
||||||
|
# liste_zeilenbeschriftungen_gesamt enthält die Symbolischen Beobachtungen
|
||||||
liste_zeilenbeschriftungen_gesamt = []
|
liste_zeilenbeschriftungen_gesamt = []
|
||||||
|
|
||||||
if A_dist is not None:
|
if A_dist is not None:
|
||||||
@@ -400,9 +403,9 @@ class FunktionalesModell:
|
|||||||
if A_gesamt is None:
|
if A_gesamt is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
# Wenn eine weiche Lagerung erfolgt, werden die Unbekannten umsortiert, sodass die für die Lagerung zu verwendenden Unbekannten rechts in der Matrix aufgeführt werden.
|
||||||
|
# Zudem werden für die Anschlusspunkte weiter Gleichungen als "Beobachtungen" unten an die Jacobimatrix angefügt.
|
||||||
if datumsfestlegung == "weiche Lagerung":
|
if datumsfestlegung == "weiche Lagerung":
|
||||||
vertauschung = list(range(len(liste_unbekannte)))
|
|
||||||
|
|
||||||
if liste_unbekannte_datumsfestlegung is not None and liste_unbekannte_datumsfestlegung != []:
|
if liste_unbekannte_datumsfestlegung is not None and liste_unbekannte_datumsfestlegung != []:
|
||||||
liste_unbekannte_alt = list(liste_unbekannte)
|
liste_unbekannte_alt = list(liste_unbekannte)
|
||||||
liste_unbekannte_datumsfestlegung = [str(u).strip() for u in liste_unbekannte_datumsfestlegung]
|
liste_unbekannte_datumsfestlegung = [str(u).strip() for u in liste_unbekannte_datumsfestlegung]
|
||||||
@@ -420,7 +423,7 @@ class FunktionalesModell:
|
|||||||
A_gesamt = A_gesamt[:, vertauschung]
|
A_gesamt = A_gesamt[:, vertauschung]
|
||||||
liste_unbekannte = [liste_unbekannte_alt[i] for i in vertauschung]
|
liste_unbekannte = [liste_unbekannte_alt[i] for i in vertauschung]
|
||||||
|
|
||||||
# Zusatzgeleichungen der weichen Lagerung
|
# Zusatzgeleichungen der weichen Lagerung für die Anschlusspunkte
|
||||||
anzhl_einheitsmatrix = len(liste_unbekannte_datumsfestlegung)
|
anzhl_einheitsmatrix = len(liste_unbekannte_datumsfestlegung)
|
||||||
if anzhl_einheitsmatrix > 0:
|
if anzhl_einheitsmatrix > 0:
|
||||||
nullenmatrix = sp.zeros(anzhl_einheitsmatrix, A_gesamt.shape[1] - anzhl_einheitsmatrix)
|
nullenmatrix = sp.zeros(anzhl_einheitsmatrix, A_gesamt.shape[1] - anzhl_einheitsmatrix)
|
||||||
@@ -430,24 +433,49 @@ class FunktionalesModell:
|
|||||||
for unbekannte_datumsfestlegung in liste_unbekannte_datumsfestlegung:
|
for unbekannte_datumsfestlegung in liste_unbekannte_datumsfestlegung:
|
||||||
liste_zeilenbeschriftungen_gesamt.append(f"lA_{unbekannte_datumsfestlegung}")
|
liste_zeilenbeschriftungen_gesamt.append(f"lA_{unbekannte_datumsfestlegung}")
|
||||||
|
|
||||||
|
# Symbolische Liste der Unbekannten speichern
|
||||||
self.liste_unbekanntenvektor_symbolisch = liste_unbekannte
|
self.liste_unbekanntenvektor_symbolisch = liste_unbekannte
|
||||||
|
|
||||||
|
# Symbolische Jacobimatrix speichern
|
||||||
Export.matrix_to_csv(r"Zwischenergebnisse\Jacobi_Matrix_Symbolisch.csv", liste_unbekannte,
|
Export.matrix_to_csv(r"Zwischenergebnisse\Jacobi_Matrix_Symbolisch.csv", liste_unbekannte,
|
||||||
liste_zeilenbeschriftungen_gesamt, A_gesamt, "Beobachtung")
|
liste_zeilenbeschriftungen_gesamt, A_gesamt, "Beobachtung")
|
||||||
return A_gesamt, liste_unbekannte, liste_zeilenbeschriftungen_gesamt
|
return A_gesamt, liste_unbekannte, liste_zeilenbeschriftungen_gesamt
|
||||||
|
|
||||||
|
|
||||||
def jacobi_matrix_zahlen_iteration_0(self, A_symbolisch: sp.Matrix, koordinatenart: str,
|
def jacobi_matrix_numerisch(self, A_symbolisch: sp.Matrix, koordinatenart: str,
|
||||||
liste_unbekannte: list = None,
|
liste_unbekannte: list = None,
|
||||||
liste_zeilenbeschriftungen_gesamt: list = None,
|
liste_zeilenbeschriftungen_gesamt: list = None,
|
||||||
iterationsnummer: int = 0) -> ndarray[tuple[Any, ...], dtype[Any]] | None:
|
iterationsnummer: int = 0) -> ndarray[tuple[Any, ...], dtype[Any]] | None:
|
||||||
|
"""Erstellt eine numerische Matrix aus einer symbolischen Jacobi-Matrix.
|
||||||
|
|
||||||
|
Es wird sympy.lambdify verwendet, um die symbolische Matrix
|
||||||
|
mit den aktuellen Substitutionen effizient als Numpy-Array auszuwerten. Die Lambdify-Funktion wird
|
||||||
|
gecached (self.func_A0), um Rechenzeit bei Ausführen mehrerer Iterationen zu sparen.
|
||||||
|
|
||||||
|
Die numerische Jacobi-Matrix wird als CSV in den Ordner Zwischenergebnisse exportiert.
|
||||||
|
|
||||||
|
:param A_symbolisch: Symbolische Jacobi-Matrix.
|
||||||
|
:type A_symbolisch: sp.Matrix
|
||||||
|
:param koordinatenart: Bezeichnung der Koordinatenart (aktuell implementiert: "naeherung_us").
|
||||||
|
:type koordinatenart: str
|
||||||
|
:param liste_unbekannte: Liste der Unbekannten (Symbole) in der Spaltenreihenfolge.
|
||||||
|
:type liste_unbekannte: list | None
|
||||||
|
:param liste_zeilenbeschriftungen_gesamt: Liste der Zeilenbeschriftungen (Beobachtungskennungen).
|
||||||
|
:type liste_zeilenbeschriftungen_gesamt: list | None
|
||||||
|
:param iterationsnummer: Iterationsnummer für Dateinamen der Zwischenergebnisse.
|
||||||
|
:type iterationsnummer: int
|
||||||
|
:return: Numerische Jacobi-Matrix als Numpy-Array.
|
||||||
|
:rtype: ndarray[tuple[Any, ...], dtype[Any]] | None
|
||||||
|
:raises ValueError: Falls Symbole in A_symbolisch enthalten sind, für die keine Substitutionen vorhanden sind.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Symbolischen Beobachtungsvektor als Instanzvariable speichern
|
||||||
self.liste_beobachtungsvektor_symbolisch = [str(x) for x in liste_zeilenbeschriftungen_gesamt]
|
self.liste_beobachtungsvektor_symbolisch = [str(x) for x in liste_zeilenbeschriftungen_gesamt]
|
||||||
|
|
||||||
|
# Wenn es sich um geozentrisch-kartesischen Koordinaten handelt, wird die Sympy-Methode Lambdify verwendet, um die Symbole aus der vorherigen Methode jacobi_matrix_symbolisch in Numerische Zahlen umzuwandeln.
|
||||||
|
# Zur Ersparnis von Rechenzeit werden die numerischen Zahlen als Numpy-Werte behandelt.
|
||||||
if koordinatenart == "naeherung_us":
|
if koordinatenart == "naeherung_us":
|
||||||
#A_numerisch = A_symbolisch.xreplace(self.substitutionen_dict)
|
|
||||||
if self.func_A0 is None:
|
if self.func_A0 is None:
|
||||||
#self.liste_symbole_lambdify = sorted(self.substitutionen_dict.keys(), key=lambda s: str(s))
|
|
||||||
self.func_A0 = sp.lambdify(
|
self.func_A0 = sp.lambdify(
|
||||||
self.liste_symbole_lambdify,
|
self.liste_symbole_lambdify,
|
||||||
A_symbolisch,
|
A_symbolisch,
|
||||||
@@ -455,45 +483,43 @@ class FunktionalesModell:
|
|||||||
cse=True
|
cse=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Überprüfung, ob alle in der Symbolischen Matrix enthaltenen Symbole substituiert werden können. Ist ein Symbol nicht durch einen numerischen Wert ersetzbar, wird eine Fehlermeldung ausgegeben.
|
||||||
fehlend = [s for s in self.liste_symbole_lambdify if s not in self.substitutionen_dict]
|
fehlend = [s for s in self.liste_symbole_lambdify if s not in self.substitutionen_dict]
|
||||||
if fehlend:
|
if fehlend:
|
||||||
Export.matrix_to_csv(
|
|
||||||
r"Zwischenergebnisse\fehlende_substitutionen_A.csv",
|
|
||||||
[""],
|
|
||||||
[str(s) for s in fehlend],
|
|
||||||
sp.Matrix([[str(s)] for s in fehlend]),
|
|
||||||
"fehlend"
|
|
||||||
)
|
|
||||||
raise ValueError(f"Fehlende Substitutionen in A: {[str(s) for s in fehlend[:30]]}")
|
raise ValueError(f"Fehlende Substitutionen in A: {[str(s) for s in fehlend[:30]]}")
|
||||||
|
|
||||||
|
|
||||||
liste_werte = [self.substitutionen_dict[s] for s in self.liste_symbole_lambdify]
|
liste_werte = [self.substitutionen_dict[s] for s in self.liste_symbole_lambdify]
|
||||||
#A_numerisch = sp.Matrix(self.func_A0(*liste_werte))
|
|
||||||
A_numerisch = np.asarray(self.func_A0(*liste_werte), dtype=float)
|
A_numerisch = np.asarray(self.func_A0(*liste_werte), dtype=float)
|
||||||
|
|
||||||
Export.matrix_to_csv(fr"Zwischenergebnisse\{iterationsnummer}Jacobi_Matrix_Numerisch_Iteration0.csv", liste_unbekannte,
|
Export.matrix_to_csv(fr"Zwischenergebnisse\{iterationsnummer}Jacobi_Matrix_Numerisch.csv", liste_unbekannte,
|
||||||
liste_zeilenbeschriftungen_gesamt, A_numerisch, "Beobachtung")
|
liste_zeilenbeschriftungen_gesamt, A_numerisch, "Beobachtung")
|
||||||
|
|
||||||
condA = float(np.linalg.cond(A_numerisch))
|
|
||||||
rankA = int(np.linalg.matrix_rank(A_numerisch))
|
|
||||||
Export.matrix_to_csv(
|
|
||||||
fr"Zwischenergebnisse\{iterationsnummer}_Jacobi_Matrix_Stats.csv",
|
|
||||||
[""],
|
|
||||||
["condA", "rankA"],
|
|
||||||
np.array([[condA], [rankA]], dtype=float),
|
|
||||||
"Wert"
|
|
||||||
)
|
|
||||||
|
|
||||||
return A_numerisch
|
return A_numerisch
|
||||||
else:
|
else:
|
||||||
print("Koordinaten noch nicht implementiert!")
|
print(f"Die Koordinatenart {koordinatenart} ist noch nicht im Programm implementiert!")
|
||||||
|
|
||||||
def beobachtungsvektor_numerisch(self, liste_beobachtungsvektor_symbolisch: list) -> MutableDenseMatrix:
|
def beobachtungsvektor_numerisch(self, liste_beobachtungsvektor_symbolisch: list) -> MutableDenseMatrix:
|
||||||
|
"""Erstellt den numerischen Beobachtungsvektor aus symbolischen Beobachtungskennungen.
|
||||||
|
|
||||||
|
Die Einträge des symbolischen Beobachtungsvektors werden über self.substitutionen_dict substituiert.
|
||||||
|
Anschlusspunkte der weichen Lagerung (lA_*) werden gesondert behandelt, indem das Präfix entfernt wird.
|
||||||
|
|
||||||
|
Der numerische Beobachtungsvektor wird als CSV-Datei in Zwischenergebnisse\\Beobachtungsvektor_Numerisch.csv exportiert.
|
||||||
|
|
||||||
|
:param liste_beobachtungsvektor_symbolisch: Liste symbolischer Beobachtungsbezeichnungen (Strings).
|
||||||
|
:type liste_beobachtungsvektor_symbolisch: list
|
||||||
|
:return: Numerischer Beobachtungsvektor als SymPy-Matrix (Spaltenvektor).
|
||||||
|
:rtype: MutableDenseMatrix
|
||||||
|
"""
|
||||||
|
|
||||||
liste_beobachtungsvektor_numerisch = []
|
liste_beobachtungsvektor_numerisch = []
|
||||||
for beobachtung_symbolisch in liste_beobachtungsvektor_symbolisch:
|
for beobachtung_symbolisch in liste_beobachtungsvektor_symbolisch:
|
||||||
beobachtung_symbolisch = str(beobachtung_symbolisch).strip()
|
beobachtung_symbolisch = str(beobachtung_symbolisch).strip()
|
||||||
|
# Die Anschlusspunkte für die weiche Lagerung (lA) werden gesondert bearbeitet, weil die Symbole anders aufgebaut sind.
|
||||||
if beobachtung_symbolisch.startswith("lA_"):
|
if beobachtung_symbolisch.startswith("lA_"):
|
||||||
beobachtung_symbolisch = str(beobachtung_symbolisch.split("_", 1)[1]).strip()
|
beobachtung_symbolisch = str(beobachtung_symbolisch.split("_", 1)[1]).strip()
|
||||||
|
# Substituieren des symbolischen Beobachtungsvektors
|
||||||
liste_beobachtungsvektor_numerisch.append(self.substitutionen_dict[sp.Symbol(beobachtung_symbolisch)])
|
liste_beobachtungsvektor_numerisch.append(self.substitutionen_dict[sp.Symbol(beobachtung_symbolisch)])
|
||||||
|
|
||||||
beobachtungsvektor_numerisch = sp.Matrix(liste_beobachtungsvektor_numerisch)
|
beobachtungsvektor_numerisch = sp.Matrix(liste_beobachtungsvektor_numerisch)
|
||||||
@@ -501,10 +527,24 @@ class FunktionalesModell:
|
|||||||
return beobachtungsvektor_numerisch
|
return beobachtungsvektor_numerisch
|
||||||
|
|
||||||
def beobachtungsvektor_naeherung_symbolisch(self, liste_beobachtungsvektor_symbolisch: list) -> sp.Matrix:
|
def beobachtungsvektor_naeherung_symbolisch(self, liste_beobachtungsvektor_symbolisch: list) -> sp.Matrix:
|
||||||
|
"""Erstellt den symbolischen Näherungs-Beobachtungsvektor f(x0).
|
||||||
|
|
||||||
|
Aus den Beobachtungskennungen werden Stand-/Zielpunkte und Beobachtungsarten abgeleitet und die
|
||||||
|
entsprechenden symbolischen Beobachtungsgleichungen aufgebaut (z. B. geometrische Distanz, GNSS-Differenzen,
|
||||||
|
Richtungs-/Zenitwinkel-Symbole, Geometrisches Nivellement über Normalhöhen).
|
||||||
|
|
||||||
|
Der symbolische Näherungs-Beobachtungsvektor wird als CSV <-Datei in Zwischenergebnisse\\Beobachtungsvektor_Näherung_Symbolisch.csv exportiert.
|
||||||
|
|
||||||
|
:param liste_beobachtungsvektor_symbolisch: Liste symbolischer Beobachtungskennungen (Strings).
|
||||||
|
:type liste_beobachtungsvektor_symbolisch: list
|
||||||
|
:return: Symbolischer Näherungs-Beobachtungsvektor als SymPy-Matrix.
|
||||||
|
:rtype: sp.Matrix
|
||||||
|
"""
|
||||||
liste_beobachtungsgleichungen = []
|
liste_beobachtungsgleichungen = []
|
||||||
self.dict_punkt_symbole = {}
|
self.dict_punkt_symbole = {}
|
||||||
liste_punktnummern = []
|
liste_punktnummern = []
|
||||||
|
|
||||||
|
# Speichern der Punktnummern der Stand- und Zeilpunkte der Beobachtungen, um daraus später die Symbole für die Unbekannten zu erstellen.
|
||||||
for beobachtung_symbolisch in liste_beobachtungsvektor_symbolisch:
|
for beobachtung_symbolisch in liste_beobachtungsvektor_symbolisch:
|
||||||
aufgeteilt = str(beobachtung_symbolisch).strip().split("_")
|
aufgeteilt = str(beobachtung_symbolisch).strip().split("_")
|
||||||
if aufgeteilt[0] == "lA":
|
if aufgeteilt[0] == "lA":
|
||||||
@@ -528,6 +568,7 @@ class FunktionalesModell:
|
|||||||
if zielpunkt not in liste_punktnummern:
|
if zielpunkt not in liste_punktnummern:
|
||||||
liste_punktnummern.append(zielpunkt)
|
liste_punktnummern.append(zielpunkt)
|
||||||
|
|
||||||
|
# Erstellen der Symbole für die Unbekannten
|
||||||
for punkt in liste_punktnummern:
|
for punkt in liste_punktnummern:
|
||||||
X, Y, Z = sp.symbols(f"X{punkt} Y{punkt} Z{punkt}")
|
X, Y, Z = sp.symbols(f"X{punkt} Y{punkt} Z{punkt}")
|
||||||
self.dict_punkt_symbole[str(punkt)] = (X, Y, Z)
|
self.dict_punkt_symbole[str(punkt)] = (X, Y, Z)
|
||||||
@@ -541,60 +582,30 @@ class FunktionalesModell:
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
if aufgeteilt[1] == "SD" or aufgeteilt[1] == "R" or aufgeteilt[1] == "ZW":
|
if aufgeteilt[1] == "SD" or aufgeteilt[1] == "R" or aufgeteilt[1] == "ZW":
|
||||||
|
|
||||||
#beobachtungen_ID = aufgeteilt[0]
|
|
||||||
beobachtungsart = aufgeteilt[1] # "SD", "R", "ZW"
|
beobachtungsart = aufgeteilt[1] # "SD", "R", "ZW"
|
||||||
beobachtungsgruppeID = aufgeteilt[2]
|
beobachtungsgruppeID = aufgeteilt[2]
|
||||||
standpunkt = str(aufgeteilt[3]).strip()
|
standpunkt = str(aufgeteilt[3]).strip()
|
||||||
zielpunkt = str(aufgeteilt[4]).strip()
|
zielpunkt = str(aufgeteilt[4]).strip()
|
||||||
|
|
||||||
|
# Hinzufügen der Symbole zum dict_punkt_symbole als Vorbereitung auf die spätere Substitution der Symbole durch numerische Werte
|
||||||
X_sp, Y_sp, Z_sp = self.dict_punkt_symbole[standpunkt]
|
X_sp, Y_sp, Z_sp = self.dict_punkt_symbole[standpunkt]
|
||||||
X_zp, Y_zp, Z_zp = self.dict_punkt_symbole[zielpunkt]
|
X_zp, Y_zp, Z_zp = self.dict_punkt_symbole[zielpunkt]
|
||||||
|
|
||||||
|
# Aufstellen der Symbolischen Gleichungen
|
||||||
dX = X_zp - X_sp
|
dX = X_zp - X_sp
|
||||||
dY = Y_zp - Y_sp
|
dY = Y_zp - Y_sp
|
||||||
dZ = Z_zp - Z_sp
|
dZ = Z_zp - Z_sp
|
||||||
s = sp.sqrt(dX ** 2 + dY ** 2 + dZ ** 2) # Schrägstrecke
|
|
||||||
|
|
||||||
B_sp = sp.Symbol(f"B{standpunkt}")
|
|
||||||
L_sp = sp.Symbol(f"L{standpunkt}")
|
|
||||||
|
|
||||||
if beobachtungsart == "SD":
|
if beobachtungsart == "SD":
|
||||||
|
|
||||||
s_geom = sp.sqrt(dX ** 2 + dY ** 2 + dZ ** 2)
|
s_geom = sp.sqrt(dX ** 2 + dY ** 2 + dZ ** 2)
|
||||||
liste_beobachtungsgleichungen.append(s_geom)
|
liste_beobachtungsgleichungen.append(s_geom)
|
||||||
|
|
||||||
elif beobachtungsart == "R":
|
elif beobachtungsart == "R":
|
||||||
|
|
||||||
#O_sp = sp.Symbol(f"O_{beobachtungsgruppeID}")
|
|
||||||
r_sp_zp = sp.Symbol(f"richtung_berechnet_{beobachtungsgruppeID}_{standpunkt}_{zielpunkt}")
|
r_sp_zp = sp.Symbol(f"richtung_berechnet_{beobachtungsgruppeID}_{standpunkt}_{zielpunkt}")
|
||||||
|
|
||||||
# Lokales System: x_loc = Nord, y_loc = Ost
|
|
||||||
#x_loc = (-sp.sin(B_sp) * sp.cos(L_sp)) * dX + (-sp.sin(B_sp) * sp.sin(L_sp)) * dY + (sp.cos(B_sp)) * dZ
|
|
||||||
#y_loc = (-sp.sin(L_sp)) * dX + (sp.cos(L_sp)) * dY
|
|
||||||
|
|
||||||
#a12 = sp.atan2(y_loc, x_loc)
|
|
||||||
|
|
||||||
# Richtung nach Otepka: r = a12 - O
|
|
||||||
liste_beobachtungsgleichungen.append(r_sp_zp)
|
liste_beobachtungsgleichungen.append(r_sp_zp)
|
||||||
|
|
||||||
|
|
||||||
elif beobachtungsart == "ZW":
|
elif beobachtungsart == "ZW":
|
||||||
|
|
||||||
#dX = X_zp - X_sp
|
|
||||||
|
|
||||||
#dY = Y_zp - Y_sp
|
|
||||||
|
|
||||||
#dZ = Z_zp - Z_sp
|
|
||||||
|
|
||||||
#s_geom = sp.sqrt(dX ** 2 + dY ** 2 + dZ ** 2)
|
|
||||||
|
|
||||||
#z_loc = (sp.cos(B_sp) * sp.cos(L_sp)) * dX + (sp.cos(B_sp) * sp.sin(L_sp)) * dY + (sp.sin(B_sp)) * dZ
|
|
||||||
|
|
||||||
#zw = sp.acos(z_loc / s_geom)
|
|
||||||
|
|
||||||
zw_sp_zp = sp.Symbol(f"zw_berechnet_{beobachtungsgruppeID}_{standpunkt}_{zielpunkt}")
|
zw_sp_zp = sp.Symbol(f"zw_berechnet_{beobachtungsgruppeID}_{standpunkt}_{zielpunkt}")
|
||||||
|
|
||||||
liste_beobachtungsgleichungen.append(zw_sp_zp)
|
liste_beobachtungsgleichungen.append(zw_sp_zp)
|
||||||
|
|
||||||
if aufgeteilt[1] == "gnssbx" or aufgeteilt[1] == "gnssby" or aufgeteilt[1] == "gnssbz":
|
if aufgeteilt[1] == "gnssbx" or aufgeteilt[1] == "gnssby" or aufgeteilt[1] == "gnssbz":
|
||||||
@@ -617,7 +628,6 @@ class FunktionalesModell:
|
|||||||
liste_beobachtungsgleichungen.append(dZ)
|
liste_beobachtungsgleichungen.append(dZ)
|
||||||
|
|
||||||
if aufgeteilt[1] == "niv":
|
if aufgeteilt[1] == "niv":
|
||||||
beobachtungsart = aufgeteilt[1]
|
|
||||||
standpunkt = str(aufgeteilt[2]).strip()
|
standpunkt = str(aufgeteilt[2]).strip()
|
||||||
zielpunkt = str(aufgeteilt[3]).strip()
|
zielpunkt = str(aufgeteilt[3]).strip()
|
||||||
|
|
||||||
@@ -628,18 +638,36 @@ class FunktionalesModell:
|
|||||||
|
|
||||||
liste_beobachtungsgleichungen.append(niv_sp_zp)
|
liste_beobachtungsgleichungen.append(niv_sp_zp)
|
||||||
|
|
||||||
|
# Finalisieren des Symbolischen Beobachtungsvektors und exportieren als csv-Datei.
|
||||||
beobachtungsvektor_naeherung_symbolisch = sp.Matrix(liste_beobachtungsgleichungen)
|
beobachtungsvektor_naeherung_symbolisch = sp.Matrix(liste_beobachtungsgleichungen)
|
||||||
Export.matrix_to_csv(r"Zwischenergebnisse\Beobachtungsvektor_Näherung_Symbolisch.csv", [""],
|
Export.matrix_to_csv(r"Zwischenergebnisse\Beobachtungsvektor_Näherung_Symbolisch.csv", [""],
|
||||||
liste_beobachtungsvektor_symbolisch, beobachtungsvektor_naeherung_symbolisch, "Beobachtungsvektor")
|
liste_beobachtungsvektor_symbolisch, beobachtungsvektor_naeherung_symbolisch, "Beobachtungsvektor")
|
||||||
|
|
||||||
return beobachtungsvektor_naeherung_symbolisch
|
return beobachtungsvektor_naeherung_symbolisch
|
||||||
|
|
||||||
def beobachtungsvektor_naeherung_numerisch_iteration0(self, liste_beobachtungsvektor_symbolisch: list,
|
def beobachtungsvektor_naeherung_numerisch(self, liste_beobachtungsvektor_symbolisch: list,
|
||||||
beobachtungsvektor_naeherung_symbolisch: sp.Matrix,
|
beobachtungsvektor_naeherung_symbolisch: sp.Matrix,
|
||||||
iterationsnummer: int = 0) -> ndarray[tuple[int, int], Any]:
|
iterationsnummer: int = 0) -> ndarray[tuple[int, int], Any]:
|
||||||
#beobachtungsvektor_naeherung_numerisch_iteration0 = beobachtungsvektor_naeherung_symbolisch.xreplace(self.substitutionen_dict)
|
"""Erstellt einen numerischen Vektor aus dem smbolischen Näherungs-Beobachtungsvektor.
|
||||||
|
|
||||||
|
Es wird sympy.lambdify verwendet, um den in beobachtungsvektor_naeherung_symbolisch erzeugten
|
||||||
|
symbolischen Vektor effizient als Numpy-Array auszuwerten. Die Lambdify-Funktion wird gecached
|
||||||
|
(self.func_beob0), um Rechenzeit in Iterationen zu sparen.
|
||||||
|
|
||||||
|
Der numerische Näherungs-Beobachtungsvektor wird als CSV-Datei im Ordner Zwischenergebnisse exportiert.
|
||||||
|
|
||||||
|
:param liste_beobachtungsvektor_symbolisch: Liste symbolischer Beobachtungskennungen (Strings).
|
||||||
|
:type liste_beobachtungsvektor_symbolisch: list
|
||||||
|
:param beobachtungsvektor_naeherung_symbolisch: Symbolischer Näherungs-Beobachtungsvektor f(x0).
|
||||||
|
:type beobachtungsvektor_naeherung_symbolisch: sp.Matrix
|
||||||
|
:param iterationsnummer: Iterationsnummer für Dateinamen der Zwischenergebnisse.
|
||||||
|
:type iterationsnummer: int
|
||||||
|
:return: Numerischer Näherungs-Beobachtungsvektor als Numpy-Array (Spaltenvektor).
|
||||||
|
:rtype: ndarray[tuple[int, int], Any]
|
||||||
|
"""
|
||||||
|
# Es wird die Sympy-Methode Lambdify verwendet, um die Symbole aus der vorherigen Methode beobachtungsvektor_naeherung_symbolisch in Numerische Zahlen umzuwandeln.
|
||||||
|
# Zur Ersparnis von Rechenzeit werden die numerischen Zahlen als Numpy-Werte behandelt.
|
||||||
if self.func_beob0 is None:
|
if self.func_beob0 is None:
|
||||||
#self.liste_symbole_lambdify = sorted(self.substitutionen_dict.keys(), key=lambda s: str(s))
|
|
||||||
self.func_beob0 = sp.lambdify(
|
self.func_beob0 = sp.lambdify(
|
||||||
self.liste_symbole_lambdify,
|
self.liste_symbole_lambdify,
|
||||||
beobachtungsvektor_naeherung_symbolisch,
|
beobachtungsvektor_naeherung_symbolisch,
|
||||||
@@ -648,17 +676,26 @@ class FunktionalesModell:
|
|||||||
)
|
)
|
||||||
|
|
||||||
liste_werte = [self.substitutionen_dict[s] for s in self.liste_symbole_lambdify]
|
liste_werte = [self.substitutionen_dict[s] for s in self.liste_symbole_lambdify]
|
||||||
#beobachtungsvektor_naeherung_numerisch_iteration0 = sp.Matrix(self.func_beob0(*liste_werte))
|
beobachtungsvektor_naeherung_numerisch = np.asarray(self.func_beob0(*liste_werte),
|
||||||
beobachtungsvektor_naeherung_numerisch_iteration0 = np.asarray(self.func_beob0(*liste_werte),
|
|
||||||
dtype=float).reshape(-1, 1)
|
dtype=float).reshape(-1, 1)
|
||||||
|
|
||||||
Export.matrix_to_csv(fr"Zwischenergebnisse\{iterationsnummer}_Beobachtungsvektor_Näherung_Numerisch_Iteration0.csv", [""],
|
Export.matrix_to_csv(fr"Zwischenergebnisse\{iterationsnummer}_Beobachtungsvektor_Näherung_Numerisch_Iteration0.csv", [""],
|
||||||
liste_beobachtungsvektor_symbolisch, beobachtungsvektor_naeherung_numerisch_iteration0,
|
liste_beobachtungsvektor_symbolisch, beobachtungsvektor_naeherung_numerisch,
|
||||||
"Beobachtungsvektor")
|
"Beobachtungsvektor")
|
||||||
|
|
||||||
return beobachtungsvektor_naeherung_numerisch_iteration0
|
return beobachtungsvektor_naeherung_numerisch
|
||||||
|
|
||||||
def unbekanntenvektor_symbolisch(self, liste_unbekannte: list) -> sp.Matrix:
|
def unbekanntenvektor_symbolisch(self, liste_unbekannte: list) -> sp.Matrix:
|
||||||
|
"""Erstellt den symbolischen Unbekanntenvektor.
|
||||||
|
|
||||||
|
Der Unbekanntenvektor wird als SymPy-Matrix aufgebaut und als CSV-Datei in
|
||||||
|
Zwischenergebnisse\\Unbekanntenvektor_Symbolisch.csv exportiert.
|
||||||
|
|
||||||
|
:param liste_unbekannte: Liste der Unbekannten (Symbole).
|
||||||
|
:type liste_unbekannte: list
|
||||||
|
:return: Symbolischer Unbekanntenvektor als SymPy-Matrix.
|
||||||
|
:rtype: sp.Matrix
|
||||||
|
"""
|
||||||
unbekanntenvektor_symbolisch = sp.Matrix(liste_unbekannte)
|
unbekanntenvektor_symbolisch = sp.Matrix(liste_unbekannte)
|
||||||
Export.matrix_to_csv(r"Zwischenergebnisse\Unbekanntenvektor_Symbolisch.csv", [""], liste_unbekannte, unbekanntenvektor_symbolisch,
|
Export.matrix_to_csv(r"Zwischenergebnisse\Unbekanntenvektor_Symbolisch.csv", [""], liste_unbekannte, unbekanntenvektor_symbolisch,
|
||||||
"Unbekanntenvektor")
|
"Unbekanntenvektor")
|
||||||
@@ -666,64 +703,82 @@ class FunktionalesModell:
|
|||||||
|
|
||||||
def unbekanntenvektor_numerisch(self, liste_unbekanntenvektor_symbolisch: list,
|
def unbekanntenvektor_numerisch(self, liste_unbekanntenvektor_symbolisch: list,
|
||||||
unbekanntenvektor_symbolisch: sp.Matrix,
|
unbekanntenvektor_symbolisch: sp.Matrix,
|
||||||
dX_Vektor: np.Matrix = None,
|
dX_Vektor: np.asarray = None,
|
||||||
unbekanntenvektor_neumerisch_vorherige_Iteration: np.Matrix = None,
|
unbekanntenvektor_numerisch_vorherige_Iteration: np.asarray = None,
|
||||||
iterationsnummer: int = 0) -> ndarray[tuple[int, int], Any] | ndarray[tuple[Any, ...], dtype[Any]]:
|
iterationsnummer: int = 0) -> ndarray[tuple[int, int], Any] | ndarray[tuple[Any, ...], dtype[Any]]:
|
||||||
|
"""Erstellt den numerischen Unbekanntenvektor jeder Iteration.
|
||||||
|
|
||||||
|
Wenn keine Iterationsfortschreibung übergeben wird (dX_Vektor und unbekanntenvektor_numerisch_vorherige_Iteration sind None),
|
||||||
|
werden die aktuellen Substitutionen genutzt und daraus der numerische Unbekanntenvektor aufgebaut.
|
||||||
|
|
||||||
|
Wenn dX_Vektor und ein Unbekanntenvektor der vorherigen Iteration übergeben werden, wird der neue
|
||||||
|
Unbekanntenvektor aus der Summe gebildet: x_neu = x_alt + dX.
|
||||||
|
|
||||||
|
Anschließend wird self.substitutionen_dict auf Basis des neuen Unbekanntenvektors aktualisiert.
|
||||||
|
Der numerische Unbekanntenvektor wird als CSV-Datei in den Ordner Zwischenergebnisse exportiert.
|
||||||
|
|
||||||
|
:param liste_unbekanntenvektor_symbolisch: Liste der Unbekannten (Symbole) in der Reihenfolge des numerischen Vektors.
|
||||||
|
:type liste_unbekanntenvektor_symbolisch: list
|
||||||
|
:param unbekanntenvektor_symbolisch: Symbolischer Unbekanntenvektor.
|
||||||
|
:type unbekanntenvektor_symbolisch: sp.Matrix
|
||||||
|
:param dX_Vektor: Verbesserungsvektor der aktuellen Iteration (optional).
|
||||||
|
:type dX_Vektor: np.asarray | None
|
||||||
|
:param unbekanntenvektor_numerisch_vorherige_Iteration: Numerischer Unbekanntenvektor der vorherigen Iteration (optional).
|
||||||
|
:type unbekanntenvektor_numerisch_vorherige_Iteration: np.asarray | None
|
||||||
|
:param iterationsnummer: Iterationsnummer für Dateinamen der Zwischenergebnisse.
|
||||||
|
:type iterationsnummer: int
|
||||||
|
:return: Numerischer Unbekanntenvektor als Numpy-Array
|
||||||
|
:rtype: ndarray[tuple[int, int], Any] | ndarray[tuple[Any, ...], dtype[Any]]
|
||||||
|
"""
|
||||||
|
|
||||||
self.liste_unbekanntenvektor_symbolisch = liste_unbekanntenvektor_symbolisch
|
self.liste_unbekanntenvektor_symbolisch = liste_unbekanntenvektor_symbolisch
|
||||||
|
|
||||||
#if not hasattr(self, "liste_unbekanntenvektor_symbolisch"):
|
# Überprüfung, ob dX und der unbekanntenvektor aus der vorhigen Iteration übergeben wurden. Wenn ja, wird daraus der neue unbekanntenvektor nach der Iteration berechnet.
|
||||||
# self.liste_unbekanntenvektor_symbolisch = liste_unbekanntenvektor_symbolisch
|
if dX_Vektor is None and unbekanntenvektor_numerisch_vorherige_Iteration is None:
|
||||||
|
|
||||||
if dX_Vektor is None and unbekanntenvektor_neumerisch_vorherige_Iteration is None:
|
|
||||||
#unbekanntenvektor_numerisch = unbekanntenvektor_symbolisch.xreplace(self.substitutionen_dict)
|
|
||||||
#if self.func_u0 is None:
|
|
||||||
# self.func_u0 = sp.lambdify(
|
|
||||||
# self.liste_symbole_lambdify,
|
|
||||||
# unbekanntenvektor_symbolisch,
|
|
||||||
# modules="numpy",
|
|
||||||
# cse=True
|
|
||||||
# )
|
|
||||||
unbekanntenvektor_numerisch = np.asarray(
|
unbekanntenvektor_numerisch = np.asarray(
|
||||||
[[float(self.substitutionen_dict[sym])] for sym in self.liste_unbekanntenvektor_symbolisch],
|
[[float(self.substitutionen_dict[sym])] for sym in self.liste_unbekanntenvektor_symbolisch],
|
||||||
dtype=float
|
dtype=float
|
||||||
).reshape(-1, 1)
|
).reshape(-1, 1)
|
||||||
|
|
||||||
#liste_werte = [self.substitutionen_dict[s] for s in self.liste_symbole_lambdify]
|
|
||||||
#unbekanntenvektor_numerisch = sp.Matrix(self.func_u0(*liste_werte))
|
|
||||||
#unbekanntenvektor_numerisch = np.asarray(self.func_u0(*liste_werte), dtype=float).reshape(-1, 1)
|
|
||||||
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
#unbekanntenvektor_numerisch = unbekanntenvektor_neumerisch_vorherige_Iteration + dX_Vektor
|
unbekanntenvektor_numerisch_vorherige_Iteration = np.asarray(
|
||||||
unbekanntenvektor_neumerisch_vorherige_Iteration = np.asarray(
|
unbekanntenvektor_numerisch_vorherige_Iteration, dtype=float).reshape(-1, 1)
|
||||||
unbekanntenvektor_neumerisch_vorherige_Iteration, dtype=float).reshape(-1, 1)
|
|
||||||
dX_Vektor = np.asarray(dX_Vektor, dtype=float).reshape(-1, 1)
|
dX_Vektor = np.asarray(dX_Vektor, dtype=float).reshape(-1, 1)
|
||||||
|
unbekanntenvektor_numerisch = unbekanntenvektor_numerisch_vorherige_Iteration + dX_Vektor
|
||||||
|
|
||||||
unbekanntenvektor_numerisch = unbekanntenvektor_neumerisch_vorherige_Iteration + dX_Vektor
|
# Aktualisieren des Dictionaries für die Subsitutionen in den anderen Methoden.
|
||||||
|
|
||||||
self.substitutionen_dict = self.dict_substitutionen_uebergeordnetes_system(unbekanntenvektor_numerisch)
|
self.substitutionen_dict = self.dict_substitutionen_uebergeordnetes_system(unbekanntenvektor_numerisch)
|
||||||
|
|
||||||
Export.matrix_to_csv(fr"Zwischenergebnisse\{iterationsnummer}_Unbekanntenvektor_Numerisch_Iteration0.csv", [""],
|
Export.matrix_to_csv(fr"Zwischenergebnisse\{iterationsnummer}_Unbekanntenvektor_Numerisch.csv", [""],
|
||||||
liste_unbekanntenvektor_symbolisch, unbekanntenvektor_numerisch,
|
liste_unbekanntenvektor_symbolisch, unbekanntenvektor_numerisch,
|
||||||
"Unbekanntenvektor")
|
"Unbekanntenvektor")
|
||||||
return unbekanntenvektor_numerisch
|
return unbekanntenvektor_numerisch
|
||||||
|
|
||||||
def unbekanntenvektor_numerisch_to_dict_unbekanntenvektor(self, liste_unbekanntenvektor_symbolisch: list, unbekanntenvektor_numerisch: np.Matrix) -> dict:
|
def unbekanntenvektor_numerisch_to_dict_unbekanntenvektor(self, liste_unbekanntenvektor_symbolisch: list, unbekanntenvektor_numerisch: np.Matrix) -> dict:
|
||||||
dict_unbekanntenvektor_numerisch = {}
|
"""Wandelt einen numerischen Unbekanntenvektor in ein Koordinatendictionary um.
|
||||||
#index = 0
|
|
||||||
|
|
||||||
|
Aus dem numerischen Unbekanntenvektor werden für alle Punkte die Koordinaten (X, Y, Z) extrahiert
|
||||||
|
und als sp.Matrix([X, Y, Z]) in einem Dictionary gespeichert.
|
||||||
|
|
||||||
|
:param liste_unbekanntenvektor_symbolisch: Liste der Unbekannten (Symbole) in der Reihenfolge des numerischen Vektors.
|
||||||
|
:type liste_unbekanntenvektor_symbolisch: list
|
||||||
|
:param unbekanntenvektor_numerisch: Numerischer Unbekanntenvektor.
|
||||||
|
:type unbekanntenvektor_numerisch: np.Matrix
|
||||||
|
:return: Dictionary {punktnummer: sp.Matrix([X, Y, Z])}.
|
||||||
|
:rtype: dict
|
||||||
|
"""
|
||||||
unbekanntenvektor_numerisch = np.asarray(unbekanntenvektor_numerisch, dtype=float).reshape(-1, 1)
|
unbekanntenvektor_numerisch = np.asarray(unbekanntenvektor_numerisch, dtype=float).reshape(-1, 1)
|
||||||
|
|
||||||
idx = {str(sym): i for i, sym in enumerate(liste_unbekanntenvektor_symbolisch)}
|
idx = {str(sym): i for i, sym in enumerate(liste_unbekanntenvektor_symbolisch)}
|
||||||
|
|
||||||
punktnummern = []
|
punktnummern = []
|
||||||
for sym in liste_unbekanntenvektor_symbolisch:
|
for symbol in liste_unbekanntenvektor_symbolisch:
|
||||||
name = str(sym)
|
name = str(symbol)
|
||||||
if name.startswith("X"):
|
if name.startswith("X"):
|
||||||
pn = name[1:]
|
pn = name[1:]
|
||||||
if pn not in punktnummern:
|
if pn not in punktnummern:
|
||||||
punktnummern.append(pn)
|
punktnummern.append(pn)
|
||||||
|
|
||||||
|
# Annahme: Für jeden Punkt, für den eine X-Koordinate vorliegt, gibt es auch immer zwingend eine Y- und Z-Koordinate
|
||||||
dict_koordinaten = {}
|
dict_koordinaten = {}
|
||||||
for pn in punktnummern:
|
for pn in punktnummern:
|
||||||
iX = idx.get(f"X{pn}", None)
|
iX = idx.get(f"X{pn}", None)
|
||||||
@@ -739,19 +794,33 @@ class FunktionalesModell:
|
|||||||
float(unbekanntenvektor_numerisch[iZ, 0]),
|
float(unbekanntenvektor_numerisch[iZ, 0]),
|
||||||
])
|
])
|
||||||
|
|
||||||
#dict_unbekanntenvektor_numerisch[punktnummer] = sp.Matrix([
|
|
||||||
# float(unbekanntenvektor_numerisch[index, 0]),
|
|
||||||
# float(unbekanntenvektor_numerisch[index + 1, 0]),
|
|
||||||
# float(unbekanntenvektor_numerisch[index + 2, 0])
|
|
||||||
#])
|
|
||||||
#index += 3
|
|
||||||
return dict_koordinaten
|
return dict_koordinaten
|
||||||
|
|
||||||
def berechnung_dl(self, beobachtungsvektor_numerisch: np.Matrix, beobachtungsvektor_naeherung_numerisch: sp.Matrix,
|
def berechnung_dl(self, beobachtungsvektor_numerisch: np.Matrix, beobachtungsvektor_naeherung_numerisch: sp.Matrix,
|
||||||
liste_beobachtungsvektor_symbolisch: list = None, iterationsnummer: int = 0) -> np.Matrix:
|
liste_beobachtungsvektor_symbolisch: list = None, iterationsnummer: int = 0) -> np.asarray:
|
||||||
|
"""Berechnet den Verbesserungsvektor dl = l − f(x0).
|
||||||
|
|
||||||
|
Der Vektor wird als Differenz aus numerischem Beobachtungsvektor und numerischem Näherungs-Beobachtungsvektor gebildet.
|
||||||
|
Für Richtungsbeobachtungen wird dl normiert.
|
||||||
|
|
||||||
|
Der Vektor dl wird als CSV-Datei in den Ordner Zwischenergebnisse exportiert.
|
||||||
|
|
||||||
|
:param beobachtungsvektor_numerisch: Numerischer Beobachtungsvektor l.
|
||||||
|
:type beobachtungsvektor_numerisch: np.Matrix
|
||||||
|
:param beobachtungsvektor_naeherung_numerisch: Numerischer Näherungs-Beobachtungsvektor f(x0).
|
||||||
|
:type beobachtungsvektor_naeherung_numerisch: sp.Matrix
|
||||||
|
:param liste_beobachtungsvektor_symbolisch: Optional: Liste der Beobachtungskennungen.
|
||||||
|
:type liste_beobachtungsvektor_symbolisch: list | None
|
||||||
|
:param iterationsnummer: Iterationsnummer für Dateinamen der Zwischenergebnisse.
|
||||||
|
:type iterationsnummer: int
|
||||||
|
:return: Verbesserungsvektor dl.
|
||||||
|
:rtype: np.asarray
|
||||||
|
"""
|
||||||
dl = beobachtungsvektor_numerisch - beobachtungsvektor_naeherung_numerisch
|
dl = beobachtungsvektor_numerisch - beobachtungsvektor_naeherung_numerisch
|
||||||
|
# Umwandeln in einen Numpy-Array, um Rechenzeit im Vergleich zu einer sympy.MAtrix zu sparen
|
||||||
dl = np.asarray(dl, dtype=float)
|
dl = np.asarray(dl, dtype=float)
|
||||||
|
|
||||||
|
# Wird keine liste_beobachtungsvektor_symbolisch übergeben, wird diese aus der Instanzvariable verwendet.
|
||||||
if liste_beobachtungsvektor_symbolisch is None:
|
if liste_beobachtungsvektor_symbolisch is None:
|
||||||
liste_beobachtungsvektor_symbolisch = self.liste_beobachtungsvektor_symbolisch
|
liste_beobachtungsvektor_symbolisch = self.liste_beobachtungsvektor_symbolisch
|
||||||
|
|
||||||
@@ -770,11 +839,25 @@ class FunktionalesModell:
|
|||||||
return dl
|
return dl
|
||||||
|
|
||||||
def dict_substitutionen_uebergeordnetes_system(self,
|
def dict_substitutionen_uebergeordnetes_system(self,
|
||||||
unbekanntenvektor_aus_iteration: np.Matrix = None) -> dict[Any, Any]:
|
unbekanntenvektor_aus_iteration: np.asarray = None) -> dict[Any, Any]:
|
||||||
db_zugriff = Datenbankzugriff(self.pfad_datenbank)
|
"""Erstellt das Substitutions-Dictionary für das geozentrisch-kartesische System.
|
||||||
berechnungen = Berechnungen(self.a, self.b)
|
|
||||||
|
Es werden (abhängig davon, ob ein Unbekanntenvektor aus einer Iteration übergeben wurde) die aktuellen
|
||||||
|
Koordinaten und daraus abgeleitete Größen erzeugt und als Substitutionen abgelegt, u. a.:
|
||||||
|
|
||||||
|
- Punktkoordinaten X*, Y*, Z* sowie geodätische Breite B* und Länge L*,
|
||||||
|
- berechnete Größen aus Tachymeterbeziehungen (Azimut, Richtung, Zenitwinkel, Schrägstrecke),
|
||||||
|
- Normalhöhen NH (über Transformationen),
|
||||||
|
- Beobachtungssymbole der Messungen (Tachymeter, GNSS, Nivellement),
|
||||||
|
- Orientierungsunbekannte O.
|
||||||
|
|
||||||
|
:param unbekanntenvektor_aus_iteration: Optionaler numerischer Unbekanntenvektor einer Iteration zur Aktualisierung der Substitutionen.
|
||||||
|
:type unbekanntenvektor_aus_iteration: np.asarray | None
|
||||||
|
:return: Dictionary mit SymPy-Symbolen als Key und numerischen Werten als Value.
|
||||||
|
:rtype: dict[Any, Any]
|
||||||
|
"""
|
||||||
if unbekanntenvektor_aus_iteration is None:
|
if unbekanntenvektor_aus_iteration is None:
|
||||||
dict_koordinaten = db_zugriff.get_koordinaten("naeherung_us")
|
dict_koordinaten = self.db_zugriff.get_koordinaten("naeherung_us")
|
||||||
else:
|
else:
|
||||||
dict_koordinaten = self.unbekanntenvektor_numerisch_to_dict_unbekanntenvektor(
|
dict_koordinaten = self.unbekanntenvektor_numerisch_to_dict_unbekanntenvektor(
|
||||||
self.liste_unbekanntenvektor_symbolisch,
|
self.liste_unbekanntenvektor_symbolisch,
|
||||||
@@ -784,13 +867,15 @@ class FunktionalesModell:
|
|||||||
for punktnummer, matrix in dict_koordinaten.items():
|
for punktnummer, matrix in dict_koordinaten.items():
|
||||||
dict_koordinaten[punktnummer] = [float(matrix[0]), float(matrix[1]), float(matrix[2])]
|
dict_koordinaten[punktnummer] = [float(matrix[0]), float(matrix[1]), float(matrix[2])]
|
||||||
|
|
||||||
|
# Abfragen der Beobachtungen der einzelnen Beobachtungsarten aus der Tabelle Beobachtungen
|
||||||
|
liste_beobachtungen_tachymeter = self.db_zugriff.get_beobachtungen_from_beobachtungenid()
|
||||||
|
liste_beobachtungen_gnssbasislinien = self.db_zugriff.get_beobachtungen_gnssbasislinien()
|
||||||
|
liste_beobachtungen_nivellemente = self.db_zugriff.get_beobachtungen_nivellement()
|
||||||
|
liste_azimut_richtungen, dict_orientierungen = self.berechnungen.berechnung_richtung_azimut_zenitwinkel(self.pfad_datenbank, dict_koordinaten)
|
||||||
|
|
||||||
liste_beobachtungen_tachymeter = db_zugriff.get_beobachtungen_from_beobachtungenid()
|
# Erstellen von Dictionaries für die weitere Verarbeitung
|
||||||
liste_beobachtungen_gnssbasislinien = db_zugriff.get_beobachtungen_gnssbasislinien()
|
|
||||||
liste_beobachtungen_nivellemente = db_zugriff.get_beobachtungen_nivellement()
|
|
||||||
liste_azimut_richtungen, dict_orientierungen = berechnungen.berechnung_richtung_azimut_zenitwinkel(self.pfad_datenbank, dict_koordinaten)
|
|
||||||
dict_koordinaten_xyz_kopie = {pn: [v[0], v[1], v[2]] for pn, v in dict_koordinaten.items()}
|
dict_koordinaten_xyz_kopie = {pn: [v[0], v[1], v[2]] for pn, v in dict_koordinaten.items()}
|
||||||
dict_koordinaten_B_L = berechnungen.geometrische_breite_laenge(dict_koordinaten_xyz_kopie)
|
dict_koordinaten_B_L = self.berechnungen.geodätische_breite_laenge(dict_koordinaten_xyz_kopie)
|
||||||
|
|
||||||
punktnummern_niv = set()
|
punktnummern_niv = set()
|
||||||
for beobachtungenID, pn_sp, pn_zp, niv_dh, niv_strecke, niv_anz_standpkte in liste_beobachtungen_nivellemente:
|
for beobachtungenID, pn_sp, pn_zp, niv_dh, niv_strecke, niv_anz_standpkte in liste_beobachtungen_nivellemente:
|
||||||
@@ -806,8 +891,8 @@ class FunktionalesModell:
|
|||||||
dict_koordinaten_niv,
|
dict_koordinaten_niv,
|
||||||
self.pfad_tif_quasigeoidundolation)
|
self.pfad_tif_quasigeoidundolation)
|
||||||
|
|
||||||
|
# Zuweisen der Symbole zu dem jeweiligen numerischen Wert. Gespeichert wird dies in einem Dictionary.
|
||||||
substitutionen = {}
|
substitutionen = {}
|
||||||
|
|
||||||
for punktnummer, vektor in dict_koordinaten_B_L.items():
|
for punktnummer, vektor in dict_koordinaten_B_L.items():
|
||||||
X_sym, Y_sym, Z_sym, B_sym, L_Sym = sp.symbols(
|
X_sym, Y_sym, Z_sym, B_sym, L_Sym = sp.symbols(
|
||||||
f"X{punktnummer} Y{punktnummer} Z{punktnummer} B{punktnummer} L{punktnummer}")
|
f"X{punktnummer} Y{punktnummer} Z{punktnummer} B{punktnummer} L{punktnummer}")
|
||||||
@@ -818,10 +903,6 @@ class FunktionalesModell:
|
|||||||
substitutionen[B_sym] = float(vektor[1])
|
substitutionen[B_sym] = float(vektor[1])
|
||||||
substitutionen[L_Sym] = float(vektor[2])
|
substitutionen[L_Sym] = float(vektor[2])
|
||||||
|
|
||||||
#for beobachtungsgruppeID, orientierung in dict_orientierungen.items():
|
|
||||||
# O_sym = sp.symbols(f"O_{beobachtungsgruppeID}")
|
|
||||||
# substitutionen[O_sym] = float(orientierung)
|
|
||||||
|
|
||||||
for beobachtungsgruppeID, standpunkt, zielpunkt, azimut, richtung, zenitwinkel, schraegstrecke, orientierung in liste_azimut_richtungen:
|
for beobachtungsgruppeID, standpunkt, zielpunkt, azimut, richtung, zenitwinkel, schraegstrecke, orientierung in liste_azimut_richtungen:
|
||||||
richtung_sym = sp.symbols(f"richtung_berechnet_{beobachtungsgruppeID}_{standpunkt}_{zielpunkt}")
|
richtung_sym = sp.symbols(f"richtung_berechnet_{beobachtungsgruppeID}_{standpunkt}_{zielpunkt}")
|
||||||
substitutionen[richtung_sym] = float(richtung)
|
substitutionen[richtung_sym] = float(richtung)
|
||||||
@@ -839,8 +920,6 @@ class FunktionalesModell:
|
|||||||
normalhoehe_sym = sp.symbols(f"NH{punktnummer}")
|
normalhoehe_sym = sp.symbols(f"NH{punktnummer}")
|
||||||
substitutionen[normalhoehe_sym] = float(koordinaten_utm[2])
|
substitutionen[normalhoehe_sym] = float(koordinaten_utm[2])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
for standpunkt, zielpunkt, beobachtungenID, beobachtungsgruppeID, tachymeter_richtung, tachymeter_zenitwinkel, tachymeter_distanz in liste_beobachtungen_tachymeter:
|
for standpunkt, zielpunkt, beobachtungenID, beobachtungsgruppeID, tachymeter_richtung, tachymeter_zenitwinkel, tachymeter_distanz in liste_beobachtungen_tachymeter:
|
||||||
alpha = sp.symbols(f"{beobachtungenID}_R_{beobachtungsgruppeID}_{standpunkt}_{zielpunkt}")
|
alpha = sp.symbols(f"{beobachtungenID}_R_{beobachtungsgruppeID}_{standpunkt}_{zielpunkt}")
|
||||||
zw = sp.symbols(f"{beobachtungenID}_ZW_{beobachtungsgruppeID}_{standpunkt}_{zielpunkt}")
|
zw = sp.symbols(f"{beobachtungenID}_ZW_{beobachtungsgruppeID}_{standpunkt}_{zielpunkt}")
|
||||||
@@ -852,7 +931,6 @@ class FunktionalesModell:
|
|||||||
substitutionen[alpha] = float(tachymeter_richtung)
|
substitutionen[alpha] = float(tachymeter_richtung)
|
||||||
substitutionen[zw] = float(tachymeter_zenitwinkel)
|
substitutionen[zw] = float(tachymeter_zenitwinkel)
|
||||||
substitutionen[s] = float(tachymeter_distanz)
|
substitutionen[s] = float(tachymeter_distanz)
|
||||||
#substitutionen[sp.Symbol(f"O{beobachtungsgruppeID}")] = 0.0
|
|
||||||
|
|
||||||
for beobachtungenID, punktnummer_sp, punktnummer_zp, gnss_bx, gnss_by, gnss_bz, gnss_s0, gnss_cxx, gnss_cxy, gnss_cxz, gnss_cyy, gnss_cyz, gnss_czz in liste_beobachtungen_gnssbasislinien:
|
for beobachtungenID, punktnummer_sp, punktnummer_zp, gnss_bx, gnss_by, gnss_bz, gnss_s0, gnss_cxx, gnss_cxy, gnss_cxz, gnss_cyy, gnss_cyz, gnss_czz in liste_beobachtungen_gnssbasislinien:
|
||||||
beobachtungenID = str(beobachtungenID).strip()
|
beobachtungenID = str(beobachtungenID).strip()
|
||||||
@@ -890,10 +968,6 @@ class FunktionalesModell:
|
|||||||
for orientierungs_id, wert in dict_O.items():
|
for orientierungs_id, wert in dict_O.items():
|
||||||
substitutionen[sp.Symbol(f"O{orientierungs_id}")] = float(wert)
|
substitutionen[sp.Symbol(f"O{orientierungs_id}")] = float(wert)
|
||||||
else:
|
else:
|
||||||
#for standpunkt, zielpunkt, beobachtungenID, beobachtungsgruppeID, *_ in liste_beobachtungen_tachymeter:
|
|
||||||
# O_sym = sp.Symbol(f"O{beobachtungsgruppeID}")
|
|
||||||
# if O_sym not in substitutionen:
|
|
||||||
# substitutionen[O_sym] = 0
|
|
||||||
for beobachtungsgruppeID, standpunkt, zielpunkt, azimut, richtung, zenitwinkel, schraegstrecke, orientierung in liste_azimut_richtungen:
|
for beobachtungsgruppeID, standpunkt, zielpunkt, azimut, richtung, zenitwinkel, schraegstrecke, orientierung in liste_azimut_richtungen:
|
||||||
O_sym = sp.Symbol(f"O{beobachtungsgruppeID}")
|
O_sym = sp.Symbol(f"O{beobachtungsgruppeID}")
|
||||||
if O_sym not in substitutionen:
|
if O_sym not in substitutionen:
|
||||||
@@ -902,7 +976,18 @@ class FunktionalesModell:
|
|||||||
return substitutionen
|
return substitutionen
|
||||||
|
|
||||||
def unbekanntenvektor_numerisch_to_dict_orientierungen(self, liste_unbekanntenvektor_symbolisch: list,
|
def unbekanntenvektor_numerisch_to_dict_orientierungen(self, liste_unbekanntenvektor_symbolisch: list,
|
||||||
unbekanntenvektor_numerisch: np.Matrix) -> dict[Any, Any]:
|
unbekanntenvektor_numerisch: np.asarray) -> dict[Any, Any]:
|
||||||
|
"""Extrahiert Orientierungsparameter aus einem numerischen Unbekanntenvektor in ein Dictionary.
|
||||||
|
|
||||||
|
Alle Unbekannten, deren Symbolname mit "O" beginnt, werden als als Dictionary in der Form {orientierungs_id: wert} zurückgegeben.
|
||||||
|
|
||||||
|
:param liste_unbekanntenvektor_symbolisch: Liste der Unbekannten (Symbole) in der Reihenfolge des numerischen Vektors.
|
||||||
|
:type liste_unbekanntenvektor_symbolisch: list
|
||||||
|
:param unbekanntenvektor_numerisch: Numerischer Unbekanntenvektor.
|
||||||
|
:type unbekanntenvektor_numerisch: np.asarray
|
||||||
|
:return: Dictionary der Orientierungen je Beobachtungsgruppe.
|
||||||
|
:rtype: dict[Any, Any]
|
||||||
|
"""
|
||||||
dict_O = {}
|
dict_O = {}
|
||||||
unbekanntenvektor_numerisch = np.asarray(unbekanntenvektor_numerisch, dtype=float).reshape(-1, 1)
|
unbekanntenvektor_numerisch = np.asarray(unbekanntenvektor_numerisch, dtype=float).reshape(-1, 1)
|
||||||
for i, symbol in enumerate(liste_unbekanntenvektor_symbolisch):
|
for i, symbol in enumerate(liste_unbekanntenvektor_symbolisch):
|
||||||
@@ -911,5 +996,4 @@ class FunktionalesModell:
|
|||||||
orientierungs_id = name[1:]
|
orientierungs_id = name[1:]
|
||||||
dict_O[orientierungs_id] = float(unbekanntenvektor_numerisch[i, 0])
|
dict_O[orientierungs_id] = float(unbekanntenvektor_numerisch[i, 0])
|
||||||
|
|
||||||
return dict_O
|
return dict_O
|
||||||
|
|
||||||
465
Import.py
465
Import.py
@@ -1,37 +1,95 @@
|
|||||||
import csv
|
import csv
|
||||||
import sqlite3
|
|
||||||
from decimal import Decimal
|
|
||||||
from typing import Any
|
|
||||||
import re
|
|
||||||
import xml.etree.ElementTree as ET
|
|
||||||
from decimal import Decimal, getcontext, ROUND_HALF_UP
|
from decimal import Decimal, getcontext, ROUND_HALF_UP
|
||||||
|
import sqlite3
|
||||||
|
from typing import Any
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
|
||||||
from Berechnungen import Berechnungen
|
from Berechnungen import Berechnungen
|
||||||
|
|
||||||
|
|
||||||
import Berechnungen
|
import Berechnungen
|
||||||
|
|
||||||
|
|
||||||
class Import:
|
class Import:
|
||||||
def __init__(self, pfad_datenbank: str) -> None:
|
"""Importfunktionen für Messdaten und Näherungswerte in die SQLite-Datenbank.
|
||||||
|
|
||||||
|
Die Klasse stellt Methoden zur Verfügung für:
|
||||||
|
|
||||||
|
- Import von Näherungskoordinaten (LH-Tachymeter, GNSS/ECEF) in die Tabelle Netzpunkte,
|
||||||
|
- Import und Vorverarbeitung von Tachymeterbeobachtungen (CSV-Datei) inkl. optionaler Korrektur durch nicht gerundete Leica-JXL,
|
||||||
|
- Import von GNSS-Basislinien inkl. Kovarianzanteilen in die Tabelle Beobachtungen,
|
||||||
|
- Import von Nivellementdaten (Normalhöhen als Näherungen sowie RVVR-Züge als dh-Beobachtungen),
|
||||||
|
- einfache Plausibilitätsprüfungen (Duplikate, InstrumentenID, Dateiname bereits importiert).
|
||||||
|
"""
|
||||||
|
def __init__(self, pfad_datenbank: str, a : float, b : float) -> None:
|
||||||
|
"""Initialisiert die Importklasse.
|
||||||
|
|
||||||
|
Speichert den Pfad zur SQLite-Datenbank und initialisiert die Hilfsklasse Berechnungen.
|
||||||
|
|
||||||
|
:param a: Große Halbachse a des Referenzellipsoids in Meter.
|
||||||
|
:type a: float
|
||||||
|
:param b: Kleine Halbachse b des Referenzellipsoids in Meter.
|
||||||
|
:type b: float
|
||||||
|
:param pfad_datenbank: Pfad zur SQLite-Datenbank.
|
||||||
|
:type pfad_datenbank: str
|
||||||
|
:return: None
|
||||||
|
:rtype: None
|
||||||
|
"""
|
||||||
|
self.a = a
|
||||||
|
self.b = b
|
||||||
self.pfad_datenbank = pfad_datenbank
|
self.pfad_datenbank = pfad_datenbank
|
||||||
pass
|
self.berechnungen = Berechnungen.Berechnungen(self.a, self.b)
|
||||||
|
|
||||||
def string_to_float(self, zahl: str) -> float:
|
def string_to_float(self, zahl: str) -> float:
|
||||||
|
"""Konvertiert einen Zahlenstring in float.
|
||||||
|
|
||||||
|
Ersetzt das Dezimaltrennzeichen "," durch "." und führt anschließend float() aus.
|
||||||
|
|
||||||
|
:param zahl: Zahlenstring (z. B. "12,345" oder "12.345").
|
||||||
|
:type zahl: str
|
||||||
|
:return: Zahlenwert als float.
|
||||||
|
:rtype: float
|
||||||
|
"""
|
||||||
zahl = zahl.replace(',', '.')
|
zahl = zahl.replace(',', '.')
|
||||||
return float(zahl)
|
return float(zahl)
|
||||||
|
|
||||||
def string_to_decimal(self, zahl: str) -> Decimal:
|
def string_to_decimal(self, zahl: str) -> Decimal:
|
||||||
|
"""Konvertiert einen Zahlenstring in Decimal.
|
||||||
|
|
||||||
|
Ersetzt das Dezimaltrennzeichen "," durch "." und erzeugt anschließend ein Decimal.
|
||||||
|
|
||||||
|
:param zahl: Zahlenstring (z. B. "12,345" oder "12.345").
|
||||||
|
:type zahl: str
|
||||||
|
:return: Konvertierter Decimal-Wert.
|
||||||
|
:rtype: Decimal
|
||||||
|
"""
|
||||||
zahl = zahl.replace(',', '.')
|
zahl = zahl.replace(',', '.')
|
||||||
return Decimal(zahl)
|
return Decimal(zahl)
|
||||||
|
|
||||||
def import_koordinaten_lh_tachymeter(self, pfad_datei: str) -> None:
|
def import_koordinaten_lh_tachymeter(self, pfad_datei: str) -> None:
|
||||||
|
"""Importiert Näherungskoordinaten im lokalen Horizontsystem des Tachymeters in die Tabelle Netzpunkte.
|
||||||
|
|
||||||
|
Die Datei wird semikolon-separiert erwartet. Pro Zeile werden Punktnummer sowie X/Y/Z-Näherung
|
||||||
|
ausgelesen und in Netzpunkte (naeherungx_lh, naeherungy_lh, naeherungz_lh) geschrieben.
|
||||||
|
|
||||||
|
Vor dem Import werden zwei zentrale Prüfungen durchgeführt:
|
||||||
|
|
||||||
|
- Punktnummern dürfen in der Importdatei nicht doppelt vorkommen,
|
||||||
|
- Punktnummern aus der Importdatei dürfen noch nicht in der Tabelle Netzpunkte vorhanden sein.
|
||||||
|
|
||||||
|
:param pfad_datei: Pfad zur CSV-Datei mit Punktnummer und Koordinaten.
|
||||||
|
:type pfad_datei: str
|
||||||
|
:return: None
|
||||||
|
:rtype: None
|
||||||
|
"""
|
||||||
liste_punktnummern = []
|
liste_punktnummern = []
|
||||||
liste_punktnummern_vorher = []
|
liste_punktnummern_vorher = []
|
||||||
liste_punktnummern_vorher_db = []
|
liste_punktnummern_vorher_db = []
|
||||||
|
|
||||||
|
# Import_abbrechen wird True, sobald eine Fehler festgestellt wird. Als Folge wird der Import abgebrochen und eine Fehlermeldung mit Handlungshinweisen für den Benutzer ausgegeben.
|
||||||
Import_abbrechen = False
|
Import_abbrechen = False
|
||||||
|
|
||||||
|
|
||||||
with open (pfad_datei, newline='', encoding='utf-8') as csvfile:
|
with open (pfad_datei, newline='', encoding='utf-8') as csvfile:
|
||||||
|
# Abfragen aller in der Tabelle Netzpunkte enthaltenen Punktnummern
|
||||||
con = sqlite3.connect(self.pfad_datenbank)
|
con = sqlite3.connect(self.pfad_datenbank)
|
||||||
cursor = con.cursor()
|
cursor = con.cursor()
|
||||||
liste_punktnummern_db = [r[0] for r in cursor.execute("SELECT DISTINCT punktnummer FROM Netzpunkte").fetchall()]
|
liste_punktnummern_db = [r[0] for r in cursor.execute("SELECT DISTINCT punktnummer FROM Netzpunkte").fetchall()]
|
||||||
@@ -41,6 +99,7 @@ class Import:
|
|||||||
r = csv.reader(csvfile, delimiter=';')
|
r = csv.reader(csvfile, delimiter=';')
|
||||||
for row in r:
|
for row in r:
|
||||||
liste_punktnummern.append(row[0])
|
liste_punktnummern.append(row[0])
|
||||||
|
# Abbruch des Imports und Ausgabe einer Fehlermeldung, wenn ein Punkt doppelt in der Importdatei vorhanden ist.
|
||||||
if row[0] in liste_punktnummern_vorher:
|
if row[0] in liste_punktnummern_vorher:
|
||||||
Import_abbrechen = True
|
Import_abbrechen = True
|
||||||
print(f"Der Import wurde abgebrochen, weil in der Datei {pfad_datei} Punktnummern doppelt vorhanden sind. Bitte in der Datei ändern und Import wiederholen.")
|
print(f"Der Import wurde abgebrochen, weil in der Datei {pfad_datei} Punktnummern doppelt vorhanden sind. Bitte in der Datei ändern und Import wiederholen.")
|
||||||
@@ -48,12 +107,14 @@ class Import:
|
|||||||
|
|
||||||
liste_punktnummern_vorher.append(row[0])
|
liste_punktnummern_vorher.append(row[0])
|
||||||
|
|
||||||
|
# Abbruch des Imports und Ausgabe einer Fehlermeldung, wenn mindestens eine Punktnummer aus der Importdatei bereits in der Tabelle Netzpunkte vorhanden ist.
|
||||||
if row[0] in liste_punktnummern_db:
|
if row[0] in liste_punktnummern_db:
|
||||||
Import_abbrechen = True
|
Import_abbrechen = True
|
||||||
print(f"Der Import wurde abgebrochen, weil mindestens ein Teil der Punktnummern aus der Datei {pfad_datei} bereits in der Datenbank vorhanden ist. Bitte in der Datei ändern und Import wiederholen.")
|
print(f"Der Import wurde abgebrochen, weil mindestens ein Teil der Punktnummern aus der Datei {pfad_datei} bereits in der Datenbank vorhanden ist. Bitte in der Datei ändern und Import wiederholen.")
|
||||||
break
|
break
|
||||||
liste_punktnummern_vorher_db.append(row[0])
|
liste_punktnummern_vorher_db.append(row[0])
|
||||||
|
|
||||||
|
# Import durchführen, wenn keine Fehler festgestellt wurden
|
||||||
if Import_abbrechen == False:
|
if Import_abbrechen == False:
|
||||||
con = sqlite3.connect(self.pfad_datenbank)
|
con = sqlite3.connect(self.pfad_datenbank)
|
||||||
cursor = con.cursor()
|
cursor = con.cursor()
|
||||||
@@ -70,15 +131,30 @@ class Import:
|
|||||||
con.close()
|
con.close()
|
||||||
print("Der Import der Näherungskoordinaten wurde erfolgreich abgeschlossen")
|
print("Der Import der Näherungskoordinaten wurde erfolgreich abgeschlossen")
|
||||||
|
|
||||||
def ist_rundung_von_jxl(self, wert_csv: str, wert_jxl_voll: str) -> bool:
|
def ist_rundung_von_jxl(self, wert_csv: str, wert_jxl: str) -> bool:
|
||||||
|
"""Prüft, ob ein CSV-Wert eine Rundung eines JXL-Wertes (mit mehr Nachkommastellen) ist.
|
||||||
|
|
||||||
|
Es wird geprüft, ob:
|
||||||
|
|
||||||
|
- beide Werte echte Zahlen sind,
|
||||||
|
- der CSV-Wert weniger Nachkommastellen als der JXL-Wert besitzt,
|
||||||
|
- der JXL-Wert auf die Nachkommastellenanzahl des CSV-Wertes gerundet exakt dem CSV-Wert entspricht.
|
||||||
|
|
||||||
|
:param wert_csv: Wert aus der CSV-Datei.
|
||||||
|
:type wert_csv: str
|
||||||
|
:param wert_jxl: Wert aus der JXL-Datei.
|
||||||
|
:type wert_jxl: str
|
||||||
|
:return: True, wenn wert_csv eine Rundung von wert_jxl auf weniger Nachkommastellen darstellt, sonst False.
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
wert_csv = str(wert_csv).strip()
|
wert_csv = str(wert_csv).strip()
|
||||||
wert_jxl_voll = str(wert_jxl_voll).strip()
|
wert_jxl = str(wert_jxl).strip()
|
||||||
|
|
||||||
if ":ZH:" in wert_csv:
|
if ":ZH:" in wert_csv:
|
||||||
wert_csv = wert_csv.split(":ZH:", 1)[0].strip()
|
wert_csv = wert_csv.split(":ZH:", 1)[0].strip()
|
||||||
|
|
||||||
if ":ZH:" in wert_jxl_voll:
|
if ":ZH:" in wert_jxl:
|
||||||
wert_jxl_voll = wert_jxl_voll.split(":ZH:", 1)[0].strip()
|
wert_jxl = wert_jxl.split(":ZH:", 1)[0].strip()
|
||||||
|
|
||||||
def ist_zahl(text: str) -> bool:
|
def ist_zahl(text: str) -> bool:
|
||||||
text = str(text).strip()
|
text = str(text).strip()
|
||||||
@@ -115,9 +191,10 @@ class Import:
|
|||||||
if ist_zahl(wert_csv) == False:
|
if ist_zahl(wert_csv) == False:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if ist_zahl(wert_jxl_voll) == False:
|
if ist_zahl(wert_jxl) == False:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
# Ermittlung der Anzahl Nachkommastellen in CSV-Datei und JXL-Datei
|
||||||
anzahl_nachkommastellen_csv = 0
|
anzahl_nachkommastellen_csv = 0
|
||||||
if "," in wert_csv:
|
if "," in wert_csv:
|
||||||
anzahl_nachkommastellen_csv = len(wert_csv.split(",", 1)[1])
|
anzahl_nachkommastellen_csv = len(wert_csv.split(",", 1)[1])
|
||||||
@@ -125,16 +202,16 @@ class Import:
|
|||||||
anzahl_nachkommastellen_csv = len(wert_csv.split(".", 1)[1])
|
anzahl_nachkommastellen_csv = len(wert_csv.split(".", 1)[1])
|
||||||
|
|
||||||
anzahl_nachkommastellen_jxl = 0
|
anzahl_nachkommastellen_jxl = 0
|
||||||
if "," in wert_jxl_voll:
|
if "," in wert_jxl:
|
||||||
anzahl_nachkommastellen_jxl = len(wert_jxl_voll.split(",", 1)[1])
|
anzahl_nachkommastellen_jxl = len(wert_jxl.split(",", 1)[1])
|
||||||
elif "." in wert_jxl_voll:
|
elif "." in wert_jxl:
|
||||||
anzahl_nachkommastellen_jxl = len(wert_jxl_voll.split(".", 1)[1])
|
anzahl_nachkommastellen_jxl = len(wert_jxl.split(".", 1)[1])
|
||||||
|
|
||||||
if anzahl_nachkommastellen_csv >= anzahl_nachkommastellen_jxl:
|
if anzahl_nachkommastellen_csv >= anzahl_nachkommastellen_jxl:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
wert_csv_decimal = self.string_to_decimal(wert_csv.replace(".", ","))
|
wert_csv_decimal = self.string_to_decimal(wert_csv.replace(".", ","))
|
||||||
wert_jxl_decimal = self.string_to_decimal(wert_jxl_voll.replace(".", ","))
|
wert_jxl_decimal = self.string_to_decimal(wert_jxl.replace(".", ","))
|
||||||
|
|
||||||
q = Decimal("1") if anzahl_nachkommastellen_csv == 0 else Decimal("1." + ("0" * anzahl_nachkommastellen_csv))
|
q = Decimal("1") if anzahl_nachkommastellen_csv == 0 else Decimal("1." + ("0" * anzahl_nachkommastellen_csv))
|
||||||
wert_jxl_gerundet = wert_jxl_decimal.quantize(q, rounding=ROUND_HALF_UP)
|
wert_jxl_gerundet = wert_jxl_decimal.quantize(q, rounding=ROUND_HALF_UP)
|
||||||
@@ -143,6 +220,13 @@ class Import:
|
|||||||
return wert_jxl_gerundet == wert_csv_gerundet
|
return wert_jxl_gerundet == wert_csv_gerundet
|
||||||
|
|
||||||
def ist_zahl_csv(self, text: str) -> bool:
|
def ist_zahl_csv(self, text: str) -> bool:
|
||||||
|
"""Prüft, ob ein String in der CSV als Zahl interpretiert werden kann.
|
||||||
|
|
||||||
|
:param text: Zu prüfender Text.
|
||||||
|
:type text: str
|
||||||
|
:return: True, wenn der Text eine Zahl ist, sonst False.
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
text = str(text).strip()
|
text = str(text).strip()
|
||||||
if text == "":
|
if text == "":
|
||||||
return False
|
return False
|
||||||
@@ -172,58 +256,92 @@ class Import:
|
|||||||
pfad_datei_csv: str,
|
pfad_datei_csv: str,
|
||||||
pfad_datei_jxl: str,
|
pfad_datei_jxl: str,
|
||||||
pfad_datei_csv_out: str) -> dict:
|
pfad_datei_csv_out: str) -> dict:
|
||||||
Import_fortsetzen = True
|
"""Korrigiert Tachymeterbeobachtungen in einer CSV-Datei über eine JXL-Datei (Leica XML).
|
||||||
getcontext().prec = 70
|
|
||||||
|
|
||||||
|
Die JXL-Datei wird eingelesen und je StationID eine Sequenz an Beobachtungen (Zielpunktname,
|
||||||
|
Horizontal-/Vertikalkreisablesung, Schrägdistanz sowie Prismenhöhe) aufgebaut. Die Winkel werden
|
||||||
|
von Grad nach gon umgerechnet und in der Nachkommastellenauflösung der JXL gerundet.
|
||||||
|
|
||||||
|
Anschließend wird die CSV zeilenweise verarbeitet:
|
||||||
|
|
||||||
|
- Standpunktzeilen werden der zugehörigen StationID/IH aus der JXL zugeordnet,
|
||||||
|
- Beobachtungszeilen werden falls der CSV-Wert eine Rundung des JXL-Werts ist durch den JXL-Wert mit voller Nachkommastellenzahl ersetzt,
|
||||||
|
- Prismenhöhen (ZH) werden aus der JXL übernommen und in der letzten Spalte als ":ZH:" angehängt.
|
||||||
|
|
||||||
|
Zusätzlich werden Fehler-Listen erstellt (fehlende IH, fehlende ZH, Standpunkte in CSV aber nicht in JXL). Diese werden dem Benutzer übergeben.
|
||||||
|
|
||||||
|
:param pfad_datei_csv: Pfad zur Eingabe-CSV (Tachymeterbeobachtungen).
|
||||||
|
:type pfad_datei_csv: str
|
||||||
|
:param pfad_datei_jxl: Pfad zur JXL-Datei.
|
||||||
|
:type pfad_datei_jxl: str
|
||||||
|
:param pfad_datei_csv_out: Pfad zur Ausgabe-CSV (korrigierte Datei).
|
||||||
|
:type pfad_datei_csv_out: str
|
||||||
|
:return: Ergebnisdictionary mit Status und Diagnosen, u. a.:
|
||||||
|
- "Import_fortsetzen" (bool),
|
||||||
|
- "dict_ersetzungen" (dict mit Zählungen für "Hz", "Z", "SD"),
|
||||||
|
- "liste_zeilen_ohne_IH" (list),
|
||||||
|
- "liste_zeilen_ohne_ZH" (list),
|
||||||
|
- "liste_zeilen_standpunkt_nicht_in_jxl" (list),
|
||||||
|
- "pfad_datei_csv_out" (str).
|
||||||
|
:rtype: dict
|
||||||
|
"""
|
||||||
|
# Import_fortsetzen wird False, sobald eine Fehler festgestellt wird. Als Folge wird der Import abgebrochen und eine Fehlermeldung mit Handlungshinweisen für den Benutzer ausgegeben.
|
||||||
|
Import_fortsetzen = True
|
||||||
|
|
||||||
|
getcontext().prec = 70
|
||||||
dict_ersetzungen = {"Hz": 0, "Z": 0, "SD": 0}
|
dict_ersetzungen = {"Hz": 0, "Z": 0, "SD": 0}
|
||||||
|
|
||||||
|
# IH = Instrumentenhöhe | ZH = Zielhöhe, bzw. Prismenhöhe
|
||||||
liste_zeilen_ohne_IH = []
|
liste_zeilen_ohne_IH = []
|
||||||
liste_zeilen_ohne_ZH = []
|
liste_zeilen_ohne_ZH = []
|
||||||
liste_zeilen_standpunkt_nicht_in_jxl = []
|
liste_zeilen_standpunkt_nicht_in_jxl = []
|
||||||
|
|
||||||
liste_stationrecords = []
|
liste_tachymeterstandpunkte = []
|
||||||
|
|
||||||
dict_stationname_stationrecords = {}
|
dict_stationname_tachymeterstandpunkte = {}
|
||||||
dict_stationname_zaehler = {}
|
dict_standpunkte_anzahl = {}
|
||||||
|
|
||||||
dict_targetID_zu_ZH = {}
|
dict_targetID_zu_ZH = {}
|
||||||
dict_stationID_zu_seq = {}
|
dict_stationID_zu_seq = {}
|
||||||
|
|
||||||
dict_stationnamen = {}
|
dict_stationnamen = {}
|
||||||
|
|
||||||
|
# Vorbereitung jxl-Datei lesen
|
||||||
tree = ET.parse(pfad_datei_jxl)
|
tree = ET.parse(pfad_datei_jxl)
|
||||||
root = tree.getroot()
|
root = tree.getroot()
|
||||||
|
|
||||||
|
# JXL-Datei auslesen
|
||||||
if Import_fortsetzen:
|
if Import_fortsetzen:
|
||||||
# StationRecords einlesen (Standpunkt, StationID, Instrumentenhöhe)
|
# Standpunkt, StationID, Instrumentenhöhe aus der JXL-Datei auslesen
|
||||||
for sr in root.iter("StationRecord"):
|
for sr in root.iter("StationRecord"):
|
||||||
stationname = (sr.findtext("StationName") or "").strip()
|
stationname = (sr.findtext("StationName") or "").strip()
|
||||||
station_id = (sr.attrib.get("ID") or "").strip()
|
station_id = (sr.attrib.get("ID") or "").strip()
|
||||||
ih = (sr.findtext("TheodoliteHeight") or "").strip()
|
ih = (sr.findtext("TheodoliteHeight") or "").strip()
|
||||||
|
|
||||||
if stationname != "" and station_id != "":
|
if stationname != "" and station_id != "":
|
||||||
liste_stationrecords.append((stationname, station_id, ih))
|
liste_tachymeterstandpunkte.append((stationname, station_id, ih))
|
||||||
dict_stationnamen[stationname] = 1
|
dict_stationnamen[stationname] = 1
|
||||||
|
|
||||||
if stationname not in dict_stationname_stationrecords:
|
if stationname not in dict_stationname_tachymeterstandpunkte:
|
||||||
dict_stationname_stationrecords[stationname] = []
|
dict_stationname_tachymeterstandpunkte[stationname] = []
|
||||||
dict_stationname_stationrecords[stationname].append((station_id, ih))
|
dict_stationname_tachymeterstandpunkte[stationname].append((station_id, ih))
|
||||||
|
|
||||||
for stationname in dict_stationname_stationrecords.keys():
|
for stationname in dict_stationname_tachymeterstandpunkte.keys():
|
||||||
dict_stationname_zaehler[stationname] = 0
|
dict_standpunkte_anzahl[stationname] = 0
|
||||||
|
|
||||||
|
# Prismenhöhe auslesen und in Dict speichern
|
||||||
for tr in root.iter("TargetRecord"):
|
for tr in root.iter("TargetRecord"):
|
||||||
target_id = (tr.attrib.get("ID") or "").strip()
|
target_id = (tr.attrib.get("ID") or "").strip()
|
||||||
zh = (tr.findtext("TargetHeight") or "").strip()
|
zh = (tr.findtext("TargetHeight") or "").strip()
|
||||||
if target_id != "":
|
if target_id != "":
|
||||||
dict_targetID_zu_ZH[target_id] = zh
|
dict_targetID_zu_ZH[target_id] = zh
|
||||||
|
|
||||||
for tupel in liste_stationrecords:
|
for tupel in liste_tachymeterstandpunkte:
|
||||||
station_id = tupel[1]
|
station_id = tupel[1]
|
||||||
if station_id not in dict_stationID_zu_seq:
|
if station_id not in dict_stationID_zu_seq:
|
||||||
dict_stationID_zu_seq[station_id] = []
|
dict_stationID_zu_seq[station_id] = []
|
||||||
|
|
||||||
|
# Horizontal- und Vertikalkreisablesungen, sowie Schrägdistanzablesungen aus JXL-Datei auslesen
|
||||||
for pr in root.iter("PointRecord"):
|
for pr in root.iter("PointRecord"):
|
||||||
station_id = (pr.findtext("StationID") or "").strip()
|
station_id = (pr.findtext("StationID") or "").strip()
|
||||||
if station_id == "" or station_id not in dict_stationID_zu_seq:
|
if station_id == "" or station_id not in dict_stationID_zu_seq:
|
||||||
@@ -267,13 +385,14 @@ class Import:
|
|||||||
z_gon_decimal = z_gon_decimal.quantize(q_z, rounding=ROUND_HALF_UP)
|
z_gon_decimal = z_gon_decimal.quantize(q_z, rounding=ROUND_HALF_UP)
|
||||||
sd_decimal = Decimal(sd_m).quantize(q_sd, rounding=ROUND_HALF_UP)
|
sd_decimal = Decimal(sd_m).quantize(q_sd, rounding=ROUND_HALF_UP)
|
||||||
|
|
||||||
# Ausgabe mit Komma
|
# Ausgabe mit Kommatrennung
|
||||||
hz_gon_text = format(hz_gon_decimal, "f").replace(".", ",")
|
hz_gon_text = format(hz_gon_decimal, "f").replace(".", ",")
|
||||||
z_gon_text = format(z_gon_decimal, "f").replace(".", ",")
|
z_gon_text = format(z_gon_decimal, "f").replace(".", ",")
|
||||||
sd_text = format(sd_decimal, "f").replace(".", ",")
|
sd_text = format(sd_decimal, "f").replace(".", ",")
|
||||||
|
|
||||||
zh = dict_targetID_zu_ZH.get(target_id, "")
|
zh = dict_targetID_zu_ZH.get(target_id, "")
|
||||||
|
|
||||||
|
# Dictionary mit den Abfragen für die Weiterverarbeitung füllen.
|
||||||
dict_stationID_zu_seq[station_id].append({
|
dict_stationID_zu_seq[station_id].append({
|
||||||
"target": zielpunkt_name,
|
"target": zielpunkt_name,
|
||||||
"hz_gon": hz_gon_text,
|
"hz_gon": hz_gon_text,
|
||||||
@@ -283,7 +402,7 @@ class Import:
|
|||||||
})
|
})
|
||||||
|
|
||||||
station_id_aktuell = None
|
station_id_aktuell = None
|
||||||
index_seq_aktuell = 0
|
index_csv_jxl_aktuell = 0
|
||||||
standpunkt_aktuell = None
|
standpunkt_aktuell = None
|
||||||
|
|
||||||
# CSV-Datei zeilenweise durchgehen und ggf. Werte ersetzen
|
# CSV-Datei zeilenweise durchgehen und ggf. Werte ersetzen
|
||||||
@@ -304,9 +423,10 @@ class Import:
|
|||||||
standpunkt = row[0].strip()
|
standpunkt = row[0].strip()
|
||||||
|
|
||||||
if standpunkt in dict_stationnamen:
|
if standpunkt in dict_stationnamen:
|
||||||
zaehler = dict_stationname_zaehler.get(standpunkt, 0)
|
zaehler = dict_standpunkte_anzahl.get(standpunkt, 0)
|
||||||
liste_records = dict_stationname_stationrecords[standpunkt]
|
liste_records = dict_stationname_tachymeterstandpunkte[standpunkt]
|
||||||
|
|
||||||
|
# Überprüfung, ob in beiden Dateien die selben Tachymeterstandpunkte vorhanden sind
|
||||||
if zaehler >= len(liste_records):
|
if zaehler >= len(liste_records):
|
||||||
Import_fortsetzen = False
|
Import_fortsetzen = False
|
||||||
print(
|
print(
|
||||||
@@ -314,18 +434,20 @@ class Import:
|
|||||||
break
|
break
|
||||||
|
|
||||||
station_id, ih = liste_records[zaehler]
|
station_id, ih = liste_records[zaehler]
|
||||||
dict_stationname_zaehler[standpunkt] = zaehler + 1
|
dict_standpunkte_anzahl[standpunkt] = zaehler + 1
|
||||||
|
|
||||||
station_id_aktuell = station_id
|
station_id_aktuell = station_id
|
||||||
index_seq_aktuell = 0
|
index_csv_jxl_aktuell = 0
|
||||||
standpunkt_aktuell = standpunkt
|
standpunkt_aktuell = standpunkt
|
||||||
|
|
||||||
|
# Erstellen einer Liste mit allen Zeilennummern aus der JXL, für die keine Instrumentenhöhe vorliegt, damit dies als Fehlermeldung mit Bearbeitungshinweisen ausgegeben werden kann.
|
||||||
if ih is None or str(ih).strip() == "":
|
if ih is None or str(ih).strip() == "":
|
||||||
liste_zeilen_ohne_IH.append((nummer_zeile, standpunkt))
|
liste_zeilen_ohne_IH.append((nummer_zeile, standpunkt))
|
||||||
|
|
||||||
writer.writerow([standpunkt, f"IH:{ih}", "", "", ""])
|
writer.writerow([standpunkt, f"IH:{ih}", "", "", ""])
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# Erstellen einer Liste mit allen Zeilennummern aus der JXL, für die keine Prismenhöhe vorliegt, damit dies als Fehlermeldung mit Bearbeitungshinweisen ausgegeben werden kann.
|
||||||
if standpunkt.isdigit():
|
if standpunkt.isdigit():
|
||||||
liste_zeilen_standpunkt_nicht_in_jxl.append((nummer_zeile, standpunkt))
|
liste_zeilen_standpunkt_nicht_in_jxl.append((nummer_zeile, standpunkt))
|
||||||
|
|
||||||
@@ -338,8 +460,6 @@ class Import:
|
|||||||
wert_z = row[2].split(":ZH:", 1)[0].strip()
|
wert_z = row[2].split(":ZH:", 1)[0].strip()
|
||||||
wert_sd = row[3].split(":ZH:", 1)[0].strip()
|
wert_sd = row[3].split(":ZH:", 1)[0].strip()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if self.ist_zahl_csv(wert_hz) and self.ist_zahl_csv(wert_z) and self.ist_zahl_csv(wert_sd):
|
if self.ist_zahl_csv(wert_hz) and self.ist_zahl_csv(wert_z) and self.ist_zahl_csv(wert_sd):
|
||||||
ist_beobachtung = True
|
ist_beobachtung = True
|
||||||
|
|
||||||
@@ -355,21 +475,23 @@ class Import:
|
|||||||
writer.writerow(row)
|
writer.writerow(row)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if index_seq_aktuell >= len(liste_seq):
|
if index_csv_jxl_aktuell >= len(liste_seq):
|
||||||
writer.writerow(row)
|
writer.writerow(row)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
jxl_eintrag = liste_seq[index_seq_aktuell]
|
# Abfragen der selben Daten aus der JXL-Datei über den index
|
||||||
index_neu = index_seq_aktuell + 1
|
jxl_eintrag = liste_seq[index_csv_jxl_aktuell]
|
||||||
|
index_neu = index_csv_jxl_aktuell + 1
|
||||||
|
|
||||||
if jxl_eintrag["target"] != zielpunkt:
|
if jxl_eintrag["target"] != zielpunkt:
|
||||||
index_ende = min(len(liste_seq), index_seq_aktuell + 200)
|
index_ende = min(len(liste_seq), index_csv_jxl_aktuell + 200)
|
||||||
liste_kandidaten = []
|
liste_kandidaten = []
|
||||||
|
|
||||||
for index_kandidat in range(index_seq_aktuell, index_ende):
|
for index_kandidat in range(index_csv_jxl_aktuell, index_ende):
|
||||||
if liste_seq[index_kandidat]["target"] == zielpunkt:
|
if liste_seq[index_kandidat]["target"] == zielpunkt:
|
||||||
liste_kandidaten.append((index_kandidat, liste_seq[index_kandidat]))
|
liste_kandidaten.append((index_kandidat, liste_seq[index_kandidat]))
|
||||||
|
|
||||||
|
# Überprüfung, ob die Ablesungen in der CSV-Datei wirklich Rundungen der selben Daten mit mehr Nachkommastellen aus der JXL-Datei sind und abspeichern der Prüfergebnisse.
|
||||||
if liste_kandidaten != []:
|
if liste_kandidaten != []:
|
||||||
if len(liste_kandidaten) == 1:
|
if len(liste_kandidaten) == 1:
|
||||||
index_kandidat, kandidat = liste_kandidaten[0]
|
index_kandidat, kandidat = liste_kandidaten[0]
|
||||||
@@ -392,9 +514,9 @@ class Import:
|
|||||||
jxl_eintrag = kandidat_best
|
jxl_eintrag = kandidat_best
|
||||||
index_neu = index_best + 1
|
index_neu = index_best + 1
|
||||||
|
|
||||||
index_seq_aktuell = index_neu
|
index_csv_jxl_aktuell = index_neu
|
||||||
|
|
||||||
# Nur ersetzen, wenn die CSV-Werte tatsächlich eine Rundung der JXL-Werte sind
|
# Nur in der CSV-Datei ersetzen, wenn die CSV-Werte tatsächlich eine Rundung der JXL-Werte sind
|
||||||
hz_out = hz_csv
|
hz_out = hz_csv
|
||||||
z_out = z_csv
|
z_out = z_csv
|
||||||
sd_out = sd_csv
|
sd_out = sd_csv
|
||||||
@@ -423,24 +545,27 @@ class Import:
|
|||||||
|
|
||||||
if Import_fortsetzen:
|
if Import_fortsetzen:
|
||||||
print(f"Korrektur erfolgreich abgeschlossen. Ausgabe: {pfad_datei_csv_out}")
|
print(f"Korrektur erfolgreich abgeschlossen. Ausgabe: {pfad_datei_csv_out}")
|
||||||
print(f"Ersetzungen (Rundung -> JXL volle Nachkommastellen): {dict_ersetzungen}")
|
print(f"Ersetzungen in der CSV-Datei (Rundung -> JXL volle Nachkommastellen): {dict_ersetzungen}")
|
||||||
|
|
||||||
print("\n--- Fehlende IH ---")
|
# Ausgabe der Zeilennummern in der JXL-Datei ohne Instrumentenhöhe
|
||||||
print(f"Anzahl: {len(liste_zeilen_ohne_IH)}")
|
|
||||||
if len(liste_zeilen_ohne_IH) > 0:
|
if len(liste_zeilen_ohne_IH) > 0:
|
||||||
|
print("\n--- Fehlende IH in JXL-Datei ---")
|
||||||
|
print(f"Anzahl: {len(liste_zeilen_ohne_IH)}")
|
||||||
print(liste_zeilen_ohne_IH)
|
print(liste_zeilen_ohne_IH)
|
||||||
|
|
||||||
print("\n--- Fehlende ZH ---")
|
# Ausgabe der Zeilennummern in der JXL-Datei ohne Prismenhöhe
|
||||||
print(f"Anzahl: {len(liste_zeilen_ohne_ZH)}")
|
|
||||||
if len(liste_zeilen_ohne_ZH) > 0:
|
if len(liste_zeilen_ohne_ZH) > 0:
|
||||||
|
print("\n--- Fehlende ZH ---")
|
||||||
|
print(f"Anzahl: {len(liste_zeilen_ohne_ZH)}")
|
||||||
print(liste_zeilen_ohne_ZH)
|
print(liste_zeilen_ohne_ZH)
|
||||||
|
|
||||||
print("\n--- Standpunkt in CSV, aber kein StationRecord in JXL ---")
|
# Ausgabe der Zeilennummern in der JXL-Datei mit unterschiedlichen Tachymeterstandpunkten im Vergleich zur CSV-Datei
|
||||||
print(f"Anzahl: {len(liste_zeilen_standpunkt_nicht_in_jxl)}")
|
|
||||||
if len(liste_zeilen_standpunkt_nicht_in_jxl) > 0:
|
if len(liste_zeilen_standpunkt_nicht_in_jxl) > 0:
|
||||||
|
print("\n--- Standpunkt in CSV-Datei, aber nicht in JXL-Datei---")
|
||||||
|
print(f"Anzahl: {len(liste_zeilen_standpunkt_nicht_in_jxl)}")
|
||||||
print(liste_zeilen_standpunkt_nicht_in_jxl)
|
print(liste_zeilen_standpunkt_nicht_in_jxl)
|
||||||
else:
|
else:
|
||||||
print("Die Korrektur wurde abgebrochen.")
|
print("Die Korrektur der CSV-Datei wurde abgebrochen.")
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"Import_fortsetzen": Import_fortsetzen,
|
"Import_fortsetzen": Import_fortsetzen,
|
||||||
@@ -451,8 +576,32 @@ class Import:
|
|||||||
"pfad_datei_csv_out": pfad_datei_csv_out
|
"pfad_datei_csv_out": pfad_datei_csv_out
|
||||||
}
|
}
|
||||||
|
|
||||||
def import_beobachtungen_tachymeter(self, pfad_datei: str, instrumentenID: int, a: float, b: float) -> None:
|
def import_beobachtungen_tachymeter(self, pfad_datei: str, instrumentenID: int) -> None:
|
||||||
berechnungen = Berechnungen.Berechnungen(a, b)
|
"""Importiert Tachymeterbeobachtungen aus einer CSV-Datei in die Tabelle Beobachtungen.
|
||||||
|
|
||||||
|
Die Datei wird blockweise verarbeitet (Standpunktzeile + Beobachtungszeilen). Aus je zwei Halbsätzen
|
||||||
|
wird ein Vollsatz abgeleitet:
|
||||||
|
|
||||||
|
- Richtung: Mittel aus Hz(HS1) und Hz(HS2-200 gon), normiert in [0, 400),
|
||||||
|
- Zenitwinkel: (ZW1 - ZW2 + 400) / 2,
|
||||||
|
- Schrägdistanz: Mittel der beiden SD.
|
||||||
|
|
||||||
|
Zusätzlich werden aus Instrumenten- und Prismenhöhe bodenbezogene Werte berechnet
|
||||||
|
(schraegdistanz_bodenbezogen, zenitwinkel_bodenbezogen) über berechne_zenitwinkel_distanz_bodenbezogen().
|
||||||
|
|
||||||
|
Vorabprüfungen:
|
||||||
|
|
||||||
|
- Abbruch, wenn der Dateiname bereits in Beobachtungen vorkommt,
|
||||||
|
- Abbruch, wenn instrumentenID nicht in Tabelle Instrumente existiert,
|
||||||
|
- Plausibilitätsprüfung auf Vollsatz-Struktur (6 Zeilen pro Zielpunkt: 3 Vollsätze * 2 Halbsätze).
|
||||||
|
|
||||||
|
:param pfad_datei: Pfad zur CSV-Datei.
|
||||||
|
:type pfad_datei: str
|
||||||
|
:param instrumentenID: ID des verwendeten Instruments aus Tabelle Instrumente.
|
||||||
|
:type instrumentenID: int
|
||||||
|
:return: None
|
||||||
|
:rtype: None
|
||||||
|
"""
|
||||||
|
|
||||||
# Prüfen, ob Bereits Daten aus der Datei in der Datenbank vorhanden sind
|
# Prüfen, ob Bereits Daten aus der Datei in der Datenbank vorhanden sind
|
||||||
con = sqlite3.connect(self.pfad_datenbank)
|
con = sqlite3.connect(self.pfad_datenbank)
|
||||||
@@ -464,16 +613,19 @@ class Import:
|
|||||||
FROM Beobachtungen""").fetchall()]
|
FROM Beobachtungen""").fetchall()]
|
||||||
liste_instrumentenid = [r[0] for r in cursor.execute("SELECT instrumenteID FROM Instrumente").fetchall()]
|
liste_instrumentenid = [r[0] for r in cursor.execute("SELECT instrumenteID FROM Instrumente").fetchall()]
|
||||||
|
|
||||||
|
cursor.close()
|
||||||
con.close()
|
con.close()
|
||||||
cursor.close
|
|
||||||
|
|
||||||
|
# Import_fortsetzen wird False, sobald eine Fehler festgestellt wird. Als Folge wird der Import abgebrochen und eine Fehlermeldung mit Handlungshinweisen für den Benutzer ausgegeben.
|
||||||
Import_fortsetzen = True
|
Import_fortsetzen = True
|
||||||
|
|
||||||
|
# Abbrechen des Imports, wenn bereits Daten aus der selben Datei importiert wurden.
|
||||||
if pfad_datei in liste_dateinamen_in_db:
|
if pfad_datei in liste_dateinamen_in_db:
|
||||||
Import_fortsetzen = False
|
Import_fortsetzen = False
|
||||||
|
|
||||||
if Import_fortsetzen:
|
if Import_fortsetzen:
|
||||||
nummer_zielpunkt = 0
|
nummer_zielpunkt = 0
|
||||||
|
# Abfragen der aktuell höschten Nummer im Attribut beobachtungsgruppeID der Tabelle Beobachtungen
|
||||||
try:
|
try:
|
||||||
nummer_beobachtungsgruppeID = max(liste_beobachtungsgruppeID)
|
nummer_beobachtungsgruppeID = max(liste_beobachtungsgruppeID)
|
||||||
except:
|
except:
|
||||||
@@ -487,20 +639,22 @@ class Import:
|
|||||||
liste_beobachtungen_vorbereitung = []
|
liste_beobachtungen_vorbereitung = []
|
||||||
|
|
||||||
for i, zeile in enumerate(f):
|
for i, zeile in enumerate(f):
|
||||||
|
# Die ersten drei Zeilen der Importdatei müssen überprungen werden
|
||||||
if i < 3:
|
if i < 3:
|
||||||
continue
|
continue
|
||||||
zeile = zeile.strip().split(";")
|
zeile = zeile.strip().split(";")
|
||||||
if len(zeile) < 5:
|
if len(zeile) < 5:
|
||||||
zeile = zeile + [""] * (5 - len(zeile))
|
zeile = zeile + [""] * (5 - len(zeile))
|
||||||
|
|
||||||
|
# Tachymeterstandpunkt speichern und beobachtungsgruppeID als ID für das einmalige Aufbauen eines Tachymeters auf einem Punkt festlegen
|
||||||
if zeile[2] == "" and zeile[3] == "" and zeile[4] == "":
|
if zeile[2] == "" and zeile[3] == "" and zeile[4] == "":
|
||||||
nummer_beobachtungsgruppeID += 1
|
nummer_beobachtungsgruppeID += 1
|
||||||
# print("Standpunkt: ",nummer_beobachtungsgruppeID ,zeile[0])
|
|
||||||
standpunkt = zeile[0]
|
standpunkt = zeile[0]
|
||||||
instrumentenhoehe = zeile[1]
|
instrumentenhoehe = zeile[1]
|
||||||
if instrumentenhoehe.startswith("IH:"):
|
if instrumentenhoehe.startswith("IH:"):
|
||||||
instrumentenhoehe = instrumentenhoehe.split("IH:", 1)[1].strip()
|
instrumentenhoehe = instrumentenhoehe.split("IH:", 1)[1].strip()
|
||||||
|
|
||||||
|
# Überprüfung, ob für jeden Beobachteten Zielpunkt 6 Zeilen vorhanden sind (3 Vollszätze mit je 2 Halbsätzen)
|
||||||
if nummer_zielpunkt % 6 != 0:
|
if nummer_zielpunkt % 6 != 0:
|
||||||
liste_fehlerhafte_zeile.append(i)
|
liste_fehlerhafte_zeile.append(i)
|
||||||
|
|
||||||
@@ -509,6 +663,8 @@ class Import:
|
|||||||
liste_zielpunkte_vs2 = []
|
liste_zielpunkte_vs2 = []
|
||||||
liste_zielpunkte_vs3 = []
|
liste_zielpunkte_vs3 = []
|
||||||
else:
|
else:
|
||||||
|
# ZH = Prismenhöhe | VS = Vollsatz | HS = Halbsatz
|
||||||
|
# Erstellen einer Liste mit Standpunkt, Vollsatz und Halbsatzzuordnung für die Beobachtungen für die Weiterverarbeitung
|
||||||
nummer_zielpunkt += 1
|
nummer_zielpunkt += 1
|
||||||
|
|
||||||
if ":ZH:" in zeile[3]:
|
if ":ZH:" in zeile[3]:
|
||||||
@@ -519,17 +675,14 @@ class Import:
|
|||||||
if zeile[0] not in liste_zielpunkte_hs:
|
if zeile[0] not in liste_zielpunkte_hs:
|
||||||
liste_zielpunkte_hs.append(zeile[0])
|
liste_zielpunkte_hs.append(zeile[0])
|
||||||
if zeile[0] in liste_zielpunkte_vs3:
|
if zeile[0] in liste_zielpunkte_vs3:
|
||||||
# print(f"{nummer_zielpunkt} VS3 HS1 {zeile}")
|
|
||||||
liste_beobachtungen_vorbereitung.append(
|
liste_beobachtungen_vorbereitung.append(
|
||||||
[nummer_beobachtungsgruppeID, "VS3", "HS1", standpunkt, zeile[0], zeile[1],
|
[nummer_beobachtungsgruppeID, "VS3", "HS1", standpunkt, zeile[0], zeile[1],
|
||||||
zeile[2], zeile[3], zeile[4], instrumentenhoehe])
|
zeile[2], zeile[3], zeile[4], instrumentenhoehe])
|
||||||
elif zeile[0] in liste_zielpunkte_vs2:
|
elif zeile[0] in liste_zielpunkte_vs2:
|
||||||
# print(f"{nummer_zielpunkt} VS2 HS1 {zeile}")
|
|
||||||
liste_beobachtungen_vorbereitung.append(
|
liste_beobachtungen_vorbereitung.append(
|
||||||
[nummer_beobachtungsgruppeID, "VS2", "HS1", standpunkt, zeile[0], zeile[1],
|
[nummer_beobachtungsgruppeID, "VS2", "HS1", standpunkt, zeile[0], zeile[1],
|
||||||
zeile[2], zeile[3], zeile[4], instrumentenhoehe])
|
zeile[2], zeile[3], zeile[4], instrumentenhoehe])
|
||||||
else:
|
else:
|
||||||
# print(f"{nummer_zielpunkt} VS1 HS1 {zeile}")
|
|
||||||
liste_beobachtungen_vorbereitung.append(
|
liste_beobachtungen_vorbereitung.append(
|
||||||
[nummer_beobachtungsgruppeID, "VS1", "HS1", standpunkt, zeile[0], zeile[1],
|
[nummer_beobachtungsgruppeID, "VS1", "HS1", standpunkt, zeile[0], zeile[1],
|
||||||
zeile[2],
|
zeile[2],
|
||||||
@@ -538,7 +691,6 @@ class Import:
|
|||||||
else:
|
else:
|
||||||
liste_zielpunkte_hs.remove(zeile[0])
|
liste_zielpunkte_hs.remove(zeile[0])
|
||||||
if zeile[0] in liste_zielpunkte_vs3:
|
if zeile[0] in liste_zielpunkte_vs3:
|
||||||
# print(f"{nummer_zielpunkt} VS3 HS2 {zeile}")
|
|
||||||
liste_beobachtungen_vorbereitung.append(
|
liste_beobachtungen_vorbereitung.append(
|
||||||
[nummer_beobachtungsgruppeID, "VS3", "HS2", standpunkt, zeile[0], zeile[1],
|
[nummer_beobachtungsgruppeID, "VS3", "HS2", standpunkt, zeile[0], zeile[1],
|
||||||
zeile[2],
|
zeile[2],
|
||||||
@@ -547,7 +699,6 @@ class Import:
|
|||||||
elif zeile[0] in liste_zielpunkte_vs2:
|
elif zeile[0] in liste_zielpunkte_vs2:
|
||||||
if zeile[0] not in liste_zielpunkte_vs3:
|
if zeile[0] not in liste_zielpunkte_vs3:
|
||||||
liste_zielpunkte_vs3.append(zeile[0])
|
liste_zielpunkte_vs3.append(zeile[0])
|
||||||
# print(f"{nummer_zielpunkt} VS2 HS2 {zeile}")
|
|
||||||
liste_beobachtungen_vorbereitung.append(
|
liste_beobachtungen_vorbereitung.append(
|
||||||
[nummer_beobachtungsgruppeID, "VS2", "HS2", standpunkt, zeile[0], zeile[1],
|
[nummer_beobachtungsgruppeID, "VS2", "HS2", standpunkt, zeile[0], zeile[1],
|
||||||
zeile[2],
|
zeile[2],
|
||||||
@@ -555,18 +706,19 @@ class Import:
|
|||||||
else:
|
else:
|
||||||
if zeile[0] not in liste_zielpunkte_vs2:
|
if zeile[0] not in liste_zielpunkte_vs2:
|
||||||
liste_zielpunkte_vs2.append(zeile[0])
|
liste_zielpunkte_vs2.append(zeile[0])
|
||||||
# print(f"{nummer_zielpunkt} VS1 HS2 {zeile}")
|
|
||||||
liste_beobachtungen_vorbereitung.append(
|
liste_beobachtungen_vorbereitung.append(
|
||||||
[nummer_beobachtungsgruppeID, "VS1", "HS2", standpunkt, zeile[0], zeile[1],
|
[nummer_beobachtungsgruppeID, "VS1", "HS2", standpunkt, zeile[0], zeile[1],
|
||||||
zeile[2],
|
zeile[2],
|
||||||
zeile[3], zeile[4], instrumentenhoehe])
|
zeile[3], zeile[4], instrumentenhoehe])
|
||||||
|
|
||||||
if liste_fehlerhafte_zeile == []:
|
# Ausgabe, welche Zeilen in der Importdatei bearbeitet werden müssen.
|
||||||
# print(f"Einlesen der Datei {pfad_datei} erfolgreich beendet.")
|
if liste_fehlerhafte_zeile != []:
|
||||||
pass
|
fehler_zeilen = ", ".join(map(str, liste_fehlerhafte_zeile))
|
||||||
else:
|
|
||||||
print(
|
print(
|
||||||
f"Das Einlesen der Datei {pfad_datei} wurde abgebrochen.\nBitte bearbeiten Sie die Zeilen rund um: {", ".join(map(str, liste_fehlerhafte_zeile))} in der csv-Datei und wiederholen Sie den Import.")
|
f"Das Einlesen der Datei {pfad_datei} wurde abgebrochen.\n"
|
||||||
|
f"Bitte bearbeiten Sie die Zeilen rund um: {fehler_zeilen} in der csv-Datei "
|
||||||
|
f"und wiederholen Sie den Import."
|
||||||
|
)
|
||||||
Import_fortsetzen = False
|
Import_fortsetzen = False
|
||||||
|
|
||||||
else:
|
else:
|
||||||
@@ -579,13 +731,12 @@ class Import:
|
|||||||
while len(liste_beobachtungen_vorbereitung) > 0:
|
while len(liste_beobachtungen_vorbereitung) > 0:
|
||||||
liste_aktueller_zielpunkt = liste_beobachtungen_vorbereitung[0]
|
liste_aktueller_zielpunkt = liste_beobachtungen_vorbereitung[0]
|
||||||
aktueller_zielpunkt = liste_aktueller_zielpunkt[4]
|
aktueller_zielpunkt = liste_aktueller_zielpunkt[4]
|
||||||
# print(liste_beobachtungen_vorbereitung[0])
|
|
||||||
|
|
||||||
for index in range(1, len(liste_beobachtungen_vorbereitung)):
|
for index in range(1, len(liste_beobachtungen_vorbereitung)):
|
||||||
liste = liste_beobachtungen_vorbereitung[index]
|
liste = liste_beobachtungen_vorbereitung[index]
|
||||||
|
|
||||||
|
# Berechnen der zu importierenden Beobachtungen. (Jeweils reduziert auf den Vollsatz)
|
||||||
if liste[4] == aktueller_zielpunkt:
|
if liste[4] == aktueller_zielpunkt:
|
||||||
# print(liste)
|
|
||||||
richtung1 = self.string_to_decimal(liste_aktueller_zielpunkt[5])
|
richtung1 = self.string_to_decimal(liste_aktueller_zielpunkt[5])
|
||||||
richtung2 = self.string_to_decimal(liste[5]) - Decimal(200)
|
richtung2 = self.string_to_decimal(liste[5]) - Decimal(200)
|
||||||
zenitwinkel_vollsatz_gon = (self.string_to_decimal(liste_aktueller_zielpunkt[6]) - self.string_to_decimal(
|
zenitwinkel_vollsatz_gon = (self.string_to_decimal(liste_aktueller_zielpunkt[6]) - self.string_to_decimal(
|
||||||
@@ -612,12 +763,8 @@ class Import:
|
|||||||
Import_fortsetzen = False
|
Import_fortsetzen = False
|
||||||
print(f"Der Import wurde abgebrochen, weil für zwei Halbsätze vom Standpunkt {liste_aktueller_zielpunkt[3]} zum Zielpunkt {aktueller_zielpunkt} unterschiedliche Instrumentenhöhen vorliegen. Bitte in der Datei {pfad_datei} korrigieren und Import neustarten.")
|
print(f"Der Import wurde abgebrochen, weil für zwei Halbsätze vom Standpunkt {liste_aktueller_zielpunkt[3]} zum Zielpunkt {aktueller_zielpunkt} unterschiedliche Instrumentenhöhen vorliegen. Bitte in der Datei {pfad_datei} korrigieren und Import neustarten.")
|
||||||
|
|
||||||
|
# Umrechnen der Zenitwinkel und Schrägdistanzen auf den Boden unter Verwendung der Instrumenten- und Prismenhöhen
|
||||||
|
schraegdistanz_bodenbezogen, zenitwinkel_bodenbezogen = self.berechnungen.berechne_zenitwinkel_distanz_bodenbezogen(
|
||||||
# print(richtung_vollsatz)
|
|
||||||
# print(zenitwinkel_vollsatz)
|
|
||||||
# print(distanz_vollsatz)
|
|
||||||
schraegdistanz_bodenbezogen, zenitwinkel_bodenbezogen = berechnungen.berechne_zenitwinkel_distanz_bodenbezogen(
|
|
||||||
float(zenitwinkel_vollsatz_rad), float(distanz_vollsatz), float(instrumentenhoehe_import), float(prismenhoehe))
|
float(zenitwinkel_vollsatz_rad), float(distanz_vollsatz), float(instrumentenhoehe_import), float(prismenhoehe))
|
||||||
|
|
||||||
liste_beobachtungen_import.append(
|
liste_beobachtungen_import.append(
|
||||||
@@ -627,12 +774,13 @@ class Import:
|
|||||||
del liste_beobachtungen_vorbereitung[0]
|
del liste_beobachtungen_vorbereitung[0]
|
||||||
break
|
break
|
||||||
|
|
||||||
|
# Überprüfung, ob das Instrument bereits vom Benutzer angelegt wurde.
|
||||||
if instrumentenID not in liste_instrumentenid:
|
if instrumentenID not in liste_instrumentenid:
|
||||||
Import_fortsetzen = False
|
Import_fortsetzen = False
|
||||||
print(
|
print(
|
||||||
"Der Import wurde abgebrochen. Bitte eine gültige InstrumentenID eingeben. Bei Bedarf ist das Instrument neu anzulegen.")
|
"Der Import wurde abgebrochen. Bitte eine gültige InstrumentenID eingeben. Bei Bedarf ist das Instrument neu anzulegen.")
|
||||||
|
|
||||||
|
# Berechnete bodenbezogene Beobachtungen, welche jeweils auf den Vollsatz reduziert sind, in die Tabelle Beobachtungen importieren.
|
||||||
if Import_fortsetzen:
|
if Import_fortsetzen:
|
||||||
con = sqlite3.connect(self.pfad_datenbank)
|
con = sqlite3.connect(self.pfad_datenbank)
|
||||||
cursor = con.cursor()
|
cursor = con.cursor()
|
||||||
@@ -649,28 +797,47 @@ class Import:
|
|||||||
|
|
||||||
def vorbereitung_import_beobachtungen_nivellement_naeherung_punkthoehen(self, pfad_datei: str,
|
def vorbereitung_import_beobachtungen_nivellement_naeherung_punkthoehen(self, pfad_datei: str,
|
||||||
instrumentenID: int) -> None | tuple[None, None] | tuple[dict[Any, Any], list[Any]]:
|
instrumentenID: int) -> None | tuple[None, None] | tuple[dict[Any, Any], list[Any]]:
|
||||||
# Prüfen, ob Bereits Daten aus der Datei in der Datenbank vorhanden sind
|
"""Bereitet den Import von Nivellementdaten zur Ableitung von Näherungs-Normalhöhen vor.
|
||||||
|
|
||||||
|
Aus einer semikolon-separierten Datei werden Zeilen erkannt, die berechnete Zielweiten/Punkthöhen enthalten,
|
||||||
|
und daraus pro Punktnummer alle gefundenen Z-Werte gesammelt. Anschließend wird je Punktnummer
|
||||||
|
der Mittelwert berechnet (Rundung auf 6 Nachkommastellen).
|
||||||
|
Danach wird geprüft, welche dieser Punktnummern bereits in der Tabelle Netzpunkte existieren.
|
||||||
|
|
||||||
|
Abbruchbedingungen:
|
||||||
|
|
||||||
|
- Dateiname bereits in Beobachtungen vorhanden,
|
||||||
|
- instrumentenID nicht in Instrumente vorhanden.
|
||||||
|
|
||||||
|
:param pfad_datei: Pfad zur Importdatei.
|
||||||
|
:type pfad_datei: str
|
||||||
|
:param instrumentenID: ID des verwendeten Niv-Instruments aus der Tabelle Instrumente.
|
||||||
|
:type instrumentenID: int
|
||||||
|
:return: (dict_punkt_mittelwert_punkthoehen, liste_punktnummern_in_db) oder (None, None) bei Abbruch.
|
||||||
|
:rtype: None | tuple[None, None] | tuple[dict[Any, Any], list[Any]]
|
||||||
|
"""
|
||||||
|
# Prüfen, ob bereits Daten aus der Datei in der Datenbank vorhanden sind
|
||||||
con = sqlite3.connect(self.pfad_datenbank)
|
con = sqlite3.connect(self.pfad_datenbank)
|
||||||
cursor = con.cursor()
|
cursor = con.cursor()
|
||||||
liste_dateinamen_in_db = [r[0] for r in cursor.execute(
|
liste_dateinamen_in_db = [r[0] for r in cursor.execute(
|
||||||
"SELECT DISTINCT dateiname FROM Beobachtungen"
|
"SELECT DISTINCT dateiname FROM Beobachtungen"
|
||||||
).fetchall()]
|
).fetchall()]
|
||||||
liste_beobachtungsgruppeID = [r[0] for r in cursor.execute("""SELECT DISTINCT beobachtungsgruppeID
|
|
||||||
FROM Beobachtungen""").fetchall()]
|
|
||||||
liste_instrumentenid = [r[0] for r in cursor.execute("SELECT instrumenteID FROM Instrumente").fetchall()]
|
liste_instrumentenid = [r[0] for r in cursor.execute("SELECT instrumenteID FROM Instrumente").fetchall()]
|
||||||
liste_netzpunkte = [r[0] for r in cursor.execute("SELECT punktnummer FROM Netzpunkte").fetchall()]
|
liste_netzpunkte = [r[0] for r in cursor.execute("SELECT punktnummer FROM Netzpunkte").fetchall()]
|
||||||
|
|
||||||
cursor.close()
|
cursor.close()
|
||||||
con.close()
|
con.close()
|
||||||
|
|
||||||
|
# Import_fortsetzen wird False, sobald eine Fehler festgestellt wird. Als Folge wird der Import abgebrochen und eine Fehlermeldung mit Handlungshinweisen für den Benutzer ausgegeben.
|
||||||
Import_fortsetzen = True
|
Import_fortsetzen = True
|
||||||
|
|
||||||
|
# Abbruch des Imports, wenn bereits Daten aus der Datei importiert wurden.
|
||||||
if pfad_datei in liste_dateinamen_in_db:
|
if pfad_datei in liste_dateinamen_in_db:
|
||||||
Import_fortsetzen = False
|
Import_fortsetzen = False
|
||||||
print(f"Der Import wurde abgebrochen, weil die Beobachtungen aus der Datei {pfad_datei} bereits in der Datenbank vorhanden sind.")
|
print(f"Der Import wurde abgebrochen, weil die Beobachtungen aus der Datei {pfad_datei} bereits in der Datenbank vorhanden sind.")
|
||||||
return None, None
|
return None, None
|
||||||
|
|
||||||
|
# Abbruch, wenn das Instrument noch nicht vom Benutzer angelegt wurde.
|
||||||
if instrumentenID not in liste_instrumentenid:
|
if instrumentenID not in liste_instrumentenid:
|
||||||
Import_fortsetzen = False
|
Import_fortsetzen = False
|
||||||
print(
|
print(
|
||||||
@@ -678,7 +845,7 @@ class Import:
|
|||||||
return None, None
|
return None, None
|
||||||
|
|
||||||
if Import_fortsetzen:
|
if Import_fortsetzen:
|
||||||
# Berechnete Punkthöhe Importieren
|
# Messwerte aus der Importdatei abfragen
|
||||||
muster_berechnete_zielweiten = "| | |Z "
|
muster_berechnete_zielweiten = "| | |Z "
|
||||||
dict_punkt_alle_punkthoehen = {}
|
dict_punkt_alle_punkthoehen = {}
|
||||||
dict_punkt_mittelwert_punkthoehen = {}
|
dict_punkt_mittelwert_punkthoehen = {}
|
||||||
@@ -702,7 +869,6 @@ class Import:
|
|||||||
wert_z = self.string_to_float(teil.split()[0])
|
wert_z = self.string_to_float(teil.split()[0])
|
||||||
|
|
||||||
if punktnummer is not None and wert_z is not None:
|
if punktnummer is not None and wert_z is not None:
|
||||||
#print(f"{punktnummer}, {float(wert_z)}")
|
|
||||||
if punktnummer not in dict_punkt_alle_punkthoehen:
|
if punktnummer not in dict_punkt_alle_punkthoehen:
|
||||||
dict_punkt_alle_punkthoehen[punktnummer] = []
|
dict_punkt_alle_punkthoehen[punktnummer] = []
|
||||||
|
|
||||||
@@ -711,6 +877,7 @@ class Import:
|
|||||||
# Hier wird auf 6 Nachkommastellen gerundet!
|
# Hier wird auf 6 Nachkommastellen gerundet!
|
||||||
dict_punkt_mittelwert_punkthoehen[punktnummer] = round(sum(liste_z) / len(liste_z),6)
|
dict_punkt_mittelwert_punkthoehen[punktnummer] = round(sum(liste_z) / len(liste_z),6)
|
||||||
|
|
||||||
|
# Erstellen einer Liste mit allen Punktnummern, die in der Datenbank vorliegen und für die Nivellementbeobachtungen vorliegen.
|
||||||
if Import_fortsetzen:
|
if Import_fortsetzen:
|
||||||
# Ausgabe, welche Niv-Punkte bereits in der Tabelle Netzpunkte enthalten sind
|
# Ausgabe, welche Niv-Punkte bereits in der Tabelle Netzpunkte enthalten sind
|
||||||
liste_punktnummern_nivellement = dict_punkt_mittelwert_punkthoehen.keys()
|
liste_punktnummern_nivellement = dict_punkt_mittelwert_punkthoehen.keys()
|
||||||
@@ -722,6 +889,7 @@ class Import:
|
|||||||
else:
|
else:
|
||||||
liste_punktnummern_nicht_in_db.append(punktnummer)
|
liste_punktnummern_nicht_in_db.append(punktnummer)
|
||||||
|
|
||||||
|
# Es werden nur Höhendifferenzen für Punkte berechnet, für die Näherungskoordinaten in der Datenbank vorliegen.
|
||||||
if Import_fortsetzen:
|
if Import_fortsetzen:
|
||||||
print(f"Für folgende Nivellementpunkte werden die Höhen in der Ausgleichung berechnet: {liste_punktnummern_in_db}\nFür folgende Punkte wird aktuell keine Höhe in der Ausgleichung berechnet: {liste_punktnummern_nicht_in_db}. Bei Bedarf im folgenden Schritt ändern!")
|
print(f"Für folgende Nivellementpunkte werden die Höhen in der Ausgleichung berechnet: {liste_punktnummern_in_db}\nFür folgende Punkte wird aktuell keine Höhe in der Ausgleichung berechnet: {liste_punktnummern_nicht_in_db}. Bei Bedarf im folgenden Schritt ändern!")
|
||||||
return dict_punkt_mittelwert_punkthoehen, liste_punktnummern_in_db
|
return dict_punkt_mittelwert_punkthoehen, liste_punktnummern_in_db
|
||||||
@@ -729,6 +897,21 @@ class Import:
|
|||||||
def import_beobachtungen_nivellement_naeherung_punkthoehen(self, dict_punkt_mittelwert_punkthoehen: dict,
|
def import_beobachtungen_nivellement_naeherung_punkthoehen(self, dict_punkt_mittelwert_punkthoehen: dict,
|
||||||
liste_punktnummern_in_db: list,
|
liste_punktnummern_in_db: list,
|
||||||
liste_punktnummern_hinzufuegen: list) -> str | None:
|
liste_punktnummern_hinzufuegen: list) -> str | None:
|
||||||
|
"""Importiert Näherungsnormalhöhen in die Tabelle Netzpunkte.
|
||||||
|
|
||||||
|
Es werden Listen geführt für neu hinzugefügte, bereits vorhandene und geänderte Punkte und
|
||||||
|
abschließend als Konsolen-Ausgabe ausgegeben.
|
||||||
|
|
||||||
|
:param dict_punkt_mittelwert_punkthoehen: Dictionary {punktnummer: normalhoehe} als Mittelwerte.
|
||||||
|
:type dict_punkt_mittelwert_punkthoehen: dict
|
||||||
|
:param liste_punktnummern_in_db: Punktnummern, die bereits in Netzpunkte existieren.
|
||||||
|
:type liste_punktnummern_in_db: list
|
||||||
|
:param liste_punktnummern_hinzufuegen: Zusätzliche Punktnummern, die ebenfalls übernommen werden sollen.
|
||||||
|
:type liste_punktnummern_hinzufuegen: list
|
||||||
|
:return: Status-String mit der Liste der Punkte, für die Normalhöhen in der Ausgleichung verfügbar sind, oder None bei Abbruch.
|
||||||
|
:rtype: str | None
|
||||||
|
"""
|
||||||
|
# Import_fortsetzen wird False, sobald eine Fehler festgestellt wird. Als Folge wird der Import abgebrochen und eine Fehlermeldung mit Handlungshinweisen für den Benutzer ausgegeben.
|
||||||
Import_fortsetzen = True
|
Import_fortsetzen = True
|
||||||
|
|
||||||
if dict_punkt_mittelwert_punkthoehen == None or liste_punktnummern_in_db == None or liste_punktnummern_hinzufuegen == None:
|
if dict_punkt_mittelwert_punkthoehen == None or liste_punktnummern_in_db == None or liste_punktnummern_hinzufuegen == None:
|
||||||
@@ -744,6 +927,7 @@ class Import:
|
|||||||
liste_punkte_geaendert = []
|
liste_punkte_geaendert = []
|
||||||
|
|
||||||
for punktnummer in liste_punktnummern_hinzufuegen:
|
for punktnummer in liste_punktnummern_hinzufuegen:
|
||||||
|
# Neu anlegen oder aktualisieren der Normalhöhen in der Tabelle Netzpunkte
|
||||||
try:
|
try:
|
||||||
cursor.execute(f"INSERT INTO Netzpunkte (punktnummer, normalhoehe_hfp) VALUES (?, ?)", (punktnummer, dict_punkt_mittelwert_punkthoehen[punktnummer]))
|
cursor.execute(f"INSERT INTO Netzpunkte (punktnummer, normalhoehe_hfp) VALUES (?, ?)", (punktnummer, dict_punkt_mittelwert_punkthoehen[punktnummer]))
|
||||||
liste_punkte_neu_hinzugefuegt.append(punktnummer)
|
liste_punkte_neu_hinzugefuegt.append(punktnummer)
|
||||||
@@ -765,10 +949,31 @@ class Import:
|
|||||||
print(f"Bereits vorhanden ({len(liste_punkte_bereits_vorhanden)}): {liste_punkte_bereits_vorhanden}")
|
print(f"Bereits vorhanden ({len(liste_punkte_bereits_vorhanden)}): {liste_punkte_bereits_vorhanden}")
|
||||||
print(f"Geändert ({len(liste_punkte_geaendert)}): {liste_punkte_geaendert}\n")
|
print(f"Geändert ({len(liste_punkte_geaendert)}): {liste_punkte_geaendert}\n")
|
||||||
|
|
||||||
return f"Für folgende Punkte werden die Höhen Ausgeglichen: {liste_punktnummern_hinzufuegen + liste_punktnummern_in_db}"
|
return f"Für folgende Punkte werden die Höhen Ausgeglichen: {liste_punktnummern_in_db}"
|
||||||
|
|
||||||
|
|
||||||
def import_beobachtungen_nivellement_RVVR(self, pfad_datei: str, instrumentenID: int) -> str | None:
|
def import_beobachtungen_nivellement_RVVR(self, pfad_datei: str, instrumentenID: int) -> str | None:
|
||||||
|
"""Importiert geometrische Nivellementbeobachtungen nach dem RVVR Prinzip die Tabelle Beobachtungen.
|
||||||
|
|
||||||
|
Es werden Zeilen mit Rück-/Vormessungen erkannt (rvvr: Rück, Vor, Vor, Rück). Pro Block werden
|
||||||
|
dh und Entfernung berechnet (Rundung jeweils auf 8 Nachkommastellen). Anschließend werden Züge reduziert, indem
|
||||||
|
Wechselpunkte, die nicht in Netzpunkte vorhanden sind, durch Summation herausgekürzt werden.
|
||||||
|
Damit werden nur Beobachtungen zwischen Punkten in Netzpunkte (bzw. reduzierten Start-/Zielpunkten)
|
||||||
|
in Beobachtungen gespeichert.
|
||||||
|
|
||||||
|
Abbruchbedingungen:
|
||||||
|
|
||||||
|
- Dateiname bereits in Beobachtungen vorhanden,
|
||||||
|
- instrumentenID nicht in Instrumente vorhanden,
|
||||||
|
- Anzahl RVVR-Zeilen nicht durch 4 teilbar.
|
||||||
|
|
||||||
|
:param pfad_datei: Pfad zur Textdatei.
|
||||||
|
:type pfad_datei: str
|
||||||
|
:param instrumentenID: ID des verwendeten Niv-Instruments aus der Tabelle Instrumente.
|
||||||
|
:type instrumentenID: int
|
||||||
|
:return: Status-String bei Erfolg oder None bei Abbruch.
|
||||||
|
:rtype: str | None
|
||||||
|
"""
|
||||||
# Prüfen, ob Bereits Daten aus der Datei in der Datenbank vorhanden sind
|
# Prüfen, ob Bereits Daten aus der Datei in der Datenbank vorhanden sind
|
||||||
con = sqlite3.connect(self.pfad_datenbank)
|
con = sqlite3.connect(self.pfad_datenbank)
|
||||||
cursor = con.cursor()
|
cursor = con.cursor()
|
||||||
@@ -781,36 +986,36 @@ class Import:
|
|||||||
cursor.close()
|
cursor.close()
|
||||||
con.close()
|
con.close()
|
||||||
|
|
||||||
|
# Import_fortsetzen wird False, sobald eine Fehler festgestellt wird. Als Folge wird der Import abgebrochen und eine Fehlermeldung mit Handlungshinweisen für den Benutzer ausgegeben.
|
||||||
Import_fortsetzen = True
|
Import_fortsetzen = True
|
||||||
|
|
||||||
|
# Import abbrechen, wenn bereits Daten aus der selben Datei importiert wurden
|
||||||
if pfad_datei in liste_dateinamen_in_db:
|
if pfad_datei in liste_dateinamen_in_db:
|
||||||
Import_fortsetzen = False
|
Import_fortsetzen = False
|
||||||
print(f"Der Import wurde abgebrochen, weil die Beobachtungen aus der Datei {pfad_datei} bereits in der Datenbank vorhanden sind.")
|
print(f"Der Import wurde abgebrochen, weil die Beobachtungen aus der Datei {pfad_datei} bereits in der Datenbank vorhanden sind.")
|
||||||
|
|
||||||
|
# Import abbrechen, wenn das Instrument noch nicht vom Benutzer angelegt wurde.
|
||||||
if instrumentenID not in liste_instrumentenid:
|
if instrumentenID not in liste_instrumentenid:
|
||||||
Import_fortsetzen = False
|
Import_fortsetzen = False
|
||||||
print(
|
print(
|
||||||
"Der Import wurde abgebrochen. Bitte eine gültige InstrumentenID eingeben. Bei Bedarf ist das Instrument neu anzulegen.")
|
"Der Import wurde abgebrochen. Bitte eine gültige InstrumentenID eingeben. Bei Bedarf ist das Instrument neu anzulegen.")
|
||||||
|
|
||||||
if Import_fortsetzen:
|
if Import_fortsetzen:
|
||||||
|
# rvvr = Rück, Vor, Vor, Rück
|
||||||
|
# Berechnen der Höhenunterschiede delta H und Schrägstrecken zwischen Zwei Punkten gemäß der Importdatei aus rvvr.
|
||||||
anzahl_zeilen_rvvr = 0
|
anzahl_zeilen_rvvr = 0
|
||||||
liste_zeilen_rvvr = []
|
liste_zeilen_rvvr = []
|
||||||
liste_punktpaare = []
|
liste_punktpaare = []
|
||||||
with open(pfad_datei, "r", encoding="utf-8") as f:
|
with open(pfad_datei, "r", encoding="utf-8") as f:
|
||||||
for i, zeile in enumerate(f):
|
for i, zeile in enumerate(f):
|
||||||
if ("Lr" in zeile) or ("Lv" in zeile):
|
if ("Lr" in zeile) or ("Lv" in zeile):
|
||||||
#print(zeile.rstrip())
|
|
||||||
|
|
||||||
liste_zeilen_rvvr.append(zeile)
|
liste_zeilen_rvvr.append(zeile)
|
||||||
anzahl_zeilen_rvvr += 1
|
anzahl_zeilen_rvvr += 1
|
||||||
|
|
||||||
|
|
||||||
if anzahl_zeilen_rvvr % 4 == 0:
|
if anzahl_zeilen_rvvr % 4 == 0:
|
||||||
index = 0
|
index = 0
|
||||||
while index < len(liste_zeilen_rvvr):
|
while index < len(liste_zeilen_rvvr):
|
||||||
block_4 = liste_zeilen_rvvr[index:index + 4]
|
block_4 = liste_zeilen_rvvr[index:index + 4]
|
||||||
|
|
||||||
liste_punktnummern_block = []
|
liste_punktnummern_block = []
|
||||||
|
|
||||||
for zeile_block in block_4:
|
for zeile_block in block_4:
|
||||||
@@ -885,7 +1090,9 @@ class Import:
|
|||||||
|
|
||||||
index += 4
|
index += 4
|
||||||
|
|
||||||
|
# Berechnen der Höhendifferenzen und schrägstrecken zwischen Punkten in der Tabelle Netzpunkte.
|
||||||
|
# Somit werden alle Wechselpunkte nicht in der Ausgleichung berücksichtigt.
|
||||||
|
# Die Berechnung erfolgt durch addition.
|
||||||
liste_beobachtungen_reduziert = []
|
liste_beobachtungen_reduziert = []
|
||||||
liste_beobachtungen_bearbeitung = []
|
liste_beobachtungen_bearbeitung = []
|
||||||
zugnummer_vorher = liste_punktpaare[0][0]
|
zugnummer_vorher = liste_punktpaare[0][0]
|
||||||
@@ -893,15 +1100,12 @@ class Import:
|
|||||||
zugnummer = einzelbeobachtung[0]
|
zugnummer = einzelbeobachtung[0]
|
||||||
if zugnummer == zugnummer_vorher:
|
if zugnummer == zugnummer_vorher:
|
||||||
if einzelbeobachtung[1] in liste_netzpunkte and einzelbeobachtung[2] in liste_netzpunkte:
|
if einzelbeobachtung[1] in liste_netzpunkte and einzelbeobachtung[2] in liste_netzpunkte:
|
||||||
#print(einzelbeobachtung)
|
|
||||||
liste_beobachtungen_reduziert.append(einzelbeobachtung + (1,))
|
liste_beobachtungen_reduziert.append(einzelbeobachtung + (1,))
|
||||||
|
|
||||||
elif einzelbeobachtung[1] in liste_netzpunkte and einzelbeobachtung[2] not in liste_netzpunkte:
|
elif einzelbeobachtung[1] in liste_netzpunkte and einzelbeobachtung[2] not in liste_netzpunkte:
|
||||||
#print(f"Zielpunkt nicht enthalten {einzelbeobachtung}")
|
|
||||||
liste_beobachtungen_bearbeitung.append(einzelbeobachtung)
|
liste_beobachtungen_bearbeitung.append(einzelbeobachtung)
|
||||||
|
|
||||||
elif einzelbeobachtung[1] not in liste_netzpunkte and einzelbeobachtung[2] in liste_netzpunkte:
|
elif einzelbeobachtung[1] not in liste_netzpunkte and einzelbeobachtung[2] in liste_netzpunkte:
|
||||||
#print(f"Startpunkt nicht enthalten {einzelbeobachtung}")
|
|
||||||
liste_beobachtungen_bearbeitung.append(einzelbeobachtung)
|
liste_beobachtungen_bearbeitung.append(einzelbeobachtung)
|
||||||
startpunkt = None
|
startpunkt = None
|
||||||
zielpunkt = None
|
zielpunkt = None
|
||||||
@@ -919,24 +1123,20 @@ class Import:
|
|||||||
summe_dh += beobachtung_bearbeiten[3]
|
summe_dh += beobachtung_bearbeiten[3]
|
||||||
summe_entfernung += beobachtung_bearbeiten[4]
|
summe_entfernung += beobachtung_bearbeiten[4]
|
||||||
anzahl_standpunkte += 1
|
anzahl_standpunkte += 1
|
||||||
|
|
||||||
# Achtung:Hier Rundung auf 8 Nachkommastellen!
|
# Achtung:Hier Rundung auf 8 Nachkommastellen!
|
||||||
liste_beobachtungen_reduziert.append(
|
liste_beobachtungen_reduziert.append(
|
||||||
(zugnummer, startpunkt, zielpunkt, round(summe_dh, 8),
|
(zugnummer, startpunkt, zielpunkt, round(summe_dh, 8),
|
||||||
round(summe_entfernung, 8), anzahl_standpunkte))
|
round(summe_entfernung, 8), anzahl_standpunkte))
|
||||||
liste_beobachtungen_bearbeitung = []
|
liste_beobachtungen_bearbeitung = []
|
||||||
else:
|
else:
|
||||||
#print(f"Startpunkt und Zielpunkt nicht enthalten {einzelbeobachtung}")
|
|
||||||
liste_beobachtungen_bearbeitung.append(einzelbeobachtung)
|
liste_beobachtungen_bearbeitung.append(einzelbeobachtung)
|
||||||
else:
|
else:
|
||||||
#print(f"-----------------------------")
|
|
||||||
if einzelbeobachtung[1] in liste_netzpunkte and einzelbeobachtung[2] in liste_netzpunkte:
|
if einzelbeobachtung[1] in liste_netzpunkte and einzelbeobachtung[2] in liste_netzpunkte:
|
||||||
#print(einzelbeobachtung)
|
|
||||||
liste_beobachtungen_reduziert.append(einzelbeobachtung + (1,))
|
liste_beobachtungen_reduziert.append(einzelbeobachtung + (1,))
|
||||||
elif einzelbeobachtung[1] in liste_netzpunkte and einzelbeobachtung[2] not in liste_netzpunkte:
|
elif einzelbeobachtung[1] in liste_netzpunkte and einzelbeobachtung[2] not in liste_netzpunkte:
|
||||||
#print(f"Zielpunkt nicht enthalten {einzelbeobachtung}")
|
|
||||||
liste_beobachtungen_bearbeitung.append(einzelbeobachtung)
|
liste_beobachtungen_bearbeitung.append(einzelbeobachtung)
|
||||||
elif einzelbeobachtung[1] not in liste_netzpunkte and einzelbeobachtung[2] in liste_netzpunkte:
|
elif einzelbeobachtung[1] not in liste_netzpunkte and einzelbeobachtung[2] in liste_netzpunkte:
|
||||||
#print(f"Startpunkt nicht enthalten {einzelbeobachtung}")
|
|
||||||
liste_beobachtungen_bearbeitung.append(einzelbeobachtung)
|
liste_beobachtungen_bearbeitung.append(einzelbeobachtung)
|
||||||
startpunkt = None
|
startpunkt = None
|
||||||
zielpunkt = None
|
zielpunkt = None
|
||||||
@@ -954,12 +1154,12 @@ class Import:
|
|||||||
summe_dh += beobachtung_bearbeiten[3]
|
summe_dh += beobachtung_bearbeiten[3]
|
||||||
summe_entfernung += beobachtung_bearbeiten[4]
|
summe_entfernung += beobachtung_bearbeiten[4]
|
||||||
anzahl_standpunkte += 1
|
anzahl_standpunkte += 1
|
||||||
|
|
||||||
#Achtung:Hier Rundung auf 8 Nachkommastellen!
|
#Achtung:Hier Rundung auf 8 Nachkommastellen!
|
||||||
liste_beobachtungen_reduziert.append(
|
liste_beobachtungen_reduziert.append(
|
||||||
(zugnummer, startpunkt, zielpunkt, round(summe_dh,8), round(summe_entfernung,8), anzahl_standpunkte))
|
(zugnummer, startpunkt, zielpunkt, round(summe_dh,8), round(summe_entfernung,8), anzahl_standpunkte))
|
||||||
liste_beobachtungen_bearbeitung = []
|
liste_beobachtungen_bearbeitung = []
|
||||||
else:
|
else:
|
||||||
# print(f"Startpunkt und Zielpunkt nicht enthalten {einzelbeobachtung}")
|
|
||||||
liste_beobachtungen_bearbeitung.append(einzelbeobachtung)
|
liste_beobachtungen_bearbeitung.append(einzelbeobachtung)
|
||||||
zugnummer_vorher = zugnummer
|
zugnummer_vorher = zugnummer
|
||||||
|
|
||||||
@@ -970,22 +1170,28 @@ class Import:
|
|||||||
con.commit()
|
con.commit()
|
||||||
cursor.close()
|
cursor.close()
|
||||||
con.close()
|
con.close()
|
||||||
|
|
||||||
return f"Die Beobachtungen aus der Datei {pfad_datei} wurden erfolgreich importiert."
|
return f"Die Beobachtungen aus der Datei {pfad_datei} wurden erfolgreich importiert."
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
print(f"Anzahl nicht RVVR durch 4 teilbar. Bitte die Datei {pfad_datei} überprüfen! Der Import wurde abgebrochen.")
|
print(f"Anzahl nicht RVVR durch 4 teilbar. Bitte die Datei {pfad_datei} überprüfen! Der Import wurde abgebrochen.")
|
||||||
Import_fortsetzen = False
|
Import_fortsetzen = False
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def import_koordinaten_gnss(self, pfad_datei: str, liste_sapos_stationen_genauigkeiten: list) -> str:
|
def import_koordinaten_gnss(self, pfad_datei: str, liste_sapos_stationen_genauigkeiten: list) -> str:
|
||||||
liste_zeilen = []
|
"""Importiert GNSS-Koordinaten (ECEF) in die Tabelle Netzpunkte.
|
||||||
dict_koordinaten = {}
|
|
||||||
|
|
||||||
|
Die Datei wird semikolon-separiert gelesen und zusätzlich aufgesplittet (Leerzeichen und Kommas).
|
||||||
|
Für Referenzstationen (Kennzeichnung "Referenz" und Standardabweichungen 0.0000/0.0000/0.0000)
|
||||||
|
werden die übergebenen Genauigkeiten (X/Y/Z) eingesetzt.
|
||||||
|
|
||||||
|
Die Koordinaten und Standardabweichungen werden in Netzpunkte geschrieben.
|
||||||
|
|
||||||
|
:param pfad_datei: Pfad zur Koordinatendatei.
|
||||||
|
:type pfad_datei: str
|
||||||
|
:param liste_sapos_stationen_genauigkeiten: Liste mit drei Werten [σX, σY, σZ] für Referenzstationen.
|
||||||
|
:type liste_sapos_stationen_genauigkeiten: list
|
||||||
|
:return: Statusmeldung zum erfolgreichen Import.
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
con = sqlite3.connect(self.pfad_datenbank)
|
con = sqlite3.connect(self.pfad_datenbank)
|
||||||
cursor = con.cursor()
|
cursor = con.cursor()
|
||||||
|
|
||||||
@@ -995,7 +1201,6 @@ class Import:
|
|||||||
row_neu = []
|
row_neu = []
|
||||||
for eintrag in row:
|
for eintrag in row:
|
||||||
eintrag = str(eintrag).strip()
|
eintrag = str(eintrag).strip()
|
||||||
|
|
||||||
eintrag = eintrag.replace("'", "")
|
eintrag = eintrag.replace("'", "")
|
||||||
aufgeteilt = eintrag.split()
|
aufgeteilt = eintrag.split()
|
||||||
for teil in aufgeteilt:
|
for teil in aufgeteilt:
|
||||||
@@ -1012,14 +1217,33 @@ class Import:
|
|||||||
stabw_vorinfo_y = excluded.stabw_vorinfo_y,
|
stabw_vorinfo_y = excluded.stabw_vorinfo_y,
|
||||||
stabw_vorinfo_z = excluded.stabw_vorinfo_z""", (row_neu[0], row_neu[4], row_neu[5], row_neu[6], row_neu[7], row_neu[8], row_neu[9])
|
stabw_vorinfo_z = excluded.stabw_vorinfo_z""", (row_neu[0], row_neu[4], row_neu[5], row_neu[6], row_neu[7], row_neu[8], row_neu[9])
|
||||||
)
|
)
|
||||||
#liste_zeilen.append(row_neu)
|
|
||||||
|
|
||||||
|
|
||||||
con.commit()
|
con.commit()
|
||||||
con.close()
|
con.close()
|
||||||
return "Import der Koordinaten aus stationärem GNSS abgeschlossen."
|
return "Import der Koordinaten aus stationärem GNSS abgeschlossen."
|
||||||
|
|
||||||
def import_basislinien_gnss(self, pfad_datei: str, instrumentenID: int) -> None:
|
def import_basislinien_gnss(self, pfad_datei: str, instrumentenID: int) -> None:
|
||||||
|
"""Importiert GNSS-Basislinien inkl. Kovarianzen in die Tabelle Beobachtungen.
|
||||||
|
|
||||||
|
Die Datei wird zeilenweise gelesen und Basislinien werden über Präfixe erkannt:
|
||||||
|
|
||||||
|
- @+ : Ziel-/Punktkennung (ID),
|
||||||
|
- @- : Gegenpunktkennung und Komponenten (bx/by/bz),
|
||||||
|
- @= : s0 und Kovarianzterme (cxx, cxy, cxz, cyy, cyz, czz).
|
||||||
|
|
||||||
|
Vorabprüfungen:
|
||||||
|
|
||||||
|
- Abbruch, wenn der Dateiname bereits in Beobachtungen vorkommt,
|
||||||
|
- Abbruch, wenn instrumentenID nicht in Instrumente existiert.
|
||||||
|
|
||||||
|
:param pfad_datei: Pfad zur Basisliniendatei (Text).
|
||||||
|
:type pfad_datei: str
|
||||||
|
:param instrumentenID: ID des verwendeten GNSS-Instruments (FK auf Instrumente).
|
||||||
|
:type instrumentenID: int
|
||||||
|
:return: None
|
||||||
|
:rtype: None
|
||||||
|
"""
|
||||||
|
# Import_fortsetzen wird False, sobald eine Fehler festgestellt wird. Als Folge wird der Import abgebrochen und eine Fehlermeldung mit Handlungshinweisen für den Benutzer ausgegeben.
|
||||||
Import_fortsetzen = True
|
Import_fortsetzen = True
|
||||||
|
|
||||||
# Prüfen, ob Bereits Daten aus der Datei in der Datenbank vorhanden sind
|
# Prüfen, ob Bereits Daten aus der Datei in der Datenbank vorhanden sind
|
||||||
@@ -1033,18 +1257,16 @@ class Import:
|
|||||||
if pfad_datei in liste_dateinamen_in_db:
|
if pfad_datei in liste_dateinamen_in_db:
|
||||||
Import_fortsetzen = False
|
Import_fortsetzen = False
|
||||||
|
|
||||||
|
# Import abbrechen, wenn das Instrument noch nicht vom Benutzer angelegt wurde.
|
||||||
liste_instrumentenid = [r[0] for r in cursor.execute("SELECT instrumenteID FROM Instrumente").fetchall()]
|
liste_instrumentenid = [r[0] for r in cursor.execute("SELECT instrumenteID FROM Instrumente").fetchall()]
|
||||||
|
cursor.close()
|
||||||
con.close()
|
con.close()
|
||||||
cursor.close
|
|
||||||
if instrumentenID not in liste_instrumentenid:
|
if instrumentenID not in liste_instrumentenid:
|
||||||
Import_fortsetzen = False
|
Import_fortsetzen = False
|
||||||
print(
|
print(
|
||||||
"Der Import wurde abgebrochen. Bitte eine gültige InstrumentenID eingeben. Bei Bedarf ist das Instrument neu anzulegen.")
|
"Der Import wurde abgebrochen. Bitte eine gültige InstrumentenID eingeben. Bei Bedarf ist das Instrument neu anzulegen.")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if Import_fortsetzen:
|
if Import_fortsetzen:
|
||||||
|
|
||||||
liste_basilinien = []
|
liste_basilinien = []
|
||||||
tupel_basislinie = ()
|
tupel_basislinie = ()
|
||||||
with (open(pfad_datei, "r", encoding="utf-8") as txt):
|
with (open(pfad_datei, "r", encoding="utf-8") as txt):
|
||||||
@@ -1052,17 +1274,13 @@ class Import:
|
|||||||
zeile = str(zeile).rstrip("\n").rstrip("\r")
|
zeile = str(zeile).rstrip("\n").rstrip("\r")
|
||||||
aufgeteilt = zeile.split()
|
aufgeteilt = zeile.split()
|
||||||
if aufgeteilt[0][:2] == "@+":
|
if aufgeteilt[0][:2] == "@+":
|
||||||
#print(aufgeteilt[0][2:])
|
|
||||||
tupel_basislinie += (aufgeteilt[0][2:],)
|
tupel_basislinie += (aufgeteilt[0][2:],)
|
||||||
if aufgeteilt[0][:2] == "@-":
|
if aufgeteilt[0][:2] == "@-":
|
||||||
#print(aufgeteilt[0][2:], aufgeteilt[1], aufgeteilt[2], aufgeteilt[3])
|
|
||||||
tupel_basislinie += (aufgeteilt[0][2:], aufgeteilt[1], aufgeteilt[2], aufgeteilt[3],)
|
tupel_basislinie += (aufgeteilt[0][2:], aufgeteilt[1], aufgeteilt[2], aufgeteilt[3],)
|
||||||
if aufgeteilt[0][:2] == "@=":
|
if aufgeteilt[0][:2] == "@=":
|
||||||
#print(aufgeteilt[1], aufgeteilt[2], aufgeteilt[3], aufgeteilt[4], aufgeteilt[5], aufgeteilt[6], aufgeteilt[7])
|
|
||||||
tupel_basislinie += (aufgeteilt[1], aufgeteilt[2], aufgeteilt[3], aufgeteilt[4], aufgeteilt[5], aufgeteilt[6], aufgeteilt[7], )
|
tupel_basislinie += (aufgeteilt[1], aufgeteilt[2], aufgeteilt[3], aufgeteilt[4], aufgeteilt[5], aufgeteilt[6], aufgeteilt[7], )
|
||||||
liste_basilinien.append(tupel_basislinie)
|
liste_basilinien.append(tupel_basislinie)
|
||||||
tupel_basislinie = ()
|
tupel_basislinie = ()
|
||||||
#print(liste_basilinien)
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
print(
|
print(
|
||||||
@@ -1078,7 +1296,4 @@ class Import:
|
|||||||
con.commit()
|
con.commit()
|
||||||
cursor.close()
|
cursor.close()
|
||||||
con.close()
|
con.close()
|
||||||
print(f"Der Import der Datei {pfad_datei} wurde erfolgreich abgeschlossen.")
|
print(f"Der Import der Datei {pfad_datei} wurde erfolgreich abgeschlossen.")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -13,11 +13,56 @@ from pyproj import CRS, Transformer
|
|||||||
|
|
||||||
|
|
||||||
class Transformationen:
|
class Transformationen:
|
||||||
|
"""Koordinatentransformationen und Helmert-Transformation (Euler-Winkel) zwischen Referenzsystemen.
|
||||||
|
|
||||||
|
Die Klasse stellt Methoden zur Verfügung für:
|
||||||
|
|
||||||
|
- Aufbau einer Rotationsmatrix aus Eulerwinkeln,
|
||||||
|
- Schätzung von 7-Parameter-Transformationsparametern (dX, dY, dZ, Maßstab m, Eulerwinkel e1/e2/e3)
|
||||||
|
aus identischen Punkten zwischen lokalem Horizontsystem (LH) und geozentrisch-kartesischem System (ECEF),
|
||||||
|
- Anwendung der geschätzten Helmerttransformation auf Punkte, die nur im Ausgangssystem vorliegen,
|
||||||
|
- Transformation zwischen ETRS89 / UTM (+ DHHN2016 Normalhöhe) und ETRS89 geozentrisch-kartesisch (ECEF),
|
||||||
|
inkl. Nutzung einer BKG-Quasigeoidundulations-Datei (GeoTIFF) für PROJ.
|
||||||
|
|
||||||
|
Die grundlegende Funktionsweise der Transformationsschätzung lautet:
|
||||||
|
|
||||||
|
1) Identische Punkte aus Ausgangs- und Zielsystem ermitteln.
|
||||||
|
2) Näherung für Maßstab m0 aus mittleren Streckenverhältnissen bilden.
|
||||||
|
3) Näherungs-Rotation R0 aus lokalen Basen (u/v/w und U/V/W) bestimmen und daraus Euler-Näherungen ableiten.
|
||||||
|
4) Iterative Parameterschätzung (Gauss-Newton) auf Basis der Beobachtungsgleichung:
|
||||||
|
P = T + m * R(e1,e2,e3) * p
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, pfad_datenbank: str) -> None:
|
def __init__(self, pfad_datenbank: str) -> None:
|
||||||
|
"""Initialisiert die Transformationsklasse.
|
||||||
|
|
||||||
|
Speichert den Pfad zur SQLite-Datenbank und initialisiert den Datenbankzugriff.
|
||||||
|
|
||||||
|
:param pfad_datenbank: Pfad zur SQLite-Datenbank.
|
||||||
|
:type pfad_datenbank: str
|
||||||
|
:return: None
|
||||||
|
:rtype: None
|
||||||
|
"""
|
||||||
self.pfad_datenbank = pfad_datenbank
|
self.pfad_datenbank = pfad_datenbank
|
||||||
|
self.db_zugriff = Datenbank.Datenbankzugriff(self.pfad_datenbank)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def R_matrix_aus_euler(e1: float, e2: float, e3: float) -> sp.Matrix:
|
def R_matrix_aus_eulerwinkeln(e1: float, e2: float, e3: float) -> sp.Matrix:
|
||||||
|
"""Erstellt eine 3x3-Rotationsmatrix aus Eulerwinkeln.
|
||||||
|
|
||||||
|
Die Rotationsmatrix wird symbolisch (SymPy) aufgebaut. Die Eulerwinkel e1, e2, e3 werden
|
||||||
|
direkt in trigonometrische Ausdrücke eingesetzt und eine orthogonale Rotationsmatrix R(e1,e2,e3)
|
||||||
|
zur Verwendung in Helmert-Transformationen zurückgegeben.
|
||||||
|
|
||||||
|
:param e1: Eulerwinkel 1 (Radiant).
|
||||||
|
:type e1: float
|
||||||
|
:param e2: Eulerwinkel 2 (Radiant).
|
||||||
|
:type e2: float
|
||||||
|
:param e3: Eulerwinkel 3 (Radiant).
|
||||||
|
:type e3: float
|
||||||
|
:return: Rotationsmatrix R als SymPy-Matrix (3x3).
|
||||||
|
:rtype: sp.Matrix
|
||||||
|
"""
|
||||||
return sp.Matrix([
|
return sp.Matrix([
|
||||||
[
|
[
|
||||||
sp.cos(e2) * sp.cos(e3),
|
sp.cos(e2) * sp.cos(e3),
|
||||||
@@ -36,32 +81,48 @@ class Transformationen:
|
|||||||
]
|
]
|
||||||
])
|
])
|
||||||
|
|
||||||
def Helmerttransformation_Euler_Transformationsparameter_berechne(self) -> dict[Any, float]:
|
def Helmerttransformation_Euler_Transformationsparameter_berechnen(self) -> dict[Any, float]:
|
||||||
db = Datenbank.Datenbankzugriff(self.pfad_datenbank)
|
"""Schätzt die Helmert-Transformationsparameter aus identischen Punkten.
|
||||||
dict_ausgangssystem = db.get_koordinaten("naeherung_lh", "Dict")
|
|
||||||
dict_zielsystem = db.get_koordinaten("naeherung_us", "Dict")
|
|
||||||
|
|
||||||
|
Aus der Datenbank werden Näherungskoordinaten des lokalen Horizontsystems (naeherung_lh)
|
||||||
|
und des geozentrisch-kartesischen Systems (naeherung_us) geladen. Für die Schnittmenge der
|
||||||
|
Punkte werden die 7 Helmertparameter geschätzt:
|
||||||
|
|
||||||
|
- Translation (dX, dY, dZ),
|
||||||
|
- Maßstab m,
|
||||||
|
- Eulerwinkel (e1, e2, e3).
|
||||||
|
|
||||||
|
Näherungen:
|
||||||
|
|
||||||
|
- m0: Mittelwert der Streckenverhältnisse aus allen Punktpaaren,
|
||||||
|
- R0: Anfangsrotationsmatrix aus lokalen Basisvektoren (u/v/w und U/V/W),
|
||||||
|
- Translation0: aus Schwerpunkten und m0/R0.
|
||||||
|
|
||||||
|
Die Parameterschätzung erfolgt iterativ mit P = I.
|
||||||
|
Abbruchkriterium: |dx_i| < schwellenwert in zwei aufeinanderfolgenden Iterationen oder max. 100 Iterationen.
|
||||||
|
|
||||||
|
:param: None
|
||||||
|
:return: Dictionary der finalen Parameter mit SymPy-Symbolen als Keys und float-Werten als Values
|
||||||
|
(Keys: dX, dY, dZ, m, e1, e2, e3).
|
||||||
|
:rtype: dict[Any, float]
|
||||||
|
"""
|
||||||
|
# Koordinaten des lokalen Horizontsystems des Tachymeters und der geozentrisch Kartesischen Näherungskoordinaten aus den statischen GNSS-Messungen aus der Tabelle Netzpunkte abfragen
|
||||||
|
dict_ausgangssystem = self.db_zugriff.get_koordinaten("naeherung_lh", "Dict")
|
||||||
|
dict_zielsystem = self.db_zugriff.get_koordinaten("naeherung_us", "Dict")
|
||||||
|
|
||||||
|
# Identische Punkte ermitteln
|
||||||
gemeinsame_punktnummern = sorted(set(dict_ausgangssystem.keys()) & set(dict_zielsystem.keys()))
|
gemeinsame_punktnummern = sorted(set(dict_ausgangssystem.keys()) & set(dict_zielsystem.keys()))
|
||||||
anzahl_gemeinsame_punkte = len(gemeinsame_punktnummern)
|
anzahl_gemeinsame_punkte = len(gemeinsame_punktnummern)
|
||||||
|
|
||||||
liste_punkte_ausgangssystem = [dict_ausgangssystem[i] for i in gemeinsame_punktnummern]
|
liste_punkte_ausgangssystem = [dict_ausgangssystem[i] for i in gemeinsame_punktnummern]
|
||||||
liste_punkte_zielsystem = [dict_zielsystem[i] for i in gemeinsame_punktnummern]
|
liste_punkte_zielsystem = [dict_zielsystem[i] for i in gemeinsame_punktnummern]
|
||||||
|
|
||||||
print("Anzahl gemeinsame Punkte:", anzahl_gemeinsame_punkte)
|
print("Anzahl verwendete Punkte für die Helmerttransformation:", anzahl_gemeinsame_punkte)
|
||||||
|
|
||||||
print("\nErste Zielpunkte:")
|
|
||||||
for pn, P in list(zip(gemeinsame_punktnummern, liste_punkte_zielsystem))[:5]:
|
|
||||||
print(pn, [float(P[0]), float(P[1]), float(P[2])])
|
|
||||||
|
|
||||||
print("\nErste Ausgangspunkte:")
|
|
||||||
for pn, p in list(zip(gemeinsame_punktnummern, liste_punkte_ausgangssystem))[:5]:
|
|
||||||
print(pn, [float(p[0]), float(p[1]), float(p[2])])
|
|
||||||
|
|
||||||
# --- Näherungswerte (minimal erweitert) ---
|
|
||||||
p1, p2, p3 = liste_punkte_ausgangssystem[0], liste_punkte_ausgangssystem[1], liste_punkte_ausgangssystem[2]
|
p1, p2, p3 = liste_punkte_ausgangssystem[0], liste_punkte_ausgangssystem[1], liste_punkte_ausgangssystem[2]
|
||||||
P1, P2, P3 = liste_punkte_zielsystem[0], liste_punkte_zielsystem[1], liste_punkte_zielsystem[2]
|
P1, P2, P3 = liste_punkte_zielsystem[0], liste_punkte_zielsystem[1], liste_punkte_zielsystem[2]
|
||||||
|
|
||||||
# 1) Näherungswert Maßstab: Mittelwert aus allen Punktpaaren
|
# Näherungswert für dem Maßstab berechnen aus dem Mittelwert aller Punktpaare
|
||||||
ratios = []
|
ratios = []
|
||||||
for i, j in combinations(range(anzahl_gemeinsame_punkte), 2):
|
for i, j in combinations(range(anzahl_gemeinsame_punkte), 2):
|
||||||
dp = (liste_punkte_ausgangssystem[j] - liste_punkte_ausgangssystem[i]).norm()
|
dp = (liste_punkte_ausgangssystem[j] - liste_punkte_ausgangssystem[i]).norm()
|
||||||
@@ -72,12 +133,7 @@ class Transformationen:
|
|||||||
|
|
||||||
m0 = sum(ratios) / len(ratios)
|
m0 = sum(ratios) / len(ratios)
|
||||||
|
|
||||||
if ratios:
|
# Näherungswert für die Translation berechnen
|
||||||
print("min/mean/max:",
|
|
||||||
min(ratios),
|
|
||||||
sum(ratios) / len(ratios),
|
|
||||||
max(ratios))
|
|
||||||
|
|
||||||
U = (P2 - P1) / (P2 - P1).norm()
|
U = (P2 - P1) / (P2 - P1).norm()
|
||||||
W = (U.cross(P3 - P1)) / (U.cross(P3 - P1)).norm()
|
W = (U.cross(P3 - P1)) / (U.cross(P3 - P1)).norm()
|
||||||
V = W.cross(U)
|
V = W.cross(U)
|
||||||
@@ -93,28 +149,18 @@ class Transformationen:
|
|||||||
|
|
||||||
Translation0 = XS - m0 * R0 * xS
|
Translation0 = XS - m0 * R0 * xS
|
||||||
|
|
||||||
# 2) Test auf orthonormale Drehmatrix bei 3 Nachkommastellen!
|
# Euler-Näherungswerte aus der Anfangsrotationsmatrix
|
||||||
if R0.T.applyfunc(lambda x: round(float(x), 3)) == R0.inv().applyfunc(lambda x: round(float(x), 3)) \
|
|
||||||
and (R0.T * R0).applyfunc(lambda x: round(float(x), 3)) == sp.eye(3).applyfunc(
|
|
||||||
lambda x: round(float(x), 3)) \
|
|
||||||
and ((round(R0.det(), 3) == 1.000 or round(R0.det(), 3) == -1.000)):
|
|
||||||
print("R ist Orthonormal!")
|
|
||||||
else:
|
|
||||||
print("R ist nicht Orthonormal!")
|
|
||||||
|
|
||||||
# 3) Euler-Näherungswerte aus R0
|
|
||||||
e2_0 = sp.asin(R0[2, 0])
|
e2_0 = sp.asin(R0[2, 0])
|
||||||
# Schutz gegen Division durch 0 wenn cos(e2) ~ 0:
|
|
||||||
cos_e2_0 = sp.cos(e2_0)
|
cos_e2_0 = sp.cos(e2_0)
|
||||||
|
|
||||||
e1_0 = sp.acos(R0[2, 2] / cos_e2_0)
|
e1_0 = sp.acos(R0[2, 2] / cos_e2_0)
|
||||||
e3_0 = sp.acos(R0[0, 0] / cos_e2_0)
|
e3_0 = sp.acos(R0[0, 0] / cos_e2_0)
|
||||||
|
|
||||||
# --- Symbolische Unbekannte (klassische 7 Parameter) ---
|
# Symbolische Unbekannte
|
||||||
dX, dY, dZ, m, e1, e2, e3 = sp.symbols('dX dY dZ m e1 e2 e3')
|
dX, dY, dZ, m, e1, e2, e3 = sp.symbols('dX dY dZ m e1 e2 e3')
|
||||||
R_symbolisch = self.R_matrix_aus_euler(e1, e2, e3)
|
R_symbolisch = self.R_matrix_aus_eulerwinkeln(e1, e2, e3)
|
||||||
|
|
||||||
# 4) Funktionales Modell
|
# Funktionales Modell
|
||||||
f_zeilen = []
|
f_zeilen = []
|
||||||
for punkt in liste_punkte_ausgangssystem:
|
for punkt in liste_punkte_ausgangssystem:
|
||||||
punkt_vektor = sp.Matrix([punkt[0], punkt[1], punkt[2]])
|
punkt_vektor = sp.Matrix([punkt[0], punkt[1], punkt[2]])
|
||||||
@@ -134,7 +180,7 @@ class Transformationen:
|
|||||||
l_vektor = sp.Matrix([koord for P in liste_punkte_zielsystem for koord in P])
|
l_vektor = sp.Matrix([koord for P in liste_punkte_zielsystem for koord in P])
|
||||||
l = l_vektor
|
l = l_vektor
|
||||||
|
|
||||||
P_mat = sp.eye(3 * anzahl_gemeinsame_punkte)
|
P_matrix = sp.eye(3 * anzahl_gemeinsame_punkte)
|
||||||
l_berechnet_0 = None
|
l_berechnet_0 = None
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
@@ -156,8 +202,8 @@ class Transformationen:
|
|||||||
dl_0 = l_vektor - l_berechnet_0
|
dl_0 = l_vektor - l_berechnet_0
|
||||||
|
|
||||||
A_0 = A_ohne_zahlen.subs(zahlen_0).evalf(n=30)
|
A_0 = A_ohne_zahlen.subs(zahlen_0).evalf(n=30)
|
||||||
N = A_0.T * P_mat * A_0
|
N = A_0.T * P_matrix * A_0
|
||||||
n_0 = A_0.T * P_mat * dl_0
|
n_0 = A_0.T * P_matrix * dl_0
|
||||||
Qxx_0 = N.inv()
|
Qxx_0 = N.inv()
|
||||||
dx = Qxx_0 * n_0
|
dx = Qxx_0 * n_0
|
||||||
x = x0 + dx
|
x = x0 + dx
|
||||||
@@ -165,7 +211,6 @@ class Transformationen:
|
|||||||
|
|
||||||
anzahl_iterationen += 1
|
anzahl_iterationen += 1
|
||||||
print(f"Iteration Nr.{anzahl_iterationen} abgeschlossen")
|
print(f"Iteration Nr.{anzahl_iterationen} abgeschlossen")
|
||||||
print(dx.evalf(n=3))
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
zahlen_i = {
|
zahlen_i = {
|
||||||
@@ -182,16 +227,17 @@ class Transformationen:
|
|||||||
dl_i = l_vektor - l_berechnet_i
|
dl_i = l_vektor - l_berechnet_i
|
||||||
|
|
||||||
A_i = A_ohne_zahlen.subs(zahlen_i).evalf(n=30)
|
A_i = A_ohne_zahlen.subs(zahlen_i).evalf(n=30)
|
||||||
N_i = A_i.T * P_mat * A_i
|
N_i = A_i.T * P_matrix * A_i
|
||||||
Qxx_i = N_i.inv()
|
Qxx_i = N_i.inv()
|
||||||
n_i = A_i.T * P_mat * dl_i
|
n_i = A_i.T * P_matrix * dl_i
|
||||||
|
|
||||||
dx = Qxx_i * n_i
|
dx = Qxx_i * n_i
|
||||||
x = sp.Matrix(x + dx)
|
x = sp.Matrix(x + dx)
|
||||||
|
|
||||||
anzahl_iterationen += 1
|
anzahl_iterationen += 1
|
||||||
print(f"Iteration Nr.{anzahl_iterationen} abgeschlossen")
|
print(f"Iteration Nr.{anzahl_iterationen} abgeschlossen")
|
||||||
print(dx.evalf(n=3))
|
|
||||||
|
|
||||||
|
|
||||||
alle_kleiner = True
|
alle_kleiner = True
|
||||||
for i in range(dx.rows):
|
for i in range(dx.rows):
|
||||||
@@ -204,11 +250,7 @@ class Transformationen:
|
|||||||
|
|
||||||
alle_kleiner_vorherige_iteration = alle_kleiner
|
alle_kleiner_vorherige_iteration = alle_kleiner
|
||||||
|
|
||||||
print(l.evalf(n=3))
|
# Neuberechnung Zielsystem
|
||||||
print(l_berechnet_0.evalf(n=3))
|
|
||||||
print(f"x = {x.evalf(n=3)}")
|
|
||||||
|
|
||||||
# --- Neuberechnung Zielsystem ---
|
|
||||||
zahlen_final = {
|
zahlen_final = {
|
||||||
dX: float(x[0]),
|
dX: float(x[0]),
|
||||||
dY: float(x[1]),
|
dY: float(x[1]),
|
||||||
@@ -228,12 +270,11 @@ class Transformationen:
|
|||||||
Zi = l_berechnet_final[3 * i + 2]
|
Zi = l_berechnet_final[3 * i + 2]
|
||||||
liste_l_berechnet_final.append(sp.Matrix([Xi, Yi, Zi]))
|
liste_l_berechnet_final.append(sp.Matrix([Xi, Yi, Zi]))
|
||||||
|
|
||||||
print("")
|
|
||||||
print("l_berechnet_final:")
|
print("l_berechnet_final:")
|
||||||
for punktnummer, l_fin in zip(gemeinsame_punktnummern, liste_l_berechnet_final):
|
for punktnummer, l_fin in zip(gemeinsame_punktnummern, liste_l_berechnet_final):
|
||||||
print(f"{punktnummer}: {float(l_fin[0]):.3f}, {float(l_fin[1]):.3f}, {float(l_fin[2]):.3f}")
|
print(f"{punktnummer}: {float(l_fin[0]):.3f}, {float(l_fin[1]):.3f}, {float(l_fin[2]):.3f}")
|
||||||
|
|
||||||
print("Streckendifferenzen:")
|
print("Streckendifferenzen zwischen Näherungskoordinate aus statischer GNSS-Messung und ergebnis der Helmerttransformation:")
|
||||||
streckendifferenzen = [
|
streckendifferenzen = [
|
||||||
(punkt_zielsys - l_final).norm()
|
(punkt_zielsys - l_final).norm()
|
||||||
for punkt_zielsys, l_final in zip(liste_punkte_zielsystem, liste_l_berechnet_final)
|
for punkt_zielsys, l_final in zip(liste_punkte_zielsystem, liste_l_berechnet_final)
|
||||||
@@ -245,30 +286,51 @@ class Transformationen:
|
|||||||
|
|
||||||
Schwerpunktsdifferenz = Schwerpunkt_Zielsystem - Schwerpunkt_berechnet
|
Schwerpunktsdifferenz = Schwerpunkt_Zielsystem - Schwerpunkt_berechnet
|
||||||
|
|
||||||
print("\nDifferenz Schwerpunkt (Vektor):")
|
print("\nDifferenz Schwerpunkt zwischen Näherungskoordinate aus statischer GNSS-Messung und ergebnis der Helmerttransformation::")
|
||||||
print(Schwerpunktsdifferenz.evalf(3))
|
print(Schwerpunktsdifferenz.evalf(3))
|
||||||
|
|
||||||
print("Betrag der Schwerpunkt-Differenz:")
|
print("Betrag der Schwerpunkt-Differenz zwischen Näherungskoordinate aus statischer GNSS-Messung und ergebnis der Helmerttransformation::")
|
||||||
print(f"{float(Schwerpunktsdifferenz.norm()):.3f}m")
|
print(f"{float(Schwerpunktsdifferenz.norm()):.3f}m")
|
||||||
|
|
||||||
return zahlen_final
|
return zahlen_final
|
||||||
|
|
||||||
def Helmerttransformation(self, transformationsparameter: dict) -> dict[Any, Any]:
|
def Helmerttransformation(self, transformationsparameter: dict) -> dict[Any, Any]:
|
||||||
db = Datenbank.Datenbankzugriff(self.pfad_datenbank)
|
"""Wendet eine Helmerttransformation auf Punkte des Ausgangssystems an.
|
||||||
dict_ausgangssystem = db.get_koordinaten("naeherung_lh", "Dict")
|
|
||||||
dict_zielsystem = db.get_koordinaten("naeherung_us", "Dict")
|
|
||||||
|
|
||||||
|
Aus der Datenbank werden Koordinaten des Ausgangssystems (naeherung_lh) und Zielsystems (naeherung_us) geladen.
|
||||||
|
Transformiert werden genau die Punkte, die:
|
||||||
|
|
||||||
|
- im Ausgangssystem vorhanden sind,
|
||||||
|
- im Zielsystem fehlen (symmetrische Differenz der Punktmengen, anschließend Filter auf Ausgangssystem).
|
||||||
|
|
||||||
|
Die Transformation erfolgt gemäß:
|
||||||
|
P = [dX, dY, dZ]^T + m * R(e1,e2,e3) * p
|
||||||
|
|
||||||
|
:param transformationsparameter: Transformationsparameter als Dictionary mit SymPy-Symbolen als Keys
|
||||||
|
(dX, dY, dZ, m, e1, e2, e3) und numerischen Werten als Values.
|
||||||
|
:type transformationsparameter: dict
|
||||||
|
:return: Dictionary {punktnummer: sp.Matrix([X, Y, Z])} der transformierten geozentrisch-kartesischen Koordinaten.
|
||||||
|
:rtype: dict[Any, Any]
|
||||||
|
"""
|
||||||
|
# Koordinaten des lokalen Horizontsystems des Tachymeters und der geozentrisch Kartesischen Näherungskoordinaten aus den statischen GNSS-Messungen aus der Tabelle Netzpunkte abfragen
|
||||||
|
dict_ausgangssystem = self.db_zugriff.get_koordinaten("naeherung_lh", "Dict")
|
||||||
|
dict_zielsystem = self.db_zugriff.get_koordinaten("naeherung_us", "Dict")
|
||||||
|
|
||||||
|
# Symbole definieren
|
||||||
dX, dY, dZ, m, e1, e2, e3 = sp.symbols('dX dY dZ m e1 e2 e3')
|
dX, dY, dZ, m, e1, e2, e3 = sp.symbols('dX dY dZ m e1 e2 e3')
|
||||||
|
|
||||||
|
# Unterschiedliche Punkte zwischen Ausgangs- und Zielsystem ermitteln
|
||||||
unterschiedliche_punktnummern = sorted(set(dict_ausgangssystem.keys()) ^ set(dict_zielsystem.keys()))
|
unterschiedliche_punktnummern = sorted(set(dict_ausgangssystem.keys()) ^ set(dict_zielsystem.keys()))
|
||||||
punktnummern_transformieren = [
|
punktnummern_transformieren = [
|
||||||
punktnummer for punktnummer in unterschiedliche_punktnummern if punktnummer in dict_ausgangssystem
|
punktnummer for punktnummer in unterschiedliche_punktnummern if punktnummer in dict_ausgangssystem
|
||||||
]
|
]
|
||||||
liste_punkte_ausgangssystem = [dict_ausgangssystem[punktnummer] for punktnummer in punktnummern_transformieren]
|
liste_punkte_ausgangssystem = [dict_ausgangssystem[punktnummer] for punktnummer in punktnummern_transformieren]
|
||||||
|
|
||||||
R = self.R_matrix_aus_euler(transformationsparameter[e1], transformationsparameter[e2], transformationsparameter[e3])
|
# Rotationsmatrix aufstellen
|
||||||
|
R = self.R_matrix_aus_eulerwinkeln(transformationsparameter[e1], transformationsparameter[e2], transformationsparameter[e3])
|
||||||
|
|
||||||
f_zeilen = []
|
f_zeilen = []
|
||||||
|
# Helmertransformation durchführen und Koordinaten speichern
|
||||||
for punkt in liste_punkte_ausgangssystem:
|
for punkt in liste_punkte_ausgangssystem:
|
||||||
punkt_vektor = sp.Matrix([punkt[0], punkt[1], punkt[2]])
|
punkt_vektor = sp.Matrix([punkt[0], punkt[1], punkt[2]])
|
||||||
f_zeile_i = sp.Matrix([transformationsparameter[dX], transformationsparameter[dY], transformationsparameter[dZ]]) + transformationsparameter[m] * R * punkt_vektor
|
f_zeile_i = sp.Matrix([transformationsparameter[dX], transformationsparameter[dY], transformationsparameter[dZ]]) + transformationsparameter[m] * R * punkt_vektor
|
||||||
@@ -289,9 +351,28 @@ class Transformationen:
|
|||||||
return dict_transformiert
|
return dict_transformiert
|
||||||
|
|
||||||
def utm_to_XYZ(self, pfad_tif_quasigeoidundolation: str, liste_utm: list) -> dict[Any, Any]:
|
def utm_to_XYZ(self, pfad_tif_quasigeoidundolation: str, liste_utm: list) -> dict[Any, Any]:
|
||||||
|
"""Rechnet UTM-Koordinaten (ETRS89 / UTM + DHHN2016) in ECEF-Koordinaten (ETRS89 geozentrisch-kartesisch) um.
|
||||||
|
|
||||||
|
Es wird ein PROJ-Transformer von:
|
||||||
|
|
||||||
|
- Quelle: EPSG:25832 + EPSG:7837 (ETRS89 / UTM Zone 32N + DHHN2016 Normalhöhe),
|
||||||
|
- Ziel: EPSG:4936 (ETRS89 geozentrisch-kartesisch)
|
||||||
|
|
||||||
|
initialisiert. Zusätzlich wird ein BKG-GeoTIFF (Quasigeoidunndulation) in den PROJ-Datenpfad eingebunden,
|
||||||
|
indem eine Kopie mit dem erwarteten Dateinamen "de_bkg_gcg2016.tif" im selben Ordner erzeugt wird.
|
||||||
|
|
||||||
|
:param pfad_tif_quasigeoidundolation: Pfad zur BKG-GeoTIFF-Datei (Quasigeoidundulation).
|
||||||
|
:type pfad_tif_quasigeoidundolation: str
|
||||||
|
:param liste_utm: Liste von UTM-Koordinaten in der Form [(punktnummer, E, N, Normalhoehe), ...].
|
||||||
|
:type liste_utm: list
|
||||||
|
:return: Dictionary {punktnummer: sp.Matrix([X, Y, Z])} mit ECEF-Koordinaten (Meter).
|
||||||
|
:rtype: dict[Any, Any]
|
||||||
|
"""
|
||||||
|
# tif vom BKG zur Quasigeoidundolation übergeben
|
||||||
pfad_gcg_tif = Path(pfad_tif_quasigeoidundolation)
|
pfad_gcg_tif = Path(pfad_tif_quasigeoidundolation)
|
||||||
pfad_gcg_tif_proj = pfad_gcg_tif.with_name("de_bkg_gcg2016.tif")
|
pfad_gcg_tif_proj = pfad_gcg_tif.with_name("de_bkg_gcg2016.tif")
|
||||||
|
|
||||||
|
# Kopie des TIF anlegen (Dies ist voraussetzung für die Transformer-Bibliothek
|
||||||
if (not pfad_gcg_tif_proj.exists()) or (pfad_gcg_tif_proj.stat().st_size != pfad_gcg_tif.stat().st_size):
|
if (not pfad_gcg_tif_proj.exists()) or (pfad_gcg_tif_proj.stat().st_size != pfad_gcg_tif.stat().st_size):
|
||||||
shutil.copy2(pfad_gcg_tif, pfad_gcg_tif_proj)
|
shutil.copy2(pfad_gcg_tif, pfad_gcg_tif_proj)
|
||||||
|
|
||||||
@@ -299,8 +380,9 @@ class Transformationen:
|
|||||||
|
|
||||||
utm_epsg = 25832
|
utm_epsg = 25832
|
||||||
crs_src = CRS.from_user_input(f"EPSG:{utm_epsg}+EPSG:7837") # ETRS89/DREF91 + DHHN2016
|
crs_src = CRS.from_user_input(f"EPSG:{utm_epsg}+EPSG:7837") # ETRS89/DREF91 + DHHN2016
|
||||||
crs_dst = CRS.from_epsg(4936) # ETRS89 geozentrisch (ECEF)
|
crs_dst = CRS.from_epsg(4936) # ETRS89 geozentrisch kartesisch
|
||||||
|
|
||||||
|
# Umrechnungsvorgaben übergeben
|
||||||
tr_best = Transformer.from_crs(
|
tr_best = Transformer.from_crs(
|
||||||
crs_src,
|
crs_src,
|
||||||
crs_dst,
|
crs_dst,
|
||||||
@@ -308,52 +390,54 @@ class Transformationen:
|
|||||||
allow_ballpark=False,
|
allow_ballpark=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Koordinaten rechnen und in Dictionary speichern
|
||||||
dict_geozentrisch_kartesisch = {}
|
dict_geozentrisch_kartesisch = {}
|
||||||
for Punktnummer, E, N, Normalhoehe in liste_utm:
|
for Punktnummer, E, N, Normalhoehe in liste_utm:
|
||||||
X, Y, Z = tr_best.transform(E, N, Normalhoehe)
|
X, Y, Z = tr_best.transform(E, N, Normalhoehe)
|
||||||
dict_geozentrisch_kartesisch[Punktnummer] = sp.Matrix([X, Y, Z])
|
dict_geozentrisch_kartesisch[Punktnummer] = sp.Matrix([X, Y, Z])
|
||||||
|
|
||||||
# geographisch 3D + zeta
|
|
||||||
#crs_geog3d = CRS.from_epsg(4937) # ETRS89 (lon, lat, h)
|
|
||||||
#tr_h = Transformer.from_crs(
|
|
||||||
# crs_src,
|
|
||||||
# crs_geog3d,
|
|
||||||
# always_xy=True,
|
|
||||||
# allow_ballpark=False,
|
|
||||||
#)
|
|
||||||
|
|
||||||
#lon, lat, h = tr_h.transform(E, N, H)
|
|
||||||
#print("lon/lat/h:", lon, lat, h)
|
|
||||||
#print("zeta (h-H):", h - H)
|
|
||||||
|
|
||||||
return dict_geozentrisch_kartesisch
|
return dict_geozentrisch_kartesisch
|
||||||
|
|
||||||
def ecef_to_utm(
|
def ecef_to_utm(
|
||||||
self,
|
self,
|
||||||
dict_koordinaten: dict,
|
dict_koordinaten: dict,
|
||||||
pfad_gcg_tif: str | Path | None = None,
|
pfad_gcg_tif: str | Path | None = None):
|
||||||
zone: int = 32,
|
"""Rechnet ECEF-Koordinaten (ETRS89 geozentrisch-kartesisch) nach nach UTM (+ DHHN2016 Normalhöhe).
|
||||||
):
|
|
||||||
|
|
||||||
if pfad_gcg_tif is not None:
|
Es wird ein PROJ-Transformer von:
|
||||||
pfad_gcg_tif = Path(pfad_gcg_tif).resolve()
|
|
||||||
if not pfad_gcg_tif.exists():
|
|
||||||
raise FileNotFoundError(f"Quasigeoid-Datei nicht gefunden: {pfad_gcg_tif}")
|
|
||||||
|
|
||||||
pfad_proj_grid = pfad_gcg_tif.with_name("de_bkg_gcg2016.tif")
|
- Quelle: EPSG:4936 (ETRS89 geozentrisch-kartesisch),
|
||||||
if (
|
- Ziel: EPSG:25832 + EPSG:7837 (ETRS89 / UTM Zone 32N + DHHN2016 Normalhöhe)
|
||||||
not pfad_proj_grid.exists()
|
|
||||||
or pfad_proj_grid.stat().st_size != pfad_gcg_tif.stat().st_size
|
|
||||||
):
|
|
||||||
shutil.copy2(pfad_gcg_tif, pfad_proj_grid)
|
|
||||||
|
|
||||||
datadir.append_data_dir(str(pfad_proj_grid.parent))
|
initialisiert. Zusätzlich wird die BKG-GeoTIFF-Datei (Quasigeoidundulation) als PROJ-Grid eingebunden,
|
||||||
|
indem eine Kopie mit dem erwarteten Namen "de_bkg_gcg2016.tif" im selben Ordner erzeugt wird.
|
||||||
|
|
||||||
crs_src = CRS.from_epsg(4936) # ETRS89 geocentric (ECEF)
|
Die Methode akzeptiert Koordinatenwerte in verschiedenen Formen (SymPy-Matrix, numpy.ndarray,
|
||||||
|
Liste/Tuple, Skalar) und extrahiert daraus drei Werte (X, Y, Z). Die Ergebnisse (E, N, H) werden auf 8 Nachkommastellen gerundet.
|
||||||
|
|
||||||
|
:param dict_koordinaten: Dictionary {punktnummer: koordinate}, wobei koordinate X/Y/Z enthält.
|
||||||
|
:type dict_koordinaten: dict
|
||||||
|
:param pfad_gcg_tif: Pfad zur BKG-GeoTIFF-Datei (Quasigeoidundulation) als str.
|
||||||
|
:type pfad_gcg_tif: str | Path | None
|
||||||
|
:return: Dictionary {punktnummer: (E, N, H)} mit UTM-Koordinaten (Meter) und Normalhöhe.
|
||||||
|
:rtype: dict
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Kopie des TIF vom BKG mit der Quasigeoidundolation erstellen
|
||||||
|
pfad_gcg_tif = Path(pfad_gcg_tif).resolve()
|
||||||
|
pfad_proj_grid = pfad_gcg_tif.with_name("de_bkg_gcg2016.tif")
|
||||||
|
if (
|
||||||
|
not pfad_proj_grid.exists()
|
||||||
|
or pfad_proj_grid.stat().st_size != pfad_gcg_tif.stat().st_size
|
||||||
|
):
|
||||||
|
shutil.copy2(pfad_gcg_tif, pfad_proj_grid)
|
||||||
|
datadir.append_data_dir(str(pfad_proj_grid.parent))
|
||||||
|
|
||||||
|
# EPSG-Codes feslegen
|
||||||
|
crs_src = CRS.from_epsg(4936) # ETRS89 geozentrisch-kartesisch
|
||||||
|
|
||||||
# Ziel-CRS: ETRS89 / UTM Zone 32/33 + DHHN2016 Normalhöhe
|
# Ziel-CRS: ETRS89 / UTM Zone 32/33 + DHHN2016 Normalhöhe
|
||||||
# EPSG:25832/25833 = ETRS89 / UTM; EPSG:7837 = DHHN2016 height
|
utm_epsg = 25832
|
||||||
utm_epsg = 25800 + zone # 25832 oder 25833
|
|
||||||
crs_dst = CRS.from_user_input(f"EPSG:{utm_epsg}+EPSG:7837")
|
crs_dst = CRS.from_user_input(f"EPSG:{utm_epsg}+EPSG:7837")
|
||||||
|
|
||||||
tr = Transformer.from_crs(
|
tr = Transformer.from_crs(
|
||||||
@@ -365,6 +449,7 @@ class Transformationen:
|
|||||||
|
|
||||||
tr_geo = Transformer.from_crs(CRS.from_epsg(4936), CRS.from_epsg(4979), always_xy=True)
|
tr_geo = Transformer.from_crs(CRS.from_epsg(4936), CRS.from_epsg(4979), always_xy=True)
|
||||||
|
|
||||||
|
# Koordinaten an Dictionary übergeben
|
||||||
dict_koordinaten_utm = {}
|
dict_koordinaten_utm = {}
|
||||||
for punktnummer, koordinate in dict_koordinaten.items():
|
for punktnummer, koordinate in dict_koordinaten.items():
|
||||||
werte = []
|
werte = []
|
||||||
@@ -399,20 +484,8 @@ class Transformationen:
|
|||||||
# Skalar
|
# Skalar
|
||||||
werte.append(float(v))
|
werte.append(float(v))
|
||||||
|
|
||||||
if len(werte) < 3:
|
|
||||||
raise ValueError(f"Zu wenig skalare Werte gefunden: {werte}")
|
|
||||||
|
|
||||||
X, Y, Z = werte[0], werte[1], werte[2]
|
X, Y, Z = werte[0], werte[1], werte[2]
|
||||||
|
|
||||||
try:
|
|
||||||
E, N, H = tr.transform(X, Y, Z, errcheck=True)
|
|
||||||
except ProjError as e:
|
|
||||||
lon, lat, h_ell = tr_geo.transform(X, Y, Z, errcheck=True)
|
|
||||||
raise ProjError(
|
|
||||||
f"transform error (outside grid) | pn={punktnummer} | "
|
|
||||||
f"X,Y,Z={X},{Y},{Z} | lon/lat={lon},{lat} | h_ell={h_ell} | {e}"
|
|
||||||
)
|
|
||||||
|
|
||||||
E, N, H = tr.transform(X, Y, Z, errcheck=True)
|
E, N, H = tr.transform(X, Y, Z, errcheck=True)
|
||||||
# Runden, weil ansonsten aufgrund begrenzter Rechenkapazität falsche Werte Resultieren
|
# Runden, weil ansonsten aufgrund begrenzter Rechenkapazität falsche Werte Resultieren
|
||||||
dict_koordinaten_utm[punktnummer] = (round(E, 8), round(N, 8), round(H, 8))
|
dict_koordinaten_utm[punktnummer] = (round(E, 8), round(N, 8), round(H, 8))
|
||||||
|
|||||||
@@ -116,11 +116,9 @@ class Genauigkeitsmaße:
|
|||||||
float(s_max), float(s_min),
|
float(s_max), float(s_min),
|
||||||
float(t_gon)
|
float(t_gon)
|
||||||
])
|
])
|
||||||
|
|
||||||
except:
|
except:
|
||||||
continue
|
continue
|
||||||
|
standardellipse = pd.DataFrame(daten, columns=["Punkt", "σx [m]", "σy [m]", "σxy [m]", "Große Halbachse [m]", "Kleine Halbachse [m]", "θ [gon]"])
|
||||||
standardellipse = pd.DataFrame(daten, columns=["Punkt", "σx", "σy", "σxy", "s_max", "s_min", "θ [gon]"])
|
|
||||||
return standardellipse
|
return standardellipse
|
||||||
|
|
||||||
|
|
||||||
@@ -191,442 +189,133 @@ class Genauigkeitsmaße:
|
|||||||
except:
|
except:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
konfidenzellipse = pd.DataFrame(daten, columns= ["Punkt", "σx", "σy", "σxy", "a_K", "b_K","θ [gon]"])
|
konfidenzellipse = pd.DataFrame(daten, columns=["Punkt", "σx [m]", "σy [m]", "σxy [m]", "Große Halbachse [m]",
|
||||||
|
"Kleine Halbachse [m]", "θ [gon]"])
|
||||||
return konfidenzellipse
|
return konfidenzellipse
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Plot:
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def konfidenzellipsoid(Qxx, s0_apost, unbekannten_liste, R, alpha, skala="f", return_2d_schnitte=True):
|
def netzplot_ellipsen(
|
||||||
|
Koord_ENU,
|
||||||
|
unbekannten_labels,
|
||||||
|
beobachtungs_labels,
|
||||||
|
df_konf_ellipsen_enu,
|
||||||
|
v_faktor=1000,
|
||||||
|
n_ellipse_pts=60,
|
||||||
|
title="Netzplot im ENU-System mit Konfidenzellipsen"
|
||||||
|
):
|
||||||
|
names = [str(s).strip() for s in unbekannten_labels]
|
||||||
|
|
||||||
Qxx = np.asarray(Qxx, float)
|
if "θ_EN [gon]" in df_konf_ellipsen_enu.columns:
|
||||||
namen_str = [str(sym) for sym in unbekannten_liste]
|
theta_col = "θ_EN [gon]"
|
||||||
punkt_ids = sorted({n[1:] for n in namen_str if n and n[0].upper() in ("X", "Y", "Z")})
|
elif "θ [gon]" in df_konf_ellipsen_enu.columns:
|
||||||
|
theta_col = "θ [gon]"
|
||||||
# Skalierungsfaktor für Konfidenzbereich
|
|
||||||
if skala.lower() == "f":
|
|
||||||
k2_3d = f.ppf(1.0 - alpha, df=3)
|
|
||||||
elif skala.lower() == "f":
|
|
||||||
k2_3d = 3.0 * f.ppf(1.0 - alpha, dfn=3, dfd=R)
|
|
||||||
else:
|
else:
|
||||||
raise ValueError("skala muss 'chi2' oder 'f' sein.")
|
raise ValueError("Spalte 'θ_EN [gon]' oder 'θ [gon]' fehlt im DataFrame.")
|
||||||
|
|
||||||
daten = []
|
punkt_ids = sorted({nm[1:] for nm in names if nm and nm[0].upper() in ("X", "Y", "Z")})
|
||||||
|
|
||||||
for pid in punkt_ids:
|
fig = go.Figure()
|
||||||
try:
|
|
||||||
idx_x = next(i for i, n in enumerate(namen_str) if n.upper() == f"X{pid}".upper())
|
|
||||||
idx_y = next(i for i, n in enumerate(namen_str) if n.upper() == f"Y{pid}".upper())
|
|
||||||
idx_z = next(i for i, n in enumerate(namen_str) if n.upper() == f"Z{pid}".upper())
|
|
||||||
except StopIteration:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# 3x3-Block aus Qxx ziehen
|
# 1) Darstellungen der Beobachtungen
|
||||||
I = [idx_x, idx_y, idx_z]
|
beob_typen = {
|
||||||
Qp = Qxx[np.ix_(I, I)]
|
'GNSS-Basislinien': {'pattern': 'gnss', 'color': 'rgba(255, 100, 0, 0.4)'},
|
||||||
|
'Tachymeter-Beob': {'pattern': '', 'color': 'rgba(100, 100, 100, 0.3)'}
|
||||||
# Kovarianzmatrix (Sigma) des Punkts
|
|
||||||
Sigma = (s0_apost ** 2) * Qp
|
|
||||||
|
|
||||||
# Standardabweichungen
|
|
||||||
sx = float(np.sqrt(Sigma[0, 0]))
|
|
||||||
sy = float(np.sqrt(Sigma[1, 1]))
|
|
||||||
sz = float(np.sqrt(Sigma[2, 2]))
|
|
||||||
|
|
||||||
# Kovarianzen
|
|
||||||
sxy = float(Sigma[0, 1])
|
|
||||||
sxz = float(Sigma[0, 2])
|
|
||||||
syz = float(Sigma[1, 2])
|
|
||||||
|
|
||||||
# Eigenzerlegung (symmetrisch -> eigh)
|
|
||||||
evals, evecs = np.linalg.eigh(Sigma)
|
|
||||||
order = np.argsort(evals)[::-1]
|
|
||||||
evals = evals[order]
|
|
||||||
evecs = evecs[:, order]
|
|
||||||
|
|
||||||
# Numerische Sicherheit: negative Mini-Eigenwerte durch Rundung abklemmen
|
|
||||||
evals = np.clip(evals, 0.0, None)
|
|
||||||
|
|
||||||
# Halbachsen des Konfidenzellipsoids:
|
|
||||||
A, B, C = (np.sqrt(evals * k2_3d)).tolist()
|
|
||||||
|
|
||||||
row = {
|
|
||||||
"Punkt": pid,
|
|
||||||
"σx": sx, "σy": sy, "σz": sz,
|
|
||||||
"σxy": sxy, "σxz": sxz, "σyz": syz,
|
|
||||||
"A_K": float(A), "B_K": float(B), "C_K": float(C),
|
|
||||||
# Orientierung als Spaltenvektoren (Eigenvektoren)
|
|
||||||
"evec_1": evecs[:, 0].tolist(),
|
|
||||||
"evec_2": evecs[:, 1].tolist(),
|
|
||||||
"evec_3": evecs[:, 2].tolist(),
|
|
||||||
"skala_k2": float(k2_3d),
|
|
||||||
"skala_typ": skala.lower()
|
|
||||||
}
|
|
||||||
|
|
||||||
# Optional: 2D-Schnitte (XY, XZ, YZ) als Ellipsenparameter
|
|
||||||
if return_2d_schnitte:
|
|
||||||
row.update(Genauigkeitsmaße.ellipsen_schnitt_2d(Sigma, alpha, R, skala))
|
|
||||||
|
|
||||||
daten.append(row)
|
|
||||||
|
|
||||||
return pd.DataFrame(daten)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def ellipsen_schnitt_2d(Sigma3, alpha, R, skala):
|
|
||||||
def ellipse_from_2x2(S2):
|
|
||||||
# Skalierung für 2D
|
|
||||||
if skala.lower() == "f":
|
|
||||||
k2 = f.ppf(1.0 - alpha, df=2)
|
|
||||||
else:
|
|
||||||
k2 = 2.0 * f.ppf(1.0 - alpha, dfn=2, dfd=R)
|
|
||||||
|
|
||||||
evals, evecs = np.linalg.eigh(S2)
|
|
||||||
order = np.argsort(evals)[::-1]
|
|
||||||
evals = np.clip(evals[order], 0.0, None)
|
|
||||||
evecs = evecs[:, order]
|
|
||||||
|
|
||||||
a, b = np.sqrt(evals * k2)
|
|
||||||
|
|
||||||
# Winkel der Hauptachse (zu a) in der Ebene: atan2(vy, vx)
|
|
||||||
vx, vy = evecs[0, 0], evecs[1, 0]
|
|
||||||
theta_rad = np.arctan2(vy, vx)
|
|
||||||
theta_gon = float(theta_rad * (200.0 / np.pi)) % 200.0
|
|
||||||
|
|
||||||
return float(a), float(b), theta_gon
|
|
||||||
|
|
||||||
# Submatrizen
|
|
||||||
S_xy = Sigma3[np.ix_([0, 1], [0, 1])]
|
|
||||||
S_xz = Sigma3[np.ix_([0, 2], [0, 2])]
|
|
||||||
S_yz = Sigma3[np.ix_([1, 2], [1, 2])]
|
|
||||||
|
|
||||||
axy, bxy, txy = ellipse_from_2x2(S_xy)
|
|
||||||
axz, bxz, txz = ellipse_from_2x2(S_xz)
|
|
||||||
ayz, byz, tyz = ellipse_from_2x2(S_yz)
|
|
||||||
|
|
||||||
return {
|
|
||||||
"aXY": axy, "bXY": bxy, "θXY [gon]": txy,
|
|
||||||
"aXZ": axz, "bXZ": bxz, "θXZ [gon]": txz,
|
|
||||||
"aYZ": ayz, "bYZ": byz, "θYZ [gon]": tyz,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for typ, info in beob_typen.items():
|
||||||
|
x_l, y_l = [], []
|
||||||
|
for bl in beobachtungs_labels:
|
||||||
|
bl_str = str(bl).lower()
|
||||||
|
is_typ = ((info['pattern'] in bl_str and info['pattern'] != '') or
|
||||||
|
(info['pattern'] == '' and 'gnss' not in bl_str and 'niv' not in bl_str))
|
||||||
|
if not is_typ:
|
||||||
|
continue
|
||||||
|
|
||||||
|
bl_raw = str(bl)
|
||||||
|
pts = []
|
||||||
|
for pid in punkt_ids:
|
||||||
|
if (f"_{pid}" in bl_raw) or bl_raw.startswith(f"{pid}_"):
|
||||||
|
if pid in Koord_ENU:
|
||||||
|
pts.append(pid)
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def transform_q_with_your_functions(q_xyz, B, L):
|
|
||||||
# East
|
|
||||||
r11 = Berechnungen.E(L, 1, 0)
|
|
||||||
r12 = Berechnungen.E(L, 0, 1)
|
|
||||||
r13 = 0
|
|
||||||
|
|
||||||
# North
|
|
||||||
r21 = Berechnungen.N(B, L, 1, 0, 0)
|
|
||||||
r22 = Berechnungen.N(B, L, 0, 1, 0)
|
|
||||||
r23 = Berechnungen.N(B, L, 0, 0, 1)
|
|
||||||
|
|
||||||
# Up
|
|
||||||
r31 = Berechnungen.U(B, L, 1, 0, 0)
|
|
||||||
r32 = Berechnungen.U(B, L, 0, 1, 0)
|
|
||||||
r33 = Berechnungen.U(B, L, 0, 0, 1)
|
|
||||||
|
|
||||||
R = np.array([
|
|
||||||
[r11, r12, r13],
|
|
||||||
[r21, r22, r23],
|
|
||||||
[r31, r32, r33]
|
|
||||||
])
|
|
||||||
|
|
||||||
q_enu = R @ q_xyz @ R.T
|
|
||||||
return q_enu
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def plot_netz_komplett_final(x_vektor, unbekannten_labels, beobachtungs_labels, Qxx, sigma0_apost,
|
|
||||||
k_faktor=2.447, v_faktor=1000):
|
|
||||||
"""
|
|
||||||
Optimierter Plot für Jupyter Notebook:
|
|
||||||
- k_faktor: Statistischer Sicherheitsfaktor (2.447 entspricht 95% für 2D)
|
|
||||||
- v_faktor: Optische Überhöhung der Ellipsen (z.B. 1000 = mm werden als m dargestellt)
|
|
||||||
"""
|
|
||||||
|
|
||||||
x_vektor = np.asarray(x_vektor, float).reshape(-1)
|
|
||||||
Qxx = np.asarray(Qxx, float)
|
|
||||||
|
|
||||||
# 1. Datenaufbereitung
|
|
||||||
coords = {}
|
|
||||||
punkt_ids = sorted(set(str(l)[1:] for l in unbekannten_labels if str(l).startswith(('X', 'Y', 'Z'))))
|
|
||||||
pts_data = []
|
|
||||||
|
|
||||||
for pid in punkt_ids:
|
|
||||||
try:
|
|
||||||
ix = next(i for i, s in enumerate(unbekannten_labels) if str(s) == f"X{pid}")
|
|
||||||
iy = next(i for i, s in enumerate(unbekannten_labels) if str(s) == f"Y{pid}")
|
|
||||||
x, y = float(x_vektor[ix]), float(x_vektor[iy])
|
|
||||||
coords[pid] = (x, y)
|
|
||||||
|
|
||||||
# Kovarianzmatrix extrahieren und mit s0^2 skalieren
|
|
||||||
q_idx = [ix, iy]
|
|
||||||
Q_sub = Qxx[np.ix_(q_idx, q_idx)] * (sigma0_apost ** 2)
|
|
||||||
pts_data.append({'id': pid, 'x': x, 'y': y, 'Q': Q_sub})
|
|
||||||
except:
|
|
||||||
continue
|
|
||||||
|
|
||||||
if len(pts_data) == 0:
|
|
||||||
raise ValueError(
|
|
||||||
"Keine Netzpunkte extrahiert. Prüfe: x_vektor Form (u,) und Qxx Form (u,u) sowie Labels 'X<id>'/'Y<id>'.")
|
|
||||||
|
|
||||||
fig = go.Figure()
|
|
||||||
|
|
||||||
# 2. Beobachtungen (Gruppiert)
|
|
||||||
beob_typen = {
|
|
||||||
'GNSS-Basislinien': {'pattern': 'gnss', 'color': 'rgba(255, 100, 0, 0.4)'},
|
|
||||||
'Nivellement': {'pattern': 'niv', 'color': 'rgba(0, 200, 100, 0.4)'},
|
|
||||||
'Tachymeter': {'pattern': '', 'color': 'rgba(100, 100, 100, 0.3)'}
|
|
||||||
}
|
|
||||||
|
|
||||||
for typ, info in beob_typen.items():
|
|
||||||
x_l, y_l = [], []
|
|
||||||
for bl in beobachtungs_labels:
|
|
||||||
bl_str = str(bl).lower()
|
|
||||||
if (info['pattern'] in bl_str and info['pattern'] != '') or (
|
|
||||||
info['pattern'] == '' and 'gnss' not in bl_str and 'niv' not in bl_str):
|
|
||||||
pts = [pid for pid in coords if f"_{pid}" in str(bl) or str(bl).startswith(f"{pid}_")]
|
|
||||||
if len(pts) >= 2:
|
if len(pts) >= 2:
|
||||||
x_l.extend([coords[pts[0]][0], coords[pts[1]][0], None])
|
p1, p2 = pts[0], pts[1]
|
||||||
y_l.extend([coords[pts[0]][1], coords[pts[1]][1], None])
|
x_l.extend([Koord_ENU[p1][0], Koord_ENU[p2][0], None]) # E
|
||||||
|
y_l.extend([Koord_ENU[p1][1], Koord_ENU[p2][1], None]) # N
|
||||||
|
|
||||||
if x_l:
|
if x_l:
|
||||||
fig.add_trace(go.Scatter(x=x_l, y=y_l, mode='lines', name=typ, line=dict(color=info['color'], width=1)))
|
fig.add_trace(go.Scatter(x=x_l, y=y_l, mode='lines', name=typ,
|
||||||
|
line=dict(color=info['color'], width=1)))
|
||||||
|
|
||||||
# 3. Konfidenzellipsen mit v_faktor
|
# 2) Darstellung der Konfidenzellipsen
|
||||||
for pt in pts_data:
|
t = np.linspace(0, 2 * np.pi, n_ellipse_pts)
|
||||||
vals, vecs = np.linalg.eigh(pt['Q'])
|
first = True
|
||||||
order = vals.argsort()[::-1]
|
for _, row in df_konf_ellipsen_enu.iterrows():
|
||||||
vals, vecs = vals[order], vecs[:, order]
|
pid = str(row["Punkt"])
|
||||||
|
if pid not in Koord_ENU:
|
||||||
|
continue
|
||||||
|
|
||||||
theta = np.degrees(np.arctan2(vecs[1, 0], vecs[0, 0]))
|
a = float(row["a_K"]) * v_faktor
|
||||||
# Skalierung: k_faktor (Statistik) * v_faktor (Optik)
|
b = float(row["b_K"]) * v_faktor
|
||||||
a = k_faktor * np.sqrt(vals[0]) * v_faktor
|
theta = float(row[theta_col]) * np.pi / 200.0 # gon->rad
|
||||||
b = k_faktor * np.sqrt(vals[1]) * v_faktor
|
|
||||||
|
|
||||||
t = np.linspace(0, 2 * np.pi, 40)
|
ex = a * np.cos(t)
|
||||||
e_x = a * np.cos(t)
|
ey = b * np.sin(t)
|
||||||
e_y = b * np.sin(t)
|
|
||||||
R = np.array([[np.cos(np.radians(theta)), -np.sin(np.radians(theta))],
|
|
||||||
[np.sin(np.radians(theta)), np.cos(np.radians(theta))]])
|
|
||||||
rot = np.dot(R, np.array([e_x, e_y]))
|
|
||||||
|
|
||||||
fig.add_trace(go.Scatter(
|
c, s = np.cos(theta), np.sin(theta)
|
||||||
x=rot[0, :] + pt['x'], y=rot[1, :] + pt['y'],
|
xr = c * ex - s * ey
|
||||||
mode='lines', line=dict(color='red', width=1.5),
|
yr = s * ex + c * ey
|
||||||
name=f"Ellipsen (Vergrößert {v_faktor}x)",
|
|
||||||
legendgroup="Ellipsen",
|
|
||||||
showlegend=(pt == pts_data[0]), # Nur einmal in der Legende zeigen
|
|
||||||
hoverinfo='skip'
|
|
||||||
))
|
|
||||||
|
|
||||||
# 4. Punkte
|
E0, N0, _ = Koord_ENU[pid]
|
||||||
df_pts = pd.DataFrame(pts_data)
|
|
||||||
fig.add_trace(go.Scatter(
|
|
||||||
x=df_pts['x'], y=df_pts['y'], mode='markers+text',
|
|
||||||
text=df_pts['id'], textposition="top center",
|
|
||||||
marker=dict(size=8, color='black'), name="Netzpunkte"
|
|
||||||
))
|
|
||||||
|
|
||||||
# 5. Layout & Notebook-Größe
|
|
||||||
fig.update_layout(
|
|
||||||
title=f"Netzausgleichung: Ellipsen {v_faktor}-fach vergrößert (k={k_faktor})",
|
|
||||||
xaxis=dict(title="X [m]", tickformat="f", separatethousands=True, scaleanchor="y", scaleratio=1, showgrid=True,
|
|
||||||
gridcolor='lightgrey'),
|
|
||||||
yaxis=dict(title="Y [m]", tickformat="f", separatethousands=True, showgrid=True, gridcolor='lightgrey'),
|
|
||||||
width=1100, # Breite angepasst
|
|
||||||
height=900, # Höhe deutlich vergrößert für Jupiter Notebook
|
|
||||||
plot_bgcolor='white',
|
|
||||||
legend=dict(yanchor="top", y=0.99, xanchor="left", x=0.01, bgcolor="rgba(255,255,255,0.8)")
|
|
||||||
)
|
|
||||||
|
|
||||||
# Info-Annotation als Ersatz für einen physischen Maßstabstab
|
|
||||||
fig.add_annotation(
|
|
||||||
text=f"<b>Maßstab Ellipsen:</b><br>Dargestellte Größe = Wahre Ellipse × {v_faktor}",
|
|
||||||
align='left', showarrow=False, xref='paper', yref='paper', x=0.02, y=0.05,
|
|
||||||
bgcolor="white", bordercolor="black", borderwidth=1)
|
|
||||||
|
|
||||||
fig.show(config={'scrollZoom': True})
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def plot_netz_final_mit_df_ellipsen(x_vektor, unbekannten_labels, beobachtungs_labels, df_ellipsen, v_faktor=1000):
|
|
||||||
# 1. Punkte extrahieren
|
|
||||||
coords = {}
|
|
||||||
# Wir nehmen an, dass die Reihenfolge im x_vektor X, Y, Z pro Punkt ist
|
|
||||||
punkt_ids = sorted(set(str(l)[1:] for l in unbekannten_labels if str(l).startswith(('X', 'Y', 'Z'))))
|
|
||||||
|
|
||||||
for pid in punkt_ids:
|
|
||||||
try:
|
|
||||||
ix = next(i for i, s in enumerate(unbekannten_labels) if str(s) == f"X{pid}")
|
|
||||||
iy = next(i for i, s in enumerate(unbekannten_labels) if str(s) == f"Y{pid}")
|
|
||||||
coords[pid] = (float(x_vektor[ix]), float(x_vektor[iy]))
|
|
||||||
except:
|
|
||||||
continue
|
|
||||||
|
|
||||||
fig = go.Figure()
|
|
||||||
|
|
||||||
# 2. Beobachtungslinien (Gruppiert)
|
|
||||||
beob_typen = {
|
|
||||||
'GNSS-Basislinien': {'pattern': 'gnss', 'color': 'rgba(255, 100, 0, 0.4)'},
|
|
||||||
'Nivellement': {'pattern': 'niv', 'color': 'rgba(0, 200, 100, 0.4)'},
|
|
||||||
'Tachymeter': {'pattern': '', 'color': 'rgba(100, 100, 100, 0.3)'}
|
|
||||||
}
|
|
||||||
|
|
||||||
for typ, info in beob_typen.items():
|
|
||||||
x_l, y_l = [], []
|
|
||||||
for bl in beobachtungs_labels:
|
|
||||||
bl_str = str(bl).lower()
|
|
||||||
# Einfache Logik zur Typtrennung
|
|
||||||
if (info['pattern'] in bl_str and info['pattern'] != '') or \
|
|
||||||
(info['pattern'] == '' and 'gnss' not in bl_str and 'niv' not in bl_str):
|
|
||||||
pts = [pid for pid in coords if f"_{pid}" in str(bl) or str(bl).startswith(f"{pid}_")]
|
|
||||||
if len(pts) >= 2:
|
|
||||||
x_l.extend([coords[pts[0]][0], coords[pts[1]][0], None])
|
|
||||||
y_l.extend([coords[pts[0]][1], coords[pts[1]][1], None])
|
|
||||||
|
|
||||||
if x_l:
|
|
||||||
fig.add_trace(go.Scatter(x=x_l, y=y_l, mode='lines', name=typ, line=dict(color=info['color'], width=1)))
|
|
||||||
|
|
||||||
# 3. Ellipsen aus dem DataFrame zeichnen
|
|
||||||
for _, row in df_ellipsen.iterrows():
|
|
||||||
pid = str(row['Punkt'])
|
|
||||||
if pid in coords:
|
|
||||||
x0, y0 = coords[pid]
|
|
||||||
|
|
||||||
# Werte aus DF (mit v_faktor skalieren)
|
|
||||||
a = row['a_K'] * v_faktor
|
|
||||||
b = row['b_K'] * v_faktor
|
|
||||||
theta_gon = row['θ [gon]']
|
|
||||||
|
|
||||||
# Umrechnung: gon -> rad für die Rotation
|
|
||||||
# Da im Plot X horizontal und Y vertikal ist, entspricht theta_gon dem Winkel zur X-Achse
|
|
||||||
theta_rad = theta_gon * (np.pi / 200.0)
|
|
||||||
|
|
||||||
# Ellipsen berechnen
|
|
||||||
t = np.linspace(0, 2 * np.pi, 50)
|
|
||||||
e_x = a * np.cos(t)
|
|
||||||
e_y = b * np.sin(t)
|
|
||||||
|
|
||||||
# Ausrichtung der Ellipsen
|
|
||||||
R = np.array([[np.cos(theta_rad), -np.sin(theta_rad)],
|
|
||||||
[np.sin(theta_rad), np.cos(theta_rad)]])
|
|
||||||
|
|
||||||
rot = np.dot(R, np.array([e_x, e_y]))
|
|
||||||
|
|
||||||
fig.add_trace(go.Scatter(
|
fig.add_trace(go.Scatter(
|
||||||
x=rot[0, :] + x0, y=rot[1, :] + y0,
|
x=E0 + xr, y=N0 + yr,
|
||||||
mode='lines', line=dict(color='red', width=1.5),
|
mode="lines",
|
||||||
name='Konfidenzellipsen',
|
line=dict(color="red", width=1.5),
|
||||||
legendgroup='Ellipsen',
|
name=f"Ellipsen (×{v_faktor})",
|
||||||
showlegend=(pid == df_ellipsen.iloc[0]['Punkt']),
|
legendgroup="Ellipsen",
|
||||||
hoverinfo='text',
|
showlegend=first,
|
||||||
text=f"Punkt {pid}<br>a_K: {row['a_K']:.4f}m<br>b_K: {row['b_K']:.4f}m"
|
hoverinfo="skip"
|
||||||
))
|
))
|
||||||
|
first = False
|
||||||
|
|
||||||
# Punkte plotten
|
# 3) Darstellung der Punkte
|
||||||
df_pts = pd.DataFrame([(pid, c[0], c[1]) for pid, c in coords.items()], columns=['ID', 'X', 'Y'])
|
xs, ys, texts, hovers = [], [], [], []
|
||||||
fig.add_trace(go.Scatter(
|
for pid in punkt_ids:
|
||||||
x=df_pts['X'], y=df_pts['Y'], mode='markers+text',
|
if pid not in Koord_ENU:
|
||||||
text=df_pts['ID'], textposition="top center",
|
continue
|
||||||
marker=dict(size=8, color='black'), name="Netzpunkte"))
|
E, N, U = Koord_ENU[pid]
|
||||||
|
xs.append(E);
|
||||||
|
ys.append(N);
|
||||||
|
texts.append(pid)
|
||||||
|
hovers.append(f"Punkt {pid}<br>E={E:.4f} m<br>N={N:.4f} m<br>U={U:.4f} m")
|
||||||
|
|
||||||
# Layout
|
fig.add_trace(go.Scatter(
|
||||||
fig.update_layout(
|
x=xs, y=ys, mode="markers+text",
|
||||||
title=f"Netzplot (Ellipsen {v_faktor}x überhöht)",
|
text=texts, textposition="top center",
|
||||||
xaxis=dict(title="X [m]", tickformat="f", separatethousands=True, scaleanchor="y", scaleratio=1,
|
marker=dict(size=8, color="black"),
|
||||||
showgrid=True, gridcolor='lightgrey'),
|
name="Netzpunkte",
|
||||||
yaxis=dict(title="Y [m]", tickformat="f", separatethousands=True, showgrid=True, gridcolor='lightgrey'),
|
hovertext=hovers, hoverinfo="text"
|
||||||
width=1100, height=900,
|
|
||||||
plot_bgcolor='white')
|
|
||||||
|
|
||||||
# Maßstabsangabe
|
|
||||||
fig.add_annotation(
|
|
||||||
text=f"<b>Skalierung:</b><br>Ellipsengröße im Plot = {v_faktor} × Realität",
|
|
||||||
align='left', showarrow=False, xref='paper', yref='paper', x=0.02, y=0.02,
|
|
||||||
bgcolor="rgba(255,255,255,0.8)", bordercolor="black", borderwidth=1)
|
|
||||||
|
|
||||||
fig.show(config={'scrollZoom': True})
|
|
||||||
|
|
||||||
import plotly.graph_objects as go
|
|
||||||
import numpy as np
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def plot_netz_3D(x_vektor, unbekannten_labels, beobachtungs_labels, df_ellipsen, v_faktor=1000):
|
|
||||||
"""
|
|
||||||
Erzeugt einen interaktiven 3D-Plot des Netzes.
|
|
||||||
- v_faktor: Vergrößerung der Genauigkeits-Achsen (z.B. 1000 für mm -> m)
|
|
||||||
"""
|
|
||||||
# 1. Punkte extrahieren
|
|
||||||
pts = {}
|
|
||||||
punkt_ids = sorted(set(str(l)[1:] for l in unbekannten_labels if str(l).startswith(('X', 'Y', 'Z'))))
|
|
||||||
|
|
||||||
for pid in punkt_ids:
|
|
||||||
try:
|
|
||||||
ix = next(i for i, s in enumerate(unbekannten_labels) if str(s) == f"X{pid}")
|
|
||||||
iy = next(i for i, s in enumerate(unbekannten_labels) if str(s) == f"Y{pid}")
|
|
||||||
iz = next(i for i, s in enumerate(unbekannten_labels) if str(s) == f"Z{pid}")
|
|
||||||
pts[pid] = (float(x_vektor[ix]), float(x_vektor[iy]), float(x_vektor[iz]))
|
|
||||||
except:
|
|
||||||
continue
|
|
||||||
|
|
||||||
fig = go.Figure()
|
|
||||||
|
|
||||||
# 2. Beobachtungen (Linien im Raum)
|
|
||||||
# Wir zeichnen hier einfach alle Verbindungen
|
|
||||||
x_line, y_line, z_line = [], [], []
|
|
||||||
for bl in beobachtungs_labels:
|
|
||||||
p_in_l = [pid for pid in pts if f"_{pid}" in str(bl) or str(bl).startswith(f"{pid}_")]
|
|
||||||
if len(p_in_l) >= 2:
|
|
||||||
p1, p2 = pts[p_in_l[0]], pts[p_in_l[1]]
|
|
||||||
x_line.extend([p1[0], p2[0], None])
|
|
||||||
y_line.extend([p1[1], p2[1], None])
|
|
||||||
z_line.extend([p1[2], p2[2], None])
|
|
||||||
|
|
||||||
fig.add_trace(go.Scatter3d(
|
|
||||||
x=x_line, y=y_line, z=z_line,
|
|
||||||
mode='lines', line=dict(color='gray', width=2),
|
|
||||||
name='Beobachtungen'
|
|
||||||
))
|
|
||||||
|
|
||||||
# 3. Punkte & "Fehler-Kreuze" (als Ersatz für Ellipsoide)
|
|
||||||
# Ein echtes 3D-Ellipsoid ist grafisch schwer, daher zeichnen wir 3 Achsen
|
|
||||||
for pid, coord in pts.items():
|
|
||||||
# Hier könnten wir die echten Halbachsen aus der 3D-Eigenwertanalyse nutzen
|
|
||||||
# Für den Anfang plotten wir die Standardabweichungen sX, sY, sZ als Kreuz
|
|
||||||
fig.add_trace(go.Scatter3d(
|
|
||||||
x=[coord[0]], y=[coord[1]], z=[coord[2]],
|
|
||||||
mode='markers+text', text=[pid],
|
|
||||||
marker=dict(size=4, color='black'), name=f'Punkt {pid}'
|
|
||||||
))
|
))
|
||||||
|
|
||||||
# 4. Layout
|
fig.update_layout(
|
||||||
fig.update_layout(
|
title=f"{title} (Ellipsen ×{v_faktor})",
|
||||||
scene=dict(
|
xaxis=dict(title="E [m]", scaleanchor="y", scaleratio=1, showgrid=True, gridcolor="lightgrey"),
|
||||||
xaxis_title='X [m]',
|
yaxis=dict(title="N [m]", showgrid=True, gridcolor="lightgrey"),
|
||||||
yaxis_title='Y [m]',
|
width=1100, height=900,
|
||||||
zaxis_title='Z [m]',
|
template="plotly_white",
|
||||||
aspectmode='data' # WICHTIG: Verhältnisse 1:1:1 bewahren
|
plot_bgcolor="white"
|
||||||
),
|
)
|
||||||
width=1000, height=800,
|
|
||||||
title="Geozentrisches Netz in 3D"
|
|
||||||
)
|
|
||||||
|
|
||||||
fig.show()
|
fig.add_annotation(
|
||||||
|
text=f"<b>Maßstab Ellipsen:</b><br>Dargestellte Größe = Konfidenzellipse × {v_faktor}",
|
||||||
|
align='left', showarrow=False, xref='paper', yref='paper', x=0.02, y=0.05,
|
||||||
|
bgcolor="white", bordercolor="black", borderwidth=1
|
||||||
|
)
|
||||||
|
|
||||||
# Aufruf
|
fig.show(config={'scrollZoom': True})
|
||||||
@@ -117,199 +117,193 @@ class Zuverlaessigkeit:
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
def aeussere_zuverlaessigkeit_EF_EP_stabil(Lokaltest, labels, Qxx, A, P, s0_apost, unbekannten_liste, x):
|
def aeussere_zuverlaessigkeit(
|
||||||
|
Lokaltest, labels, Qxx, A, P, s0_apost, unbekannten_liste, x,
|
||||||
|
angle_units="rad",
|
||||||
|
ep_use_abs=True,
|
||||||
|
exclude_prefixes=("lA_",),
|
||||||
|
):
|
||||||
df = Lokaltest.copy()
|
df = Lokaltest.copy()
|
||||||
labels = list(labels)
|
labels = [str(l) for l in list(labels)]
|
||||||
|
|
||||||
Qxx = np.asarray(Qxx, float)
|
Qxx = np.asarray(Qxx, float)
|
||||||
A = np.asarray(A, float)
|
A = np.asarray(A, float)
|
||||||
P = np.asarray(P, float)
|
P = np.asarray(P, float)
|
||||||
x = np.asarray(x, float).reshape(-1)
|
x = np.asarray(x, float).reshape(-1)
|
||||||
|
|
||||||
|
namen_str = [str(sym) for sym in unbekannten_liste]
|
||||||
|
|
||||||
|
n = A.shape[0]
|
||||||
|
if len(labels) != n:
|
||||||
|
raise ValueError(f"len(labels)={len(labels)} passt nicht zu A.shape[0]={n}.")
|
||||||
|
if len(df) != n:
|
||||||
|
raise ValueError(f"Lokaltest hat {len(df)} Zeilen, A hat {n} Beobachtungen.")
|
||||||
|
|
||||||
|
# Pseudobeobachtungen rausfiltern
|
||||||
|
keep = np.ones(n, dtype=bool)
|
||||||
|
if exclude_prefixes:
|
||||||
|
for i, lbl in enumerate(labels):
|
||||||
|
if any(lbl.startswith(pref) for pref in exclude_prefixes):
|
||||||
|
keep[i] = False
|
||||||
|
|
||||||
|
# alles konsistent kürzen (wichtig: auch A & P!)
|
||||||
|
df = df.loc[keep].reset_index(drop=True)
|
||||||
|
labels = [lbl for (lbl, k) in zip(labels, keep) if k]
|
||||||
|
A = A[keep, :]
|
||||||
|
P = P[np.ix_(keep, keep)]
|
||||||
|
|
||||||
|
# neue n
|
||||||
|
n = A.shape[0]
|
||||||
|
|
||||||
|
# Daten aus dem Lokaltest
|
||||||
ri = df["r_i"].astype(float).to_numpy()
|
ri = df["r_i"].astype(float).to_numpy()
|
||||||
GF = df["GF_i"].astype(float).to_numpy()
|
GF = df["GF_i"].astype(float).to_numpy()
|
||||||
GRZW = df["GRZW_i"].astype(float).to_numpy()
|
GRZW = df["GRZW_i"].astype(float).to_numpy()
|
||||||
n = A.shape[0]
|
|
||||||
|
|
||||||
# Namen als Strings für die Suche
|
s0 = float(s0_apost)
|
||||||
namen_str = [str(sym) for sym in unbekannten_liste]
|
|
||||||
|
|
||||||
# 1) Einflussfaktor EF berechnen
|
def to_rad(val):
|
||||||
EF = np.zeros(n, dtype=float)
|
if angle_units == "rad":
|
||||||
for i in range(n):
|
return val
|
||||||
nabla_l = np.zeros((n, 1))
|
if angle_units == "gon":
|
||||||
nabla_l[i, 0] = GRZW[i]
|
return val * (np.pi / 200.0)
|
||||||
nabla_x = Qxx @ (A.T @ (P @ nabla_l))
|
if angle_units == "deg":
|
||||||
Qxx_inv_nabla_x = np.linalg.solve(Qxx, nabla_x)
|
return val * (np.pi / 180.0)
|
||||||
EF2 = ((nabla_x.T @ Qxx_inv_nabla_x) / (float(s0_apost) ** 2)).item()
|
raise ValueError("angle_units muss 'rad', 'gon' oder 'deg' sein.")
|
||||||
EF[i] = np.sqrt(max(0, EF2))
|
|
||||||
|
|
||||||
# 2) Koordinaten-Dict
|
# Punktkoordinaten aus x (für Streckenäquivalent bei Winkel-EP)
|
||||||
coords = {}
|
coords = {}
|
||||||
punkt_ids = [n[1:] for n in namen_str if n.upper().startswith("X")]
|
punkt_ids = sorted({name[1:] for name in namen_str
|
||||||
|
if name[:1].upper() in ("X", "Y", "Z") and len(name) > 1})
|
||||||
for pid in punkt_ids:
|
for pid in punkt_ids:
|
||||||
try:
|
try:
|
||||||
ix = namen_str.index(f"X{pid}")
|
ix = namen_str.index(f"X{pid}")
|
||||||
iy = namen_str.index(f"Y{pid}")
|
iy = namen_str.index(f"Y{pid}")
|
||||||
iz = namen_str.index(f"Z{pid}")
|
iz = namen_str.index(f"Z{pid}")
|
||||||
|
coords[pid] = (x[ix], x[iy], x[iz])
|
||||||
coords[pid] = (x[ix], x[iy], x[iz] if iz is not None else 0.0)
|
except ValueError:
|
||||||
except:
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# 3) EP + Standpunkte
|
# Standpunkt/Zielpunkt
|
||||||
EP_m = np.full(len(labels), np.nan, dtype=float)
|
standpunkte = [""] * n
|
||||||
standpunkte = [""] * len(labels)
|
zielpunkte = [""] * n
|
||||||
|
|
||||||
for i, lbl in enumerate(labels):
|
for i, lbl in enumerate(labels):
|
||||||
parts = lbl.split("_")
|
parts = lbl.split("_")
|
||||||
sp, zp = None, None
|
sp, zp = None, None
|
||||||
|
|
||||||
if any(k in lbl for k in ["_SD_", "_R_", "_ZW_"]):
|
if any(k in lbl for k in ["_SD_", "_R_", "_ZW_"]):
|
||||||
if len(parts) >= 5: sp, zp = parts[3].strip(), parts[4].strip()
|
if len(parts) >= 5:
|
||||||
|
sp, zp = parts[3].strip(), parts[4].strip()
|
||||||
elif "gnss" in lbl.lower():
|
elif "gnss" in lbl.lower():
|
||||||
sp, zp = parts[-2].strip(), parts[-1].strip()
|
if len(parts) >= 2:
|
||||||
|
sp, zp = parts[-2].strip(), parts[-1].strip()
|
||||||
elif "niv" in lbl.lower():
|
elif "niv" in lbl.lower():
|
||||||
if len(parts) >= 4:
|
if len(parts) >= 4:
|
||||||
sp = parts[3].strip()
|
sp = parts[3].strip()
|
||||||
|
if len(parts) >= 5:
|
||||||
|
zp = parts[4].strip()
|
||||||
else:
|
else:
|
||||||
sp = parts[-1].strip()
|
sp = parts[-1].strip()
|
||||||
|
|
||||||
standpunkte[i] = sp if sp is not None else ""
|
standpunkte[i] = sp or ""
|
||||||
|
zielpunkte[i] = zp or ""
|
||||||
|
|
||||||
# SD, GNSS, Niv: direkt Wegfehler
|
# Berechnung des EPs
|
||||||
if "_SD_" in lbl or "gnss" in lbl.lower() or "niv" in lbl.lower():
|
EP_GF = (1.0 - ri) * GF
|
||||||
EP_m[i] = (1.0 - ri[i]) * GF[i]
|
EP_grzw = (1.0 - ri) * GRZW
|
||||||
# Winkel: Streckenäquivalent
|
if ep_use_abs:
|
||||||
elif "_R_" in lbl or "_ZW_" in lbl:
|
EP_GF = np.abs(EP_GF)
|
||||||
if sp in coords and zp in coords:
|
EP_grzw = np.abs(EP_grzw)
|
||||||
X1, Y1, _ = coords[sp]
|
|
||||||
X2, Y2, _ = coords[zp]
|
|
||||||
s = np.sqrt((X2 - X1) ** 2 + (Y2 - Y1) ** 2)
|
|
||||||
EP_m[i] = (1.0 - ri[i]) * (GF[i] * s)
|
|
||||||
|
|
||||||
# 4) SP am Standpunkt (2D oder 1D)
|
EP_hat_m = np.full(n, np.nan, float)
|
||||||
diagQ = np.diag(Qxx)
|
EP_grzw_m = np.full(n, np.nan, float)
|
||||||
SP_cache_mm = {}
|
|
||||||
for sp in set([s for s in standpunkte if s]):
|
|
||||||
try:
|
|
||||||
ix = namen_str.index(f"X{sp}")
|
|
||||||
iy = namen_str.index(f"Y{sp}")
|
|
||||||
SP_cache_mm[sp] = float(s0_apost) * np.sqrt(diagQ[ix] + diagQ[iy]) * 1000.0
|
|
||||||
except ValueError:
|
|
||||||
# Falls keine Lage, prüfe Höhe (Nivellement)
|
|
||||||
try:
|
|
||||||
iz = namen_str.index(f"Z{sp}")
|
|
||||||
SP_cache_mm[sp] = float(s0_apost) * np.sqrt(diagQ[iz]) * 1000.0
|
|
||||||
except ValueError:
|
|
||||||
SP_cache_mm[sp] = 0.0
|
|
||||||
|
|
||||||
SP_mm = np.array([SP_cache_mm.get(sp, np.nan) for sp in standpunkte], dtype=float)
|
|
||||||
|
|
||||||
return pd.DataFrame({
|
|
||||||
"Beobachtung": labels, "Stand-Pkt": standpunkte, "EF": EF,
|
|
||||||
"EP [mm]": EP_m * 1000.0, "SP [mm]": SP_mm, "EF*SP [mm]": EF * SP_mm
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def aeussere_zuverlaessigkeit_EF_EP(Lokaltest, labels, Qxx, A, P, s0_apost, unbekannten_liste, x):
|
|
||||||
df = Lokaltest.copy()
|
|
||||||
labels = list(labels)
|
|
||||||
Qxx = np.asarray(Qxx, float)
|
|
||||||
A = np.asarray(A, float)
|
|
||||||
P = np.asarray(P, float)
|
|
||||||
x = np.asarray(x, float).reshape(-1)
|
|
||||||
ri = df["r_i"].astype(float).to_numpy()
|
|
||||||
GF = df["GF_i"].astype(float).to_numpy()
|
|
||||||
s_vi = df["s_vi"].astype(float).to_numpy()
|
|
||||||
GRZW = df["GRZW_i"].astype(float).to_numpy()
|
|
||||||
nzp = df["δ0"].astype(float).to_numpy()
|
|
||||||
n = A.shape[0] # Anzahl Beobachtungen
|
|
||||||
u = A.shape[1] # Anzahl Unbekannte
|
|
||||||
|
|
||||||
# Einflussfaktor EF berechnen
|
|
||||||
EF = np.zeros(n, dtype=float)
|
|
||||||
|
|
||||||
for i in range(n):
|
|
||||||
# 1) ∇l_i aufstellen
|
|
||||||
nabla_l = np.zeros((n, 1))
|
|
||||||
nabla_l[i, 0] = GRZW[i]
|
|
||||||
|
|
||||||
# 2) ∇x_i = Qxx * A^T * P * ∇l_i
|
|
||||||
nabla_x = Qxx @ (A.T @ (P @ nabla_l))
|
|
||||||
|
|
||||||
# 3) EF_i^2 = (∇x_i^T * Qxx^{-1} * ∇x_i) / s0^2
|
|
||||||
Qxx_inv_nabla_x = np.linalg.solve(Qxx, nabla_x) # = Qxx^{-1} ∇x_i
|
|
||||||
#EF2 = float((nabla_x.T @ Qxx_inv_nabla_x) / (float(s0_apost) ** 2)).item()
|
|
||||||
EF2 = ((nabla_x.T @ Qxx_inv_nabla_x) / (float(s0_apost) ** 2)).item()
|
|
||||||
|
|
||||||
EF[i] = np.sqrt(EF2)
|
|
||||||
|
|
||||||
# Koordinaten-Dict aus x
|
|
||||||
coords = {}
|
|
||||||
j = 0
|
|
||||||
while j < len(unbekannten_liste):
|
|
||||||
name = str(unbekannten_liste[j])
|
|
||||||
if name.startswith("X"):
|
|
||||||
pn = name[1:]
|
|
||||||
coords[pn] = (x[j], x[j + 1], x[j + 2])
|
|
||||||
j += 3
|
|
||||||
else:
|
|
||||||
j += 1
|
|
||||||
|
|
||||||
# EP + Standpunkte
|
|
||||||
EP_m = np.full(len(labels), np.nan, dtype=float)
|
|
||||||
standpunkte = [""] * len(labels)
|
|
||||||
|
|
||||||
for i, lbl in enumerate(labels):
|
for i, lbl in enumerate(labels):
|
||||||
parts = lbl.split("_")
|
sp = standpunkte[i]
|
||||||
sp = None
|
zp = zielpunkte[i]
|
||||||
zp = None
|
|
||||||
|
|
||||||
# Tachymeter: ID_SD_GRP_SP_ZP / ID_R_GRP_SP_ZP / ID_ZW_GRP_SP_ZP
|
is_angle = ("_R_" in lbl) or ("_ZW_" in lbl)
|
||||||
if ("_SD_" in lbl) or ("_R_" in lbl) or ("_ZW_" in lbl):
|
if not is_angle:
|
||||||
if len(parts) >= 5:
|
EP_hat_m[i] = EP_GF[i]
|
||||||
sp = parts[3].strip()
|
EP_grzw_m[i] = EP_grzw[i]
|
||||||
zp = parts[4].strip()
|
continue
|
||||||
|
|
||||||
# GNSS: *_gnssbx_SP_ZP etc.
|
# Winkel -> Querabweichung = Winkel(rad) * Strecke (3D)
|
||||||
if ("gnss" in lbl) and (len(parts) >= 4):
|
if sp in coords and zp in coords:
|
||||||
sp = parts[-2].strip()
|
X1, Y1, Z1 = coords[sp]
|
||||||
zp = parts[-1].strip()
|
X2, Y2, Z2 = coords[zp]
|
||||||
|
s = np.sqrt((X2 - X1) ** 2 + (Y2 - Y1) ** 2 + (Z2 - Z1) ** 2)
|
||||||
|
|
||||||
standpunkte[i] = sp if sp is not None else ""
|
EP_hat_m[i] = to_rad(EP_GF[i]) * s
|
||||||
one_minus_r = (1.0 - ri[i])
|
EP_grzw_m[i] = to_rad(EP_grzw[i]) * s
|
||||||
|
|
||||||
# SD + GNSS: direkt in m
|
# 3x3 Blöcke
|
||||||
if ("_SD_" in lbl) or ("gnss" in lbl):
|
def idx_xyz(pid):
|
||||||
EP_m[i] = one_minus_r * GF[i]
|
return [
|
||||||
|
namen_str.index(f"X{pid}"),
|
||||||
|
namen_str.index(f"Y{pid}"),
|
||||||
|
namen_str.index(f"Z{pid}")
|
||||||
|
]
|
||||||
|
|
||||||
# R / ZW: Winkel -> Streckenäquivalent über s
|
# EF lokal + SP lokal (3D)
|
||||||
elif ("_R_" in lbl) or ("_ZW_" in lbl):
|
EF = np.full(n, np.nan, float)
|
||||||
if sp and zp and (sp in coords) and (zp in coords):
|
SP_loc_m = np.full(n, np.nan, float)
|
||||||
X1, Y1, Z1 = coords[sp]
|
EFSP_loc_m = np.full(n, np.nan, float)
|
||||||
X2, Y2, Z2 = coords[zp]
|
|
||||||
s = float(np.sqrt((X2 - X1) ** 2 + (Y2 - Y1) ** 2 + (Z2 - Z1) ** 2))
|
|
||||||
EP_m[i] = one_minus_r * ((GF[i]) * s)
|
|
||||||
|
|
||||||
# SP am Standpunkt (2D)
|
for i in range(n):
|
||||||
diagQ = np.diag(Qxx)
|
sp = standpunkte[i]
|
||||||
SP_cache_mm = {}
|
zp = zielpunkte[i]
|
||||||
|
|
||||||
for sp in set([s for s in standpunkte if s]):
|
blocks = []
|
||||||
idx_x = [k for k, sym in enumerate(unbekannten_liste) if str(sym) == f"X{sp}"][0]
|
idx = []
|
||||||
qx = diagQ[idx_x]
|
|
||||||
qy = diagQ[idx_x + 1]
|
|
||||||
SP_cache_mm[sp] = float(s0_apost) * np.sqrt(qx + qy) * 1000.0
|
|
||||||
|
|
||||||
SP_mm = np.array([SP_cache_mm.get(sp, np.nan) for sp in standpunkte], dtype=float)
|
try:
|
||||||
|
if sp:
|
||||||
|
b = idx_xyz(sp)
|
||||||
|
blocks.append(b)
|
||||||
|
idx += b
|
||||||
|
if zp:
|
||||||
|
b = idx_xyz(zp)
|
||||||
|
blocks.append(b)
|
||||||
|
idx += b
|
||||||
|
except ValueError:
|
||||||
|
continue
|
||||||
|
|
||||||
out = pd.DataFrame({
|
if not blocks:
|
||||||
|
continue
|
||||||
|
|
||||||
|
idx = list(dict.fromkeys(idx)) # unique
|
||||||
|
|
||||||
|
# Δx_i aus Grenzstörung
|
||||||
|
dl = np.zeros((n, 1))
|
||||||
|
dl[i, 0] = GRZW[i]
|
||||||
|
dx = Qxx @ (A.T @ (P @ dl))
|
||||||
|
|
||||||
|
dx_loc = dx[idx, :]
|
||||||
|
Q_loc = Qxx[np.ix_(idx, idx)]
|
||||||
|
|
||||||
|
# EF lokal
|
||||||
|
EF2 = (dx_loc.T @ np.linalg.solve(Q_loc, dx_loc)).item() / (s0 ** 2)
|
||||||
|
EF[i] = np.sqrt(max(0.0, EF2))
|
||||||
|
|
||||||
|
# SP lokal 3D: max trace der 3x3 Punktblöcke
|
||||||
|
tr_list = [np.trace(Qxx[np.ix_(b, b)]) for b in blocks]
|
||||||
|
if not tr_list:
|
||||||
|
continue
|
||||||
|
|
||||||
|
sigmaPmax_loc = s0 * np.sqrt(max(tr_list))
|
||||||
|
SP_loc_m[i] = sigmaPmax_loc
|
||||||
|
EFSP_loc_m[i] = EF[i] * sigmaPmax_loc
|
||||||
|
|
||||||
|
ausgabe_zuv = pd.DataFrame({
|
||||||
"Beobachtung": labels,
|
"Beobachtung": labels,
|
||||||
"Stand-Pkt": standpunkte,
|
"Stand-Pkt": standpunkte,
|
||||||
|
"Ziel-Pkt": zielpunkte,
|
||||||
|
"r_i": ri,
|
||||||
|
"EP_GF [mm]": EP_hat_m * 1000.0,
|
||||||
|
"EP_grzw [mm]": EP_grzw_m * 1000.0,
|
||||||
"EF": EF,
|
"EF": EF,
|
||||||
"EP [mm]": EP_m * 1000.0,
|
"SP_loc_3D [mm]": SP_loc_m * 1000.0,
|
||||||
"SP [mm]": SP_mm,
|
"EF*SP_loc_3D [mm]": EFSP_loc_m * 1000.0,
|
||||||
"EF*SP [mm]": EF * SP_mm,
|
|
||||||
})
|
})
|
||||||
return out
|
return ausgabe_zuv
|
||||||
|
|||||||
@@ -1,91 +1,85 @@
|
|||||||
import sympy as sp
|
|
||||||
import numpy as np
|
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from typing import Dict, Tuple, Iterable, Any
|
import numpy as np
|
||||||
from Export import Export
|
import sympy as sp
|
||||||
|
from typing import Dict, Iterable
|
||||||
|
|
||||||
from Datenbank import Datenbankzugriff
|
from Datenbank import Datenbankzugriff
|
||||||
|
from Export import Export
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class StochastischesModell:
|
class StochastischesModell:
|
||||||
|
"""Stochastisches Modell zur Aufstellung und Auswertung von Varianz-Kovarianz-Matrizen.
|
||||||
|
|
||||||
|
Die Klasse stellt Methoden zur Verfügung für:
|
||||||
|
|
||||||
|
- symbolischen Aufbau der Varianz-Kovarianz-Matrix der Beobachtungen Qll,
|
||||||
|
- numerische Substitution von Qll aus der Datenbank,
|
||||||
|
- symbolischen und numerischen Aufbau der Zusatz-Varianz-Kovarianz-Matrix QAA für Anschlusspunkte (weiche Lagerung, lA_*),
|
||||||
|
- Ableitung zentraler Matrizen der Ausgleichung (P, Qxx, Qll_dach, Qvv) als Hilfsfunktionen.
|
||||||
|
|
||||||
|
Die Grundidee ist, die stochastischen Zusammenhänge zunächst symbolisch abzubilden (SymPy) und anschließend
|
||||||
|
mit Datenbankwerten zu substituieren.
|
||||||
|
"""
|
||||||
|
|
||||||
n_beob: int
|
n_beob: int
|
||||||
sigma_beob: Iterable[float] =None #σ a priori der einzelnen Beobachtung
|
sigma_beob: Iterable[float] =None #σ a priori der einzelnen Beobachtung
|
||||||
gruppe_beob: Iterable[int] =None #Gruppenzugehörigkeit jeder Beobachtung (Distanz, Richtung, GNSS, Nivellement,...,)
|
gruppe_beob: Iterable[int] =None #Gruppenzugehörigkeit jeder Beobachtung (Distanz, Richtung, GNSS, Nivellement,...,)
|
||||||
sigma0_gruppe: Dict[int, float] = field(default_factory=dict) #σ0² für jede Gruppe
|
sigma0_gruppe: Dict[int, float] = field(default_factory=dict) #σ0² für jede Gruppe
|
||||||
|
|
||||||
|
|
||||||
def __post_init__(self):
|
def __init__(self, pfad_datenbank: str) -> None:
|
||||||
# Defaults setzen
|
"""Initialisiert das stochastische Modell.
|
||||||
if self.sigma_beob is None:
|
|
||||||
self.sigma_beob = [1.0] * int(self.n_beob)
|
|
||||||
|
|
||||||
if self.gruppe_beob is None:
|
Speichert den Pfad zur SQLite-Datenbank, initialisiert den Datenbankzugriff und legt Cache-Variablen an,
|
||||||
self.gruppe_beob = [1] * int(self.n_beob)
|
die in den numerischen Auswertungen zur Rechenzeitersparnis wiederverwendet werden.
|
||||||
|
|
||||||
# In SymPy-Spaltenvektoren umwandeln
|
|
||||||
#self.sigma_beob = sp.Matrix(list(self.sigma_beob))
|
|
||||||
#self.gruppe_beob = sp.Matrix(list(self.gruppe_beob))
|
|
||||||
|
|
||||||
# Dimension prüfen
|
|
||||||
#if self.sigma_beob.rows != self.gruppe_beob.rows:
|
|
||||||
# raise ValueError("sigma_beob und gruppe_beob müssen gleich viele Einträge haben.")
|
|
||||||
|
|
||||||
#if self.sigma_beob.rows != int(self.n_beob):
|
|
||||||
# raise ValueError("n_beob passt nicht zur Länge von sigma_beob / gruppe_beob.")
|
|
||||||
|
|
||||||
# Fehlende Gruppen mit sigma0_sq = 1.0 ergänzen
|
|
||||||
#unique_groups = sorted({int(g) for g in self.gruppe_beob})
|
|
||||||
#for g in unique_groups:
|
|
||||||
# if g not in self.sigma0_gruppe:
|
|
||||||
# self.sigma0_gruppe[g] = 1.0
|
|
||||||
|
|
||||||
# In NumPy-Spaltenvektoren umwandeln
|
|
||||||
self.sigma_beob = np.asarray(list(self.sigma_beob), dtype=float).reshape(-1, 1)
|
|
||||||
self.gruppe_beob = np.asarray(list(self.gruppe_beob), dtype=int).reshape(-1, 1)
|
|
||||||
|
|
||||||
# Dimension prüfen
|
|
||||||
if self.sigma_beob.shape[0] != self.gruppe_beob.shape[0]:
|
|
||||||
raise ValueError("sigma_beob und gruppe_beob müssen gleich viele Einträge haben.")
|
|
||||||
|
|
||||||
if self.sigma_beob.shape[0] != int(self.n_beob):
|
|
||||||
raise ValueError("n_beob passt nicht zur Länge von sigma_beob / gruppe_beob.")
|
|
||||||
|
|
||||||
# Fehlende Gruppen mit sigma0_sq = 1.0 ergänzen
|
|
||||||
unique_groups = sorted({int(g) for g in self.gruppe_beob.flatten()})
|
|
||||||
for g in unique_groups:
|
|
||||||
if g not in self.sigma0_gruppe:
|
|
||||||
self.sigma0_gruppe[g] = 1.0
|
|
||||||
|
|
||||||
|
:param pfad_datenbank: Pfad zur SQLite-Datenbank.
|
||||||
|
:type pfad_datenbank: str
|
||||||
|
:return: None
|
||||||
|
:rtype: None
|
||||||
|
"""
|
||||||
|
self.pfad_datenbank = pfad_datenbank
|
||||||
self.func_Qll_numerisch = None
|
self.func_Qll_numerisch = None
|
||||||
self.liste_symbole_lambdify = None
|
self.liste_symbole_lambdify = None
|
||||||
|
self.db_zugriff = Datenbankzugriff(self.pfad_datenbank)
|
||||||
|
|
||||||
|
def Qll_symbolisch(self, liste_beobachtungen_symbolisch: list) -> sp.Matrix:
|
||||||
|
"""Erstellt die symbolische Varianz-Kovarianz-Matrix Qll der Beobachtungen.
|
||||||
|
|
||||||
#def berechne_Qll(self) -> Tuple[sp.Matrix, sp.Matrix]:
|
Aus den symbolischen Beobachtungskennungen wird die Beobachtungsart abgeleitet (Tachymeter: SD/R/ZW,
|
||||||
# n = self.n_beob
|
GNSS: gnssbx/gnssby/gnssbz, Geometrisches Nivellement: niv). Für jede Beobachtung wird eine symbolische Varianzgleichung
|
||||||
# Q_ll = sp.zeros(n, n)
|
aufgestellt und in Qll eingetragen.
|
||||||
# P = sp.zeros(n, n)
|
|
||||||
# for i in range(self.n_beob):
|
|
||||||
# sigma_i = self.sigma_beob[i, 0] #σ-Wert der i-ten Beobachtung holen
|
|
||||||
# g = int(self.gruppe_beob[i, 0]) #Gruppenzugehörigkeit der Beobachtung bestimmen
|
|
||||||
# sigma0_sq = self.sigma0_gruppe[g] #Den Varianzfaktor der Gruppe holen
|
|
||||||
# q_ii = sigma_i**2 #σ² berechnen
|
|
||||||
# Q_ll[i, i] = q_ii #Diagonale
|
|
||||||
# return Q_ll
|
|
||||||
|
|
||||||
def Qll_symbolisch(self, pfad_datenbank: str, liste_beobachtungen_symbolisch: list) -> sp.Matrix:
|
Berücksichtigte Gleichungen:
|
||||||
|
|
||||||
|
- Tachymeter SD: σ = sqrt(σ_konstant² + (σ_streckenprop * SD / 1 000 000)²), Varianz = varkomp * σ²
|
||||||
|
- Tachymeter R/ZW: σ = sqrt(σ_konstant² + (σ_konstant_SD / SD)²), Varianz = varkomp * σ²
|
||||||
|
- GNSS: Diagonale und Korrelationen je Basislinie aus cxx/cyy/czz und cxy/cxz/cyz, jeweils skaliert mit s0²
|
||||||
|
- Nivellement: σ = sqrt(n_wechselpunkte * σ_konstant² + σ_streckenprop² * distanz / 1000), Varianz = varkomp * σ²
|
||||||
|
|
||||||
|
Die symbolische Matrix wird als CSV-Datei in Zwischenergebnisse\\Qll_Symbolisch.csv exportiert.
|
||||||
|
|
||||||
|
:param liste_beobachtungen_symbolisch: Liste der symbolischen Beobachtungskennungen.
|
||||||
|
:type liste_beobachtungen_symbolisch: list
|
||||||
|
:return: Symbolische Varianz-Kovarianz-Matrix Qll.
|
||||||
|
:rtype: sp.Matrix
|
||||||
|
"""
|
||||||
liste_standardabweichungen_symbole = []
|
liste_standardabweichungen_symbole = []
|
||||||
|
|
||||||
|
# Vorbereitung und Abfrage der notwendigen Listen und Dictionaries
|
||||||
liste_beobachtungen_symbolisch = [str(b) for b in liste_beobachtungen_symbolisch]
|
liste_beobachtungen_symbolisch = [str(b) for b in liste_beobachtungen_symbolisch]
|
||||||
liste_beobachtungen_symbolisch = [b for b in liste_beobachtungen_symbolisch if not b.startswith("lA_")]
|
liste_beobachtungen_symbolisch = [b for b in liste_beobachtungen_symbolisch if not b.startswith("lA_")]
|
||||||
Qll = sp.zeros(len(liste_beobachtungen_symbolisch), len(liste_beobachtungen_symbolisch))
|
Qll = sp.zeros(len(liste_beobachtungen_symbolisch), len(liste_beobachtungen_symbolisch))
|
||||||
|
dict_beobachtungenID_instrumenteID = self.db_zugriff.get_instrumenteID_beobachtungenID_dict()
|
||||||
|
|
||||||
db_zugriff = Datenbankzugriff(pfad_datenbank)
|
# Aufstellen der Symbolischen Gleichungen für die Einträge in der Qll-Matrix jeder Beobachtungsgruppe
|
||||||
dict_beobachtungenID_instrumenteID = db_zugriff.get_instrumenteID_beobachtungenID_dict()
|
|
||||||
|
|
||||||
for i, beobachtung_symbolisch_i in enumerate(liste_beobachtungen_symbolisch):
|
for i, beobachtung_symbolisch_i in enumerate(liste_beobachtungen_symbolisch):
|
||||||
aufgeteilt_i = beobachtung_symbolisch_i.split("_")
|
aufgeteilt_i = beobachtung_symbolisch_i.split("_")
|
||||||
|
|
||||||
beobachtungenID_i = int(aufgeteilt_i[0])
|
beobachtungenID_i = int(aufgeteilt_i[0])
|
||||||
instrumenteID_i = dict_beobachtungenID_instrumenteID[beobachtungenID_i]
|
instrumenteID_i = dict_beobachtungenID_instrumenteID[beobachtungenID_i]
|
||||||
|
|
||||||
|
# SD = Schrägdistanzen | R = Richtung | ZW = Zenitwinkel
|
||||||
|
# varkomp = Varianzkomponentenschätzung (Ist noch keine Erfolgt, ist dieser Faktor = 1
|
||||||
if aufgeteilt_i[1] == "SD" or aufgeteilt_i[1] == "R" or aufgeteilt_i[1] == "ZW":
|
if aufgeteilt_i[1] == "SD" or aufgeteilt_i[1] == "R" or aufgeteilt_i[1] == "ZW":
|
||||||
|
|
||||||
beobachtungsart_i = str(aufgeteilt_i[1])
|
beobachtungsart_i = str(aufgeteilt_i[1])
|
||||||
@@ -120,6 +114,7 @@ class StochastischesModell:
|
|||||||
|
|
||||||
Qll[i, i] = (varianzkompontenschaetzung) * sigma ** 2
|
Qll[i, i] = (varianzkompontenschaetzung) * sigma ** 2
|
||||||
|
|
||||||
|
# Setzen der 0-Einträge in der Qll-Matrix
|
||||||
for j in range(i + 1, len(liste_beobachtungen_symbolisch)):
|
for j in range(i + 1, len(liste_beobachtungen_symbolisch)):
|
||||||
beobachtung_symbolisch_j = liste_beobachtungen_symbolisch[j]
|
beobachtung_symbolisch_j = liste_beobachtungen_symbolisch[j]
|
||||||
aufgeteilt_j = beobachtung_symbolisch_j.split("_")
|
aufgeteilt_j = beobachtung_symbolisch_j.split("_")
|
||||||
@@ -128,8 +123,10 @@ class StochastischesModell:
|
|||||||
if beobachtungsart_i == "SD" and beobachtungsart_j == "SD":
|
if beobachtungsart_i == "SD" and beobachtungsart_j == "SD":
|
||||||
Qll[i, j] = 0
|
Qll[i, j] = 0
|
||||||
Qll[j, i] = 0
|
Qll[j, i] = 0
|
||||||
|
|
||||||
|
# GNSS
|
||||||
|
# s0, sowie die kovarianzen cxx, ... entstammen direkt LeicaGeooffice
|
||||||
if aufgeteilt_i [1] == "gnssbx" or aufgeteilt_i[1] == "gnssby" or aufgeteilt_i[1] == "gnssbz":
|
if aufgeteilt_i [1] == "gnssbx" or aufgeteilt_i[1] == "gnssby" or aufgeteilt_i[1] == "gnssbz":
|
||||||
#beobachtungenID_i = int(aufgeteilt_i[0])
|
|
||||||
beobachtungsart_i = str(aufgeteilt_i[1])
|
beobachtungsart_i = str(aufgeteilt_i[1])
|
||||||
varianzkompontenschaetzung = sp.Symbol(
|
varianzkompontenschaetzung = sp.Symbol(
|
||||||
f"varkomp_{instrumenteID_i}_GNSS-Rover_Basislinienbeobachtungen")
|
f"varkomp_{instrumenteID_i}_GNSS-Rover_Basislinienbeobachtungen")
|
||||||
@@ -185,9 +182,8 @@ class StochastischesModell:
|
|||||||
liste_standardabweichungen_symbole.append(czz)
|
liste_standardabweichungen_symbole.append(czz)
|
||||||
Qll[i, i] = (varianzkompontenschaetzung) * (czz * (s0 ** 2))
|
Qll[i, i] = (varianzkompontenschaetzung) * (czz * (s0 ** 2))
|
||||||
|
|
||||||
|
# Geometrisches Nivellement
|
||||||
if aufgeteilt_i[1] == "niv":
|
if aufgeteilt_i[1] == "niv":
|
||||||
#beobachtungenID_i = int(aufgeteilt_i[0])
|
|
||||||
#instrumenteID_i = dict_beobachtungenID_instrumenteID[beobachtungenID_i]
|
|
||||||
beobachtungsart_i = str(aufgeteilt_i[1])
|
beobachtungsart_i = str(aufgeteilt_i[1])
|
||||||
|
|
||||||
varianzkompontenschaetzung = sp.Symbol(
|
varianzkompontenschaetzung = sp.Symbol(
|
||||||
@@ -198,6 +194,7 @@ class StochastischesModell:
|
|||||||
nivellement_distanz = sp.Symbol(f"niv_distanz_{beobachtungenID_i}")
|
nivellement_distanz = sp.Symbol(f"niv_distanz_{beobachtungenID_i}")
|
||||||
nivellement_anz_wechselpunkte = sp.Symbol(f"niv_anz_wechselpunkte_{beobachtungenID_i}")
|
nivellement_anz_wechselpunkte = sp.Symbol(f"niv_anz_wechselpunkte_{beobachtungenID_i}")
|
||||||
|
|
||||||
|
# Berechnen der Standardabweichung unter Einbeziehung der Anzahl Wechselpunkte
|
||||||
sigma = sp.sqrt(nivellement_anz_wechselpunkte * stabw_apriori_konstant ** 2 + stabw_apriori_streckenprop ** 2 * nivellement_distanz / 1000)
|
sigma = sp.sqrt(nivellement_anz_wechselpunkte * stabw_apriori_konstant ** 2 + stabw_apriori_streckenprop ** 2 * nivellement_distanz / 1000)
|
||||||
liste_standardabweichungen_symbole.append(sigma)
|
liste_standardabweichungen_symbole.append(sigma)
|
||||||
|
|
||||||
@@ -206,24 +203,49 @@ class StochastischesModell:
|
|||||||
Export.matrix_to_csv(r"Zwischenergebnisse\Qll_Symbolisch.csv", liste_beobachtungen_symbolisch, liste_beobachtungen_symbolisch, Qll, "Qll")
|
Export.matrix_to_csv(r"Zwischenergebnisse\Qll_Symbolisch.csv", liste_beobachtungen_symbolisch, liste_beobachtungen_symbolisch, Qll, "Qll")
|
||||||
return Qll
|
return Qll
|
||||||
|
|
||||||
def Qll_numerisch(self, pfad_datenbank: str, Qll_Matrix_Symbolisch: sp.Matrix, liste_beobachtungen_symbolisch: list) -> np.Matrix:
|
def Qll_numerisch(self, Qll_Matrix_Symbolisch: sp.Matrix, liste_beobachtungen_symbolisch: list) -> np.Matrix:
|
||||||
|
"""Erstellt eine numerische Varianz-Kovarianz-Matrix aus einer symbolischen Qll-Matrix.
|
||||||
|
|
||||||
|
Es werden die zur Substitution benötigten Werte aus der Datenbank abgefragt und den in Qll vorkommenden Symbolen zugeordnet,
|
||||||
|
u. a.:
|
||||||
|
|
||||||
|
- Genauigkeiten (stabw_apriori_konstant / stabw_apriori_streckenprop) je Instrument und Beobachtungsart,
|
||||||
|
- Varianzkomponenten (varkomp_*) je Instrument und Beobachtungsgruppe,
|
||||||
|
- gemessene Tachymeter-Distanzen SD_* zur Bildung der Winkel-/Zenitwinkel-Ansätze,
|
||||||
|
- GNSS-Kovarianzen cxx, cxy, cxz, cyy, cyz, czz sowie s0 je Basislinie,
|
||||||
|
- Geomtrisches Nivellement-Strecken und Anzahl Wechsel-/Standpunkte.
|
||||||
|
|
||||||
|
Die Methode prüft, ob alle freien Symbole der Qll-Matrix substituierbar sind.
|
||||||
|
Zur Rechenzeitersparnis wird ein Lambdify-Cache geführt. Die Matrix wird anschließend gezielt
|
||||||
|
über Nicht-Null-Einträge befüllt, um unnötige Auswertung von Nullzellen zu vermeiden.
|
||||||
|
|
||||||
|
Die numerische Matrix wird als CSV-Datei in Zwischenergebnisse\\Qll_Numerisch.csv exportiert.
|
||||||
|
|
||||||
|
:param Qll_Matrix_Symbolisch: Symbolische Varianz-Kovarianz-Matrix Qll.
|
||||||
|
:type Qll_Matrix_Symbolisch: sp.Matrix
|
||||||
|
:param liste_beobachtungen_symbolisch: Liste der symbolischen Beobachtungskennungen.
|
||||||
|
:type liste_beobachtungen_symbolisch: list
|
||||||
|
:return: Numerische Varianz-Kovarianz-Matrix Qll als Numpy-Array.
|
||||||
|
:rtype: np.Matrix
|
||||||
|
:raises ValueError: Falls Symbole in Qll_Matrix_Symbolisch enthalten sind, für die keine Substitutionen vorhanden sind.
|
||||||
|
"""
|
||||||
liste_beobachtungen_symbolisch = [str(b).strip() for b in liste_beobachtungen_symbolisch]
|
liste_beobachtungen_symbolisch = [str(b).strip() for b in liste_beobachtungen_symbolisch]
|
||||||
liste_beobachtungen_symbolisch = [b for b in liste_beobachtungen_symbolisch if not b.startswith("lA_")]
|
liste_beobachtungen_symbolisch = [b for b in liste_beobachtungen_symbolisch if not b.startswith("lA_")]
|
||||||
|
|
||||||
db_zugriff = Datenbankzugriff(pfad_datenbank)
|
# Abfragen der Zahlen zu den Symbolen aus der Datenbank
|
||||||
dict_genauigkeiten = db_zugriff.get_genauigkeiten_dict()
|
dict_genauigkeiten = self.db_zugriff.get_genauigkeiten_dict()
|
||||||
dict_beobachtungenID_instrumenteID = db_zugriff.get_instrumenteID_beobachtungenID_dict()
|
|
||||||
|
|
||||||
liste_beobachtungen_tachymeter = db_zugriff.get_beobachtungen_from_beobachtungenid()
|
liste_beobachtungen_tachymeter = self.db_zugriff.get_beobachtungen_from_beobachtungenid()
|
||||||
liste_beobachtungen_gnss = db_zugriff.get_beobachtungen_gnssbasislinien()
|
liste_beobachtungen_gnss = self.db_zugriff.get_beobachtungen_gnssbasislinien()
|
||||||
liste_beobachtungen_nivellement = db_zugriff.get_beobachtungen_nivellement()
|
liste_beobachtungen_nivellement = self.db_zugriff.get_beobachtungen_nivellement()
|
||||||
|
liste_varianzkomponenten = self.db_zugriff.get_varianzkomponentenschaetzung()
|
||||||
liste_varianzkomponenten = db_zugriff.get_varianzkomponentenschaetzung()
|
|
||||||
|
|
||||||
|
# Erstellen eines Dicts mit der zuordnung beobachtungenID : Distanz
|
||||||
dict_beobachtungenID_distanz = {}
|
dict_beobachtungenID_distanz = {}
|
||||||
for standpunkt, zielpunkt, beobachtungenID, beobachtungsgruppeID, tachymeter_richtung, tachymeter_zenitwinkel, tachymeter_distanz in liste_beobachtungen_tachymeter:
|
for standpunkt, zielpunkt, beobachtungenID, beobachtungsgruppeID, tachymeter_richtung, tachymeter_zenitwinkel, tachymeter_distanz in liste_beobachtungen_tachymeter:
|
||||||
dict_beobachtungenID_distanz[int(beobachtungenID)] = tachymeter_distanz
|
dict_beobachtungenID_distanz[int(beobachtungenID)] = tachymeter_distanz
|
||||||
|
|
||||||
|
# Erstellen eines Dicts mit den Genauigkeiten aus der Tabelle Genauigkeiten
|
||||||
dict_genauigkeiten_neu = {}
|
dict_genauigkeiten_neu = {}
|
||||||
for genauigkeitenID, eintrag in dict_genauigkeiten.items():
|
for genauigkeitenID, eintrag in dict_genauigkeiten.items():
|
||||||
instrumenteID = int(eintrag[0])
|
instrumenteID = int(eintrag[0])
|
||||||
@@ -235,6 +257,7 @@ class StochastischesModell:
|
|||||||
|
|
||||||
substitutionen = {}
|
substitutionen = {}
|
||||||
|
|
||||||
|
# Erstellen eines Dicts mit den konstanten Anteilen der Standardabweichungen aus der Datenbank
|
||||||
dict_konstante_sd = {}
|
dict_konstante_sd = {}
|
||||||
for (instrumenteID, beobachtungsart), (stabw_apriori_konstant,
|
for (instrumenteID, beobachtungsart), (stabw_apriori_konstant,
|
||||||
stabw_apriori_streckenprop) in dict_genauigkeiten_neu.items():
|
stabw_apriori_streckenprop) in dict_genauigkeiten_neu.items():
|
||||||
@@ -242,9 +265,11 @@ class StochastischesModell:
|
|||||||
if stabw_apriori_konstant is not None:
|
if stabw_apriori_konstant is not None:
|
||||||
dict_konstante_sd[instrumenteID] = float(stabw_apriori_konstant)
|
dict_konstante_sd[instrumenteID] = float(stabw_apriori_konstant)
|
||||||
|
|
||||||
|
# Zuordnen des numerischen Varianzfaktors zum entsprechenden Symbol für die Substitution
|
||||||
for (varianzkomponenteID, instrumenteID, beobachtungsgruppe, varianz_varianzkomponentenschaetzung) in liste_varianzkomponenten:
|
for (varianzkomponenteID, instrumenteID, beobachtungsgruppe, varianz_varianzkomponentenschaetzung) in liste_varianzkomponenten:
|
||||||
substitutionen[sp.Symbol(f"varkomp_{instrumenteID}_{beobachtungsgruppe.strip()}")] = float(varianz_varianzkomponentenschaetzung)
|
substitutionen[sp.Symbol(f"varkomp_{instrumenteID}_{beobachtungsgruppe.strip()}")] = float(varianz_varianzkomponentenschaetzung)
|
||||||
|
|
||||||
|
# Zuordnen der numerischen Genauigkeitsangaben aus der Datenbank zum entsprechenden Symbol für die Substitution
|
||||||
for (instrumenteID, beobachtungsart), (stabw_apriori_konstant,
|
for (instrumenteID, beobachtungsart), (stabw_apriori_konstant,
|
||||||
stabw_apriori_streckenprop) in dict_genauigkeiten_neu.items():
|
stabw_apriori_streckenprop) in dict_genauigkeiten_neu.items():
|
||||||
|
|
||||||
@@ -257,7 +282,6 @@ class StochastischesModell:
|
|||||||
elif beobachtungsart == "Geometrisches_Nivellement":
|
elif beobachtungsart == "Geometrisches_Nivellement":
|
||||||
beobachtungsart_kurz = "niv"
|
beobachtungsart_kurz = "niv"
|
||||||
|
|
||||||
|
|
||||||
if stabw_apriori_konstant is not None:
|
if stabw_apriori_konstant is not None:
|
||||||
substitutionen[sp.Symbol(f"stabw_apriori_konstant_{beobachtungsart_kurz}_{instrumenteID}")] = float(stabw_apriori_konstant)
|
substitutionen[sp.Symbol(f"stabw_apriori_konstant_{beobachtungsart_kurz}_{instrumenteID}")] = float(stabw_apriori_konstant)
|
||||||
if stabw_apriori_streckenprop is not None:
|
if stabw_apriori_streckenprop is not None:
|
||||||
@@ -266,17 +290,12 @@ class StochastischesModell:
|
|||||||
wert = wert / 1000.0
|
wert = wert / 1000.0
|
||||||
substitutionen[sp.Symbol(f"stabw_apriori_streckenprop_{beobachtungsart_kurz}_{instrumenteID}")] = wert
|
substitutionen[sp.Symbol(f"stabw_apriori_streckenprop_{beobachtungsart_kurz}_{instrumenteID}")] = wert
|
||||||
|
|
||||||
# --- DEBUG NIV Genauigkeiten (einmalig) ---
|
|
||||||
for k, v in substitutionen.items():
|
|
||||||
ks = str(k)
|
|
||||||
if "stabw_apriori_streckenprop_niv_" in ks or "stabw_apriori_konstant_niv_" in ks:
|
|
||||||
print("DEBUG", ks, "=", v)
|
|
||||||
|
|
||||||
for instrumenteID, wert in dict_konstante_sd.items():
|
for instrumenteID, wert in dict_konstante_sd.items():
|
||||||
substitutionen[sp.Symbol(f"stabw_apriori_konstant_SD_{instrumenteID}")] = float(wert)
|
substitutionen[sp.Symbol(f"stabw_apriori_konstant_SD_{instrumenteID}")] = float(wert)
|
||||||
|
|
||||||
liste_beobachtungen_symbolisch = [str(b) for b in liste_beobachtungen_symbolisch]
|
liste_beobachtungen_symbolisch = [str(b) for b in liste_beobachtungen_symbolisch]
|
||||||
|
|
||||||
|
# Zuordnen der numerischen gemessenen Distanz aus der Datenbank zum entsprechenden Symbol für die Substitution
|
||||||
for beobachtung_symbolisch in liste_beobachtungen_symbolisch:
|
for beobachtung_symbolisch in liste_beobachtungen_symbolisch:
|
||||||
aufgeteilt = beobachtung_symbolisch.split("_")
|
aufgeteilt = beobachtung_symbolisch.split("_")
|
||||||
beobachtungenID = int(aufgeteilt[0])
|
beobachtungenID = int(aufgeteilt[0])
|
||||||
@@ -285,7 +304,7 @@ class StochastischesModell:
|
|||||||
if distanz is not None:
|
if distanz is not None:
|
||||||
substitutionen[sp.Symbol(f"SD_{beobachtungenID}")] = float(distanz)
|
substitutionen[sp.Symbol(f"SD_{beobachtungenID}")] = float(distanz)
|
||||||
|
|
||||||
#GNSS Basislinien
|
# Zuordnen der numerischen GNSS-Bestandteile aus der Tabelle Beobachtungen zum entsprechenden Symbol für die Substitution
|
||||||
for gnss_beobachtungen in liste_beobachtungen_gnss:
|
for gnss_beobachtungen in liste_beobachtungen_gnss:
|
||||||
beobachtungenID = gnss_beobachtungen[0]
|
beobachtungenID = gnss_beobachtungen[0]
|
||||||
gnss_s0 = gnss_beobachtungen[6]
|
gnss_s0 = gnss_beobachtungen[6]
|
||||||
@@ -313,7 +332,7 @@ class StochastischesModell:
|
|||||||
substitutionen[sp.Symbol(f"niv_anz_wechselpunkte_{beobachtungenID}")] = float(niv_anz_standpkte)
|
substitutionen[sp.Symbol(f"niv_anz_wechselpunkte_{beobachtungenID}")] = float(niv_anz_standpkte)
|
||||||
substitutionen[sp.Symbol(f"niv_distanz_{beobachtungenID}")] = float(niv_strecke)
|
substitutionen[sp.Symbol(f"niv_distanz_{beobachtungenID}")] = float(niv_strecke)
|
||||||
|
|
||||||
#Qll_numerisch = Qll_Matrix_Symbolisch.xreplace(substitutionen)
|
# Substituieren mit der Methode lambdify
|
||||||
if (self.func_Qll_numerisch is None) or (set(self.liste_symbole_lambdify) != set(substitutionen.keys())):
|
if (self.func_Qll_numerisch is None) or (set(self.liste_symbole_lambdify) != set(substitutionen.keys())):
|
||||||
self.liste_symbole_lambdify = sorted(substitutionen.keys(), key=lambda s: str(s))
|
self.liste_symbole_lambdify = sorted(substitutionen.keys(), key=lambda s: str(s))
|
||||||
self.func_Qll_numerisch = sp.lambdify(
|
self.func_Qll_numerisch = sp.lambdify(
|
||||||
@@ -323,14 +342,13 @@ class StochastischesModell:
|
|||||||
cse=True
|
cse=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Ausgeben der fehlenden Substitutionen
|
||||||
fehlend = sorted(list(Qll_Matrix_Symbolisch.free_symbols - set(substitutionen.keys())), key=lambda s: str(s))
|
fehlend = sorted(list(Qll_Matrix_Symbolisch.free_symbols - set(substitutionen.keys())), key=lambda s: str(s))
|
||||||
if fehlend:
|
if fehlend:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"Qll_numerisch: Fehlende Substitutionen ({len(fehlend)}): {[str(s) for s in fehlend[:80]]}")
|
f"Qll_numerisch: Fehlende Substitutionen ({len(fehlend)}): {[str(s) for s in fehlend[:80]]}")
|
||||||
|
|
||||||
#liste_werte = [substitutionen[s] for s in self.liste_symbole_lambdify]
|
# Eingrenzen der Substitution auf nicht 0 Zellen
|
||||||
#Qll_numerisch = np.asarray(self.func_Qll_numerisch(*liste_werte), dtype=float)
|
|
||||||
|
|
||||||
rows = int(Qll_Matrix_Symbolisch.rows)
|
rows = int(Qll_Matrix_Symbolisch.rows)
|
||||||
cols = int(Qll_Matrix_Symbolisch.cols)
|
cols = int(Qll_Matrix_Symbolisch.cols)
|
||||||
|
|
||||||
@@ -349,12 +367,6 @@ class StochastischesModell:
|
|||||||
|
|
||||||
eintrag_num = eintrag.xreplace(substitutionen)
|
eintrag_num = eintrag.xreplace(substitutionen)
|
||||||
|
|
||||||
if hasattr(eintrag_num, "free_symbols") and len(eintrag_num.free_symbols) > 0:
|
|
||||||
rest = sorted(list(eintrag_num.free_symbols), key=lambda s: str(s))
|
|
||||||
raise ValueError(
|
|
||||||
f"Qll_numerisch: Eintrag [{i},{j}] bleibt symbolisch. Rest-Symbole: {[str(s) for s in rest[:20]]}"
|
|
||||||
)
|
|
||||||
|
|
||||||
Qll_numerisch[i, j] = float(eintrag_num)
|
Qll_numerisch[i, j] = float(eintrag_num)
|
||||||
|
|
||||||
Export.matrix_to_csv(
|
Export.matrix_to_csv(
|
||||||
@@ -367,15 +379,28 @@ class StochastischesModell:
|
|||||||
|
|
||||||
return Qll_numerisch
|
return Qll_numerisch
|
||||||
|
|
||||||
def QAA_symbolisch(self, liste_beobachtungen_symbolisch: list, pfad_datenbank: str) -> np.Matrix:
|
def QAA_symbolisch(self, liste_beobachtungen_symbolisch: list) -> np.Matrix:
|
||||||
|
"""Erstellt die symbolische Varianz-Kovarianz-Matrix QAA der Anschlusspunkte (weiche Lagerung).
|
||||||
|
|
||||||
|
Es werden ausschließlich Beobachtungen berücksichtigt, deren Kennung mit "lA_" beginnt. Für jede Anschlussbedingung
|
||||||
|
wird eine (symbolische) Standardabweichung StabwAA_* angesetzt und mit der Varianzkomponente der Beobachtungsgruppe
|
||||||
|
"Anschlusspunkte" multipliziert.
|
||||||
|
|
||||||
|
Die symbolische Matrix wird als CSV-Datei in Zwischenergebnisse\\QAA_Symbolisch.csv exportiert.
|
||||||
|
|
||||||
|
:param liste_beobachtungen_symbolisch: Liste der symbolischen Beobachtungskennungen.
|
||||||
|
:type liste_beobachtungen_symbolisch: list
|
||||||
|
:return: Symbolische Varianz-Kovarianz-Matrix QAA.
|
||||||
|
:rtype: sp.Matrix
|
||||||
|
"""
|
||||||
liste_standardabweichungen_symbole = []
|
liste_standardabweichungen_symbole = []
|
||||||
liste_beobachtungen_symbolisch = [str(b) for b in liste_beobachtungen_symbolisch]
|
liste_beobachtungen_symbolisch = [str(b) for b in liste_beobachtungen_symbolisch]
|
||||||
liste_beobachtungen_symbolisch = [b for b in liste_beobachtungen_symbolisch if b.startswith("lA_")]
|
liste_beobachtungen_symbolisch = [b for b in liste_beobachtungen_symbolisch if b.startswith("lA_")]
|
||||||
Qll = sp.zeros(len(liste_beobachtungen_symbolisch), len(liste_beobachtungen_symbolisch))
|
Qll = sp.zeros(len(liste_beobachtungen_symbolisch), len(liste_beobachtungen_symbolisch))
|
||||||
|
|
||||||
db_zugriff = Datenbankzugriff(pfad_datenbank)
|
instrumente_id_anschlusspunkte = self.db_zugriff.get_instrument_liste("Anschlusspunkte")[0][0]
|
||||||
instrumente_id_anschlusspunkte = db_zugriff.get_instrument_liste("Anschlusspunkte")[0][0]
|
|
||||||
|
|
||||||
|
# Erstellen der Symbolischen Gleichungen für die QAA-Matrix
|
||||||
for i, beobachtung_symbolisch_i in enumerate(liste_beobachtungen_symbolisch):
|
for i, beobachtung_symbolisch_i in enumerate(liste_beobachtungen_symbolisch):
|
||||||
aufgeteilt_i = beobachtung_symbolisch_i.split("_")
|
aufgeteilt_i = beobachtung_symbolisch_i.split("_")
|
||||||
datumskoordinate = str(aufgeteilt_i[1])
|
datumskoordinate = str(aufgeteilt_i[1])
|
||||||
@@ -394,17 +419,32 @@ class StochastischesModell:
|
|||||||
Export.matrix_to_csv(r"Zwischenergebnisse\QAA_Symbolisch.csv", liste_beobachtungen_symbolisch, liste_beobachtungen_symbolisch, Qll, "Qll")
|
Export.matrix_to_csv(r"Zwischenergebnisse\QAA_Symbolisch.csv", liste_beobachtungen_symbolisch, liste_beobachtungen_symbolisch, Qll, "Qll")
|
||||||
return Qll
|
return Qll
|
||||||
|
|
||||||
def QAA_numerisch(self, pfad_datenbank: str, QAA_Matrix_Symbolisch: sp.Matrix, liste_beobachtungen_symbolisch: list) -> np.Matrix:
|
def QAA_numerisch(self, QAA_Matrix_Symbolisch: sp.Matrix, liste_beobachtungen_symbolisch: list) -> np.Matrix:
|
||||||
|
"""Erstellt eine numerische Matrix aus einer symbolischen QAA-Matrix.
|
||||||
|
|
||||||
|
Es werden die numerischen Standardabweichungen der Anschlussbedingungen sowie die Varianzkomponente der Gruppe
|
||||||
|
"Anschlusspunkte" aus der Datenbank abgefragt und substituiert. Zur Rechenzeitersparnis wird ein Lambdify-Cache geführt,
|
||||||
|
der bei Änderung der Symbolmenge neu aufgebaut.
|
||||||
|
|
||||||
|
Die numerische Matrix wird als CSV-Datei in Zwischenergebnisse\\QAA_Numerisch.csv exportiert.
|
||||||
|
|
||||||
|
:param QAA_Matrix_Symbolisch: Symbolische Varianz-Kovarianz-Matrix QAA.
|
||||||
|
:type QAA_Matrix_Symbolisch: sp.Matrix
|
||||||
|
:param liste_beobachtungen_symbolisch: Liste der symbolischen Beobachtungskennungen.
|
||||||
|
:type liste_beobachtungen_symbolisch: list
|
||||||
|
:return: Numerische Varianz-Kovarianz-Matrix QAA als Numpy-Array.
|
||||||
|
:rtype: np.Matrix
|
||||||
|
"""
|
||||||
|
# Symbolische Listen
|
||||||
liste_beobachtungen_symbolisch = [str(b).strip() for b in liste_beobachtungen_symbolisch]
|
liste_beobachtungen_symbolisch = [str(b).strip() for b in liste_beobachtungen_symbolisch]
|
||||||
liste_beobachtungen_symbolisch = [b for b in liste_beobachtungen_symbolisch if b.startswith("lA_")]
|
liste_beobachtungen_symbolisch = [b for b in liste_beobachtungen_symbolisch if b.startswith("lA_")]
|
||||||
|
|
||||||
db_zugriff = Datenbankzugriff(pfad_datenbank)
|
# Abfrage der numerischen Werte aus der Datenbank
|
||||||
dict_stabwAA_vorinfo = db_zugriff.get_stabw_AA_Netzpunkte()
|
dict_stabwAA_vorinfo = self.db_zugriff.get_stabw_AA_Netzpunkte()
|
||||||
|
liste_varianzkomponenten = self.db_zugriff.get_varianzkomponentenschaetzung()
|
||||||
liste_varianzkomponenten = db_zugriff.get_varianzkomponentenschaetzung()
|
|
||||||
|
|
||||||
|
# Zuordnen des Symbols zum numerischen Wert
|
||||||
substitutionen = {}
|
substitutionen = {}
|
||||||
|
|
||||||
for koordinate, stabwAA in dict_stabwAA_vorinfo.items():
|
for koordinate, stabwAA in dict_stabwAA_vorinfo.items():
|
||||||
substitutionen[sp.Symbol(str(koordinate).strip())] = float(stabwAA)
|
substitutionen[sp.Symbol(str(koordinate).strip())] = float(stabwAA)
|
||||||
|
|
||||||
@@ -412,11 +452,13 @@ class StochastischesModell:
|
|||||||
varianz_varianzkomponentenschaetzung) in liste_varianzkomponenten:
|
varianz_varianzkomponentenschaetzung) in liste_varianzkomponenten:
|
||||||
substitutionen[sp.Symbol(f"varkomp_{instrumenteID}_Anschlusspunkte")] = float(varianz_varianzkomponentenschaetzung)
|
substitutionen[sp.Symbol(f"varkomp_{instrumenteID}_Anschlusspunkte")] = float(varianz_varianzkomponentenschaetzung)
|
||||||
|
|
||||||
|
# Speichern der MAtrix in einer Instanzvariablen
|
||||||
if not hasattr(self, "func_QAA_numerisch"):
|
if not hasattr(self, "func_QAA_numerisch"):
|
||||||
self.func_QAA_numerisch = None
|
self.func_QAA_numerisch = None
|
||||||
if not hasattr(self, "liste_symbole_lambdify_QAA"):
|
if not hasattr(self, "liste_symbole_lambdify_QAA"):
|
||||||
self.liste_symbole_lambdify_QAA = []
|
self.liste_symbole_lambdify_QAA = []
|
||||||
|
|
||||||
|
# Substituieren
|
||||||
if (self.func_QAA_numerisch is None) or (set(self.liste_symbole_lambdify_QAA) != set(substitutionen.keys())):
|
if (self.func_QAA_numerisch is None) or (set(self.liste_symbole_lambdify_QAA) != set(substitutionen.keys())):
|
||||||
self.liste_symbole_lambdify_QAA = sorted(substitutionen.keys(), key=lambda s: str(s))
|
self.liste_symbole_lambdify_QAA = sorted(substitutionen.keys(), key=lambda s: str(s))
|
||||||
self.func_QAA_numerisch = sp.lambdify(
|
self.func_QAA_numerisch = sp.lambdify(
|
||||||
@@ -440,21 +482,66 @@ class StochastischesModell:
|
|||||||
return QAA_numerisch
|
return QAA_numerisch
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def berechne_P(Q_ll):
|
def berechne_P(Q_ll: np.ndarray) -> np.ndarray:
|
||||||
|
"""Berechnet die Gewichtsmatrix P aus einer Varianz-Kovarianz-Matrix Qll.
|
||||||
|
|
||||||
|
Die Gewichtsmatrix wird als Inverse von Qll gebildet: P = inv(Qll).
|
||||||
|
|
||||||
|
:param Q_ll: Varianz-Kovarianz-Matrix der Beobachtungen.
|
||||||
|
:type Q_ll: np.ndarray
|
||||||
|
:return: Gewichtsmatrix P.
|
||||||
|
:rtype: np.ndarray
|
||||||
|
"""
|
||||||
P = np.linalg.inv(Q_ll)
|
P = np.linalg.inv(Q_ll)
|
||||||
return P
|
return P
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def berechne_Q_xx(N):
|
def berechne_Q_xx(N: np.ndarray) -> np.ndarray:
|
||||||
|
"""Berechnet die Kofaktormatrix der Unbekannten Qxx aus der Normalgleichungsmatrix N.
|
||||||
|
|
||||||
|
Die Kofaktormatrix wird als Inverse der Normalgleichungsmatrix gebildet: Qxx = inv(N).
|
||||||
|
Vor der Inversion wird geprüft, ob N quadratisch ist.
|
||||||
|
|
||||||
|
:param N: Normalgleichungsmatrix.
|
||||||
|
:type N: np.ndarray
|
||||||
|
:return: Kofaktormatrix der Unbekannten Qxx.
|
||||||
|
:rtype: np.ndarray
|
||||||
|
:raises ValueError: Falls N nicht quadratisch ist.
|
||||||
|
"""
|
||||||
if N.shape[0] != N.shape[1]:
|
if N.shape[0] != N.shape[1]:
|
||||||
raise ValueError("N muss eine quadratische Matrix sein")
|
raise ValueError("N muss eine quadratische Matrix sein")
|
||||||
Qxx = np.linalg.inv(N)
|
Qxx = np.linalg.inv(N)
|
||||||
return Qxx
|
return Qxx
|
||||||
|
|
||||||
def berechne_Q_ll_dach(A, Q_xx):
|
@staticmethod
|
||||||
|
def berechne_Q_ll_dach(A: np.ndarray, Q_xx: np.ndarray) -> np.ndarray:
|
||||||
|
"""Berechnet die (geschätzte) Varianz-Kovarianz-Matrix der Beobachtungen Qll_dach.
|
||||||
|
|
||||||
|
Die Matrix wird gemäß Qll_dach = A @ Qxx @ A.T gebildet.
|
||||||
|
|
||||||
|
:param A: Jacobi-Matrix.
|
||||||
|
:type A: np.ndarray
|
||||||
|
:param Q_xx: Kofaktormatrix der Unbekannten.
|
||||||
|
:type Q_xx: np.ndarray
|
||||||
|
:return: Geschätzte Varianz-Kovarianz-Matrix der Beobachtungen Qll_dach.
|
||||||
|
:rtype: np.ndarray
|
||||||
|
"""
|
||||||
Q_ll_dach = A @ Q_xx @ A.T
|
Q_ll_dach = A @ Q_xx @ A.T
|
||||||
return Q_ll_dach
|
return Q_ll_dach
|
||||||
|
|
||||||
def berechne_Qvv(Q_ll, Q_ll_dach):
|
@staticmethod
|
||||||
|
def berechne_Qvv(Q_ll: np.ndarray, Q_ll_dach: np.ndarray) -> np.ndarray:
|
||||||
|
"""Berechnet die Varianz-Kovarianz-Matrix der Residuen Qvv.
|
||||||
|
|
||||||
|
Die Residuenkovarianz wird als Differenz aus Beobachtungs-Kovarianz und dem durch das Modell erklärten Anteil gebildet:
|
||||||
|
Qvv = Qll - Qll_dach.
|
||||||
|
|
||||||
|
:param Q_ll: Varianz-Kovarianz-Matrix der Beobachtungen.
|
||||||
|
:type Q_ll: np.ndarray
|
||||||
|
:param Q_ll_dach: Geschätzte Varianz-Kovarianz-Matrix der Beobachtungen.
|
||||||
|
:type Q_ll_dach: np.ndarray
|
||||||
|
:return: Varianz-Kovarianz-Matrix der Residuen Qvv.
|
||||||
|
:rtype: np.ndarray
|
||||||
|
"""
|
||||||
Q_vv = Q_ll - Q_ll_dach
|
Q_vv = Q_ll - Q_ll_dach
|
||||||
return Q_vv
|
return Q_vv
|
||||||
Reference in New Issue
Block a user