def n_EA_E_and_p_AB_E2n_EB_E(n_EA_E, p_AB_E, z_EA=0, a=6378137, f=1.0 / 298.257223563, R_Ee=None): """ Returns position B from position A and delta vector decomposed in E. Parameters ---------- n_EA_E: 3 x k array n-vector(s) [no unit] of position A, decomposed in E. p_AB_E: 3 x m array Cartesian position vector(s) [m] from A to B, decomposed in E. z_EA: 1 x n array Depth(s) [m] of system A, relative to the ellipsoid. (z_EA = -height) a: real scalar, default WGS-84 ellipsoid. Semi-major axis of the Earth ellipsoid given in [m]. f: real scalar, default WGS-84 ellipsoid. Flattening [no unit] of the Earth ellipsoid. If f==0 then spherical Earth with radius a is used in stead of WGS-84. R_Ee : 3 x 3 array rotation matrix defining the axes of the coordinate frame E. Returns ------- n_EB_E: 3 x max(k,m,n) array n-vector(s) [no unit] of position B, decomposed in E. z_EB: 1 x max(k,m,n) array Depth(s) [m] of system B, relative to the ellipsoid. (z_EB = -height) Notes ----- The n-vector for position A (`n_EA_E`) and the delta vector from position A to position B decomposed in E (`p_AB_E`) are given. The output is the n-vector of position B (`n_EB_E`) and depth of B (`z_EB`). The calculation is excact, taking the ellipsity of the Earth into account. It is also non-singular as both n-vector and p-vector are non-singular (except for the center of the Earth). The default ellipsoid model used is WGS-84, but other ellipsoids/spheres might be specified. The shape of the output `n_EB_E` and `z_EB` is the broadcasted shapes of `n_EA_E`, `p_AB_E` and `z_EA`. Examples -------- {super} See also -------- n_EA_E_and_n_EB_E2p_AB_E, p_EB_E2n_EB_E, n_EB_E2p_EB_E """ if R_Ee is None: R_Ee = E_rotation() n_EA_E, p_AB_E = np.atleast_2d(n_EA_E, p_AB_E) # Function 2. in Section 5.4 in Gade (2010): p_EA_E = n_EB_E2p_EB_E(n_EA_E, z_EA, a, f, R_Ee) p_EB_E = p_EA_E + p_AB_E n_EB_E, z_EB = p_EB_E2n_EB_E(p_EB_E, a, f, R_Ee) return n_EB_E, z_EB
def lat_lon2n_E(latitude, longitude, R_Ee=None): """ Converts latitude and longitude to n-vector. Parameters ---------- latitude, longitude: real scalars or vectors of length n. Geodetic latitude and longitude given in [rad] R_Ee : 3 x 3 array rotation matrix defining the axes of the coordinate frame E. Returns ------- n_E: 3 x n array n-vector(s) [no unit] decomposed in E. Examples -------- >>> import nvector as nv >>> pi = 3.141592653589793 Scalar call >>> nv.allclose(nv.lat_lon2n_E(0, 0), [[1.], ... [0.], ... [0.]]) True Vectorized call >>> nv.allclose(nv.lat_lon2n_E([0., 0.], [0., pi/2]), [[1., 0.], ... [0., 1.], ... [0., 0.]]) True Broadcasting call >>> nv.allclose(nv.lat_lon2n_E(0., [0, pi/2]), [[1., 0.], ... [0., 1.], ... [0., 0.]]) True See also -------- n_E2lat_lon """ if R_Ee is None: R_Ee = E_rotation() # Equation (3) from Gade (2010): n-vector decomposed in E with axes='e' n_e = np.vstack((sin(latitude) * np.ones_like(longitude), cos(latitude) * sin(longitude), -cos(latitude) * cos(longitude))) # n_E = dot(R_Ee.T, n_e) n_E = np.matmul(R_Ee.T, n_e) # n-vector decomposed in E with axes 'E' return n_E
def n_EA_E_distance_and_azimuth2n_EB_E(n_EA_E, distance_rad, azimuth, R_Ee=None): """ Returns position B from azimuth and distance from position A Parameters ---------- n_EA_E: 3 x k array n-vector(s) [no unit] of position A decomposed in E. distance_rad: m array great circle distance [rad] from position A to B azimuth: n array Angle [rad] the line makes with a meridian, taken clockwise from north. Returns ------- n_EB_E: 3 x max(k,m,n) array n-vector(s) [no unit] of position B decomposed in E. Notes ----- The result for spherical Earth is returned. The shape of the output `n_EB_E` is the broadcasted shapes of `n_EA_E`, `distance_rad` and `azimuth. Examples -------- {super} See also -------- n_EA_E_and_n_EB_E2azimuth, great_circle_distance_rad """ if R_Ee is None: R_Ee = E_rotation() n_EA_E, distance_rad, azimuth = np.atleast_1d(n_EA_E, distance_rad, azimuth) # Step1: Find unit vectors for north and east: k_east_E = unit(cross(dot(R_Ee.T, [[1], [0], [0]]), n_EA_E, axis=0)) k_north_E = cross(n_EA_E, k_east_E, axis=0) # Step2: Find the initial direction vector d_E: d_E = k_north_E * cos(azimuth) + k_east_E * sin(azimuth) # Step3: Find n_EB_E: n_EB_E = n_EA_E * cos(distance_rad) + d_E * sin(distance_rad) return n_EB_E
def great_circle_distance_rad(n_EA_E, n_EB_E, R_Ee=None): """ Returns great circle distance in radians between positions A and B on a sphere Parameters ---------- n_EA_E, n_EB_E: 3 x k and 3 x m arrays n-vector(s) [no unit] of position A and B, decomposed in E. Returns ------- distance_rad : array of length max(k, m) Great circle distance(s) in radians Notes ----- The result for spherical Earth is returned. The shape of the output `distance_rad` is the broadcasted shapes of `n_EA_E`and `n_EB_E`. Formulae is given by equation (16) in Gade (2010) and is well conditioned for all angles. See also: https://en.wikipedia.org/wiki/Great-circle_distance. See also -------- great_circle_distance """ if R_Ee is None: R_Ee = E_rotation() n_EA_E, n_EB_E = np.atleast_2d(n_EA_E, n_EB_E) sin_theta = norm(np.cross(n_EA_E, n_EB_E, axis=0), axis=0) cos_theta = np.sum(n_EA_E * n_EB_E, axis=0) # Alternatively: # sin_phi = norm(n_EA_E-n_EB_E, axis=0)/2 # phi = theta/2 # cos_phi = norm(n_EA_E+n_EB_E, axis=0)/2 # theta = 2 * np.arctan2(sin_phi, cos_phi) # ill conditioned for small angles: # distance_rad_version1 = arccos(dot(n_EA_E,n_EB_E)) # ill-conditioned for angles near pi/2 (and not valid above pi/2) # distance_rad_version2 = arcsin(norm(cross(n_EA_E,n_EB_E))) return np.arctan2(sin_theta, cos_theta)
def n_EA_E_and_n_EB_E2azimuth(n_EA_E, n_EB_E, a=6378137, f=1.0 / 298.257223563, R_Ee=None): """ Returns azimuth from A to B, relative to North: Parameters ---------- n_EA_E, n_EB_E: 3 x m and 3 x n arrays n-vector(s) [no unit] of position A and B, respectively, decomposed in E. a: real scalar, default WGS-84 ellipsoid. Semi-major axis of the Earth ellipsoid given in [m]. f: real scalar, default WGS-84 ellipsoid. Flattening [no unit] of the Earth ellipsoid. If f==0 then spherical Earth with radius a is used in stead of WGS-84. R_Ee : 3 x 3 array rotation matrix defining the axes of the coordinate frame E. Returns ------- azimuth: max(m, n) array Angle [rad] the line makes with a meridian, taken clockwise from north. Notes ----- The shape of the output `azimuth` is the broadcasted shapes of `n_EA_E` and `n_EB_E`. See also -------- great_circle_distance_rad, n_EA_E_distance_and_azimuth2n_EB_E, course_over_ground """ if R_Ee is None: R_Ee = E_rotation() # Find p_AB_N (delta decomposed in N). p_AB_N = n_EA_E_and_n_EB_E2p_AB_N(n_EA_E, n_EB_E, z_EA=0, z_EB=0, a=a, f=f, R_Ee=R_Ee) # Find the direction (azimuth) to B, relative to north: return arctan2(p_AB_N[1], p_AB_N[0])
def p_EB_E2n_EB_E(p_EB_E, a=6378137, f=1.0 / 298.257223563, R_Ee=None): """ Converts Cartesian position vector in meters to n-vector. Parameters ---------- p_EB_E: 3 x n array Cartesian position vector(s) [m] from E to B, decomposed in E. a: real scalar, default WGS-84 ellipsoid. Semi-major axis of the Earth ellipsoid given in [m]. f: real scalar, default WGS-84 ellipsoid. Flattening [no unit] of the Earth ellipsoid. If f==0 then spherical Earth with radius a is used in stead of WGS-84. R_Ee : 3 x 3 array rotation matrix defining the axes of the coordinate frame E. Returns ------- n_EB_E: 3 x n array n-vector(s) [no unit] of position B, decomposed in E. depth: 1 x n array Depth(s) [m] of system B, relative to the ellipsoid (depth = -height) Notes ----- The position of B (typically body) relative to E (typically Earth) is given into this function as cartesian position vector `p_EB_E`, in meters. ("ECEF-vector"). The function converts to n-vector, `n_EB_E` and its `depth`. The calculation is excact, taking the ellipsity of the Earth into account. It is also non-singular as both n-vector and p-vector are non-singular (except for the center of the Earth). The default ellipsoid model used is WGS-84, but other ellipsoids/spheres might be specified. Examples -------- {super} See also -------- n_EB_E2p_EB_E, n_EA_E_and_p_AB_E2n_EB_E, n_EA_E_and_n_EB_E2p_AB_E """ if R_Ee is None: # R_Ee selects correct E-axes, see E_rotation for details R_Ee = E_rotation() # Make sure to rotate the coordinates so that: # x -> north pole and yz-plane coincides with the equatorial # plane before using equation 23! p_EB_e = np.matmul(R_Ee, p_EB_E) # The following code implements equation (23) from Gade (2010): x_scale, yz_scale, depth = _equation23(a, f, p_EB_e) n_EB_e_x = x_scale * p_EB_e[0, :] n_EB_e_y = yz_scale * p_EB_e[1, :] n_EB_e_z = yz_scale * p_EB_e[2, :] n_EB_e = np.vstack((n_EB_e_x, n_EB_e_y, n_EB_e_z)) # Rotate back to the original coordinate system. n_EB_E = unit(np.matmul(R_Ee.T, n_EB_e)) # Ensure unit length return n_EB_E, depth
def n_EB_E2p_EB_E(n_EB_E, depth=0, a=6378137, f=1.0 / 298.257223563, R_Ee=None): """ Converts n-vector to Cartesian position vector in meters. Parameters ---------- n_EB_E: 3 x m array n-vector(s) [no unit] of position B, decomposed in E. depth: 1 x n array Depth(s) [m] of system B, relative to the ellipsoid (depth = -height) a: real scalar, default WGS-84 ellipsoid. Semi-major axis of the Earth ellipsoid given in [m]. f: real scalar, default WGS-84 ellipsoid. Flattening [no unit] of the Earth ellipsoid. If f==0 then spherical Earth with radius a is used in stead of WGS-84. R_Ee : 3 x 3 array rotation matrix defining the axes of the coordinate frame E. Returns ------- p_EB_E: 3 x max(m,n) array Cartesian position vector(s) [m] from E to B, decomposed in E. Notes ----- The position of B (typically body) relative to E (typically Earth) is given into this function as n-vector, `n_EB_E`. The function converts to cartesian position vector ("ECEF-vector"), `p_EB_E`, in meters. The calculation is exact, taking the ellipsity of the Earth into account. It is also non-singular as both n-vector and p-vector are non-singular (except for the center of the Earth). The default ellipsoid model used is WGS-84, but other ellipsoids/spheres might be specified. The shape of the output `p_EB_E` is the broadcasted shapes of `n_EB_E` and `depth`. Examples -------- {super} See also -------- p_EB_E2n_EB_E, n_EA_E_and_p_AB_E2n_EB_E, n_EA_E_and_n_EB_E2p_AB_E """ if R_Ee is None: R_Ee = E_rotation() # n_EB_E = np.atleast_2d(n_EB_E) # _nvector_check_length(n_EB_E) # n_EB_e = unit(np.matmul(R_Ee, n_EB_E)) # Make sure to rotate the coordinates so that: # x -> north pole and yz-plane coincides with the equatorial # plane before using equation 22! n_EB_e = change_axes_to_E(n_EB_E, R_Ee) b = polar_radius(a, f) # semi-minor axis # The following code implements equation (22) in Gade (2010): scale = np.vstack((1, (1 - f), (1 - f))) denominator = norm(n_EB_e / scale, axis=0, keepdims=True) # We first calculate the position at the origin of coordinate system L, # which has the same n-vector as B (n_EL_e = n_EB_e), # but lies at the surface of the Earth (z_EL = 0). p_EL_e = b / denominator * n_EB_e / scale**2 # rotate back to the original coordinate system p_EB_E = np.matmul(R_Ee.T, p_EL_e - n_EB_e * depth) return p_EB_E