From e5b0dec161c98a6b3d8d46759220e8ab09263116 Mon Sep 17 00:00:00 2001 From: Amy Stamile <astamile@usgs.gov> Date: Fri, 19 Apr 2024 12:44:56 -0700 Subject: [PATCH] Added docs and classes --- knoten/utils.py | 155 +++++++++++++++++++++++++++++++++++++++----- tests/test_utils.py | 92 +++++++++++++------------- 2 files changed, 187 insertions(+), 60 deletions(-) diff --git a/knoten/utils.py b/knoten/utils.py index 5920e38..0a69fd6 100644 --- a/knoten/utils.py +++ b/knoten/utils.py @@ -1,11 +1,39 @@ import pyproj import numpy as np -from collections import namedtuple +# from collections import namedtuple +from typing import NamedTuple -def sep_angle(a_pt, b_pt, c_pt): - return sep_angle(a_pt - b_pt, c_pt - b_pt) +class Point(NamedTuple): + x: float + y: float + z: float + +class LatLon(NamedTuple): + lat: float + lon: float + +class Sphere(NamedTuple): + lat: float + lon: float + radius: float + +class Matrix(NamedTuple): + vec_a: Point + vec_b: Point + vec_c: Point def sep_angle(a_vec, b_vec): + """ + Parameters + ---------- + a_vec : Point object (x, y, z) + + b_vec : Point object (x, y, z) + + Returns + ------- + : float + """ dot_prod = a_vec.x * b_vec.x + a_vec.y * b_vec.y + a_vec.z * b_vec.z dot_prod /= magnitude(a_vec) * magnitude(b_vec) @@ -15,17 +43,43 @@ def sep_angle(a_vec, b_vec): return np.arccos(dot_prod) def magnitude(vec): + """ + Parameters + ---------- + vec : Point object (x, y, z) + + Returns + ------- + : float + """ return np.sqrt(vec.x * vec.x + vec.y * vec.y + vec.z * vec.z) def distance(start, stop): - Point = namedtuple("Point", 'x, y, z') + """ + Parameters + ---------- + start : Point object (x, y, z) + + stop : Point object (x, y, z) + + Returns + ------- + : float + """ diff = Point(stop.x - start.x, stop.y - start.y, stop.z - start.z) return magnitude(diff) def radiansToDegrees(radian_lat_lon): - LatLon = namedtuple("LatLon", 'lat lon') + """ + Parameters + ---------- + radian_lat_lon : LatLon object (lat, lon) in radians + Returns + ------- + : LatLon object (lat, lon) in degrees + """ degree_lon = radian_lat_lon.lon if (degree_lon < 0): degree_lon += 2 * np.pi @@ -35,8 +89,15 @@ def radiansToDegrees(radian_lat_lon): return LatLon(degreeLat, degree_lon) def spherical_to_rect(spherical): - Point = namedtuple("Point", 'x, y, z') + """ + Parameters + ---------- + spherical : Sphere object (lat, lon, radius) + Returns + ------- + : Point object (x, y, z) + """ x = spherical.radius * np.cos(spherical.lat) * np.cos(spherical.lon) y = spherical.radius * np.cos(spherical.lat) * np.sin(spherical.lon) z = spherical.radius * np.sin(spherical.lat) @@ -44,8 +105,15 @@ def spherical_to_rect(spherical): return Point(x, y, z) def rect_to_spherical(rectangular): - Sphere = namedtuple("Sphere", 'lat, lon, radius') + """ + Parameters + ---------- + rectangular : Point object (x, y, z) + Returns + ------- + : Sphere object (lat, lon, radius) + """ rad = magnitude(rectangular) if (rad < 1e-15): return Sphere(0.0, 0.0, 0.0) @@ -57,8 +125,17 @@ def rect_to_spherical(rectangular): ) def ground_azimuth(ground_pt, sub_pt): - LatLon = namedtuple("LatLon", 'lat lon') + """ + Parameters + ---------- + ground_pt : LatLon object (lat, lon) + sub_pt : LatLon object (lat, lon) + + Returns + ------- + : float + """ if (ground_pt.lat >= 0.0): a = (90.0 - sub_pt.lat) * np.pi / 180.0 b = (90.0 - ground_pt.lat) * np.pi / 180.0 @@ -128,19 +205,47 @@ def ground_azimuth(ground_pt, sub_pt): return azimuth def crossProduct(a_vec, b_vec): - Point = namedtuple("Point", 'x, y, z') + """ + Parameters + ---------- + a_vec : Point object (x, y, z) + + b_vec : Point object (x, y, z) + Returns + ------- + : Point object (x, y, z) + """ x = a_vec.y * b_vec.z - a_vec.z * b_vec.y y = a_vec.z * b_vec.x - a_vec.x * b_vec.z z = a_vec.x * b_vec.y - a_vec.y * b_vec.x return Point(x, y, z) - def unit_vector(vec): + """ + Parameters + ---------- + vec : Point object (x, y, z) + + Returns + ------- + : Point object (x, y, z) + """ mag = magnitude(vec) return vec / mag def perpendicular_vector(a_vec, b_vec): + """ + Parameters + ---------- + a_vec : Point object (x, y, z) + + b_vec : Point object (x, y, z) + + Returns + ------- + : Point object (x, y, z) + """ if (magnitude(a_vec) == 0): return b_vec @@ -157,16 +262,34 @@ def perpendicular_vector(a_vec, b_vec): return q def scale_vector(vec, scalar): - Point = namedtuple("Point", 'x, y, z') + """ + Parameters + ---------- + vec : Point object (x, y, z) + scalar : float + + Returns + ------- + : Point object (x, y, z) + """ return Point(vec.x * scalar, vec.y * scalar, vec.z * scalar) -def matrixVecProduct(mat, vec): - Point = namedtuple("Point", 'x, y, z') +def matrix_vec_product(mat, vec): + """ + Parameters + ---------- + mat : Matrix object (vec_a, vec_b, vec_c) + + vec : Point object (x, y, z) - x = mat.a.x * vec.x + mat.a.y * vec.y + mat.a.z * vec.z - y = mat.b.x * vec.x + mat.b.y * vec.y + mat.b.z * vec.z - z = mat.c.x * vec.x + mat.c.y * vec.y + mat.c.z * vec.z + Returns + ------- + : Point object (x, y, z) + """ + x = mat.vec_a.x * vec.x + mat.vec_a.y * vec.y + mat.vec_a.z * vec.z + y = mat.vec_b.x * vec.x + mat.vec_b.y * vec.y + mat.vec_b.z * vec.z + z = mat.vec_c.x * vec.x + mat.vec_c.y * vec.y + mat.vec_c.z * vec.z return Point(x, y, z) diff --git a/tests/test_utils.py b/tests/test_utils.py index 8547c0e..d68ab18 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,100 +1,104 @@ import numpy as np from knoten import utils -from collections import namedtuple - -Point = namedtuple("Point", 'x, y, z') -Sphere = namedtuple("Sphere", 'lat, lon, radius') def test_sep_angle_right(): - pt1 = Point(1, 0, 0) - pt2 = Point(0, 1, 0) + pt1 = utils.Point(1, 0, 0) + pt2 = utils.Point(0, 1, 0) np.testing.assert_array_equal(utils.sep_angle(pt1, pt2), np.pi / 2.0) def test_sep_angle_acute(): - pt1 = Point(1, 0, 0) - pt2 = Point(1, 1, 0) + pt1 = utils.Point(1, 0, 0) + pt2 = utils.Point(1, 1, 0) np.testing.assert_allclose(utils.sep_angle(pt1, pt2), np.pi / 4.0, atol=1e-12) def test_sep_angle_obtuse(): - pt1 = Point(1, 0, 0) - pt2 = Point(-1, 1, 0) + pt1 = utils.Point(1, 0, 0) + pt2 = utils.Point(-1, 1, 0) np.testing.assert_array_equal(utils.sep_angle(pt1, pt2), 3.0 * np.pi / 4.0) def test_sep_angle_normalization(): - pt1 = Point(1, 0, 0) - pt2 = Point(1, 1, 0) - pt3 = Point(100, 0, 0) - pt4 = Point(100, 100, 0) + pt1 = utils.Point(1, 0, 0) + pt2 = utils.Point(1, 1, 0) + pt3 = utils.Point(100, 0, 0) + pt4 = utils.Point(100, 100, 0) np.testing.assert_array_equal(utils.sep_angle(pt1, pt2), utils.sep_angle(pt3, pt4)) def test_magnitude_unit(): - assert utils.magnitude(Point(1.0, 0.0, 0.0)) == 1.0 - assert utils.magnitude(Point(0.0, 1.0, 0.0)) == 1.0 - assert utils.magnitude(Point(0.0, 0.0, 1.0)) == 1.0 + assert utils.magnitude(utils.Point(1.0, 0.0, 0.0)) == 1.0 + assert utils.magnitude(utils.Point(0.0, 1.0, 0.0)) == 1.0 + assert utils.magnitude(utils.Point(0.0, 0.0, 1.0)) == 1.0 def test_magnitude_nonunit(): - assert utils.magnitude(Point(0.0, 0.0, 0.0)) == 0.0 - assert utils.magnitude(Point(2.0, 1.0, 4.0)) == np.sqrt(21.0) - np.testing.assert_allclose(utils.magnitude(Point(0.2, 0.1, 0.4)), np.sqrt(0.21), atol=1e-12) + assert utils.magnitude(utils.Point(0.0, 0.0, 0.0)) == 0.0 + assert utils.magnitude(utils.Point(2.0, 1.0, 4.0)) == np.sqrt(21.0) + np.testing.assert_allclose(utils.magnitude(utils.Point(0.2, 0.1, 0.4)), np.sqrt(0.21), atol=1e-12) def test_distance(): - assert utils.distance(Point(1.0, 2.0, 3.0), Point(6.0, 5.0, 4.0)) == np.sqrt(35) + assert utils.distance(utils.Point(1.0, 2.0, 3.0), utils.Point(6.0, 5.0, 4.0)) == np.sqrt(35) def test_spherical_to_rect(): - result = utils.spherical_to_rect(Sphere(0.0, 0.0, 1000.0)) + result = utils.spherical_to_rect(utils.Sphere(0.0, 0.0, 1000.0)) np.testing.assert_allclose(result.x, 1000.0, atol=1e-12) np.testing.assert_allclose(result.y, 0.0, atol=1e-12) np.testing.assert_allclose(result.z, 0.0, atol=1e-12) - result = utils.spherical_to_rect(Sphere(0.0, np.pi, 1000.0)) + result = utils.spherical_to_rect(utils.Sphere(0.0, np.pi, 1000.0)) np.testing.assert_allclose( result.x, -1000.0, atol=1e-12) np.testing.assert_allclose( result.y, 0.0, atol=1e-12) np.testing.assert_allclose( result.z, 0.0, atol=1e-12) - result = utils.spherical_to_rect(Sphere(np.pi / 2.0, 0.0, 1000.0)) + result = utils.spherical_to_rect(utils.Sphere(np.pi / 2.0, 0.0, 1000.0)) np.testing.assert_allclose( result.x, 0.0, atol=1e-12) np.testing.assert_allclose( result.y, 0.0, atol=1e-12) np.testing.assert_allclose( result.z, 1000.0, atol=1e-12) - result = utils.spherical_to_rect(Sphere(np.pi / -2.0, 0.0, 1000.0)) + result = utils.spherical_to_rect(utils.Sphere(np.pi / -2.0, 0.0, 1000.0)) np.testing.assert_allclose( result.x, 0.0, atol=1e-12) np.testing.assert_allclose( result.y, 0.0, atol=1e-12) np.testing.assert_allclose( result.z, -1000.0, atol=1e-12) def test_rect_to_spherical(): - result = utils.rect_to_spherical(Point(1000.0, 0.0, 0.0)) - np.testing.assert_array_equal(result, Sphere(0.0, 0.0, 1000.0)) + result = utils.rect_to_spherical(utils.Point(1000.0, 0.0, 0.0)) + np.testing.assert_array_equal(result, utils.Sphere(0.0, 0.0, 1000.0)) - result = utils.rect_to_spherical(Point(-1000.0, 0.0, 0.0)) - np.testing.assert_array_equal(result, Sphere(0.0, np.pi, 1000.0)) + result = utils.rect_to_spherical(utils.Point(-1000.0, 0.0, 0.0)) + np.testing.assert_array_equal(result, utils.Sphere(0.0, np.pi, 1000.0)) - result = utils.rect_to_spherical(Point(0.0, 0.0, 1000.0)) - np.testing.assert_array_equal(result, Sphere(np.pi / 2.0, 0.0, 1000.0)) + result = utils.rect_to_spherical(utils.Point(0.0, 0.0, 1000.0)) + np.testing.assert_array_equal(result, utils.Sphere(np.pi / 2.0, 0.0, 1000.0)) - result = utils.rect_to_spherical(Point(0.0, 0.0, -1000.0)) - np.testing.assert_array_equal(result, Sphere(np.pi / -2.0, 0.0, 1000.0)) + result = utils.rect_to_spherical(utils.Point(0.0, 0.0, -1000.0)) + np.testing.assert_array_equal(result, utils.Sphere(np.pi / -2.0, 0.0, 1000.0)) def test_ground_azimuth(): - LatLon = namedtuple("LatLon", "lat lon") - - ground_pt = LatLon(0, -180) - subsolar_pt = LatLon(0, 90) + ground_pt = utils.LatLon(0, -180) + subsolar_pt = utils.LatLon(0, 90) np.testing.assert_array_equal(270.0, utils.ground_azimuth(ground_pt, subsolar_pt)) def test_perpendicular_vector(): - vec_a = Point(6.0, 6.0, 6.0) - vec_b = Point(2.0, 0.0, 0.0) - result = Point(0.0, 6.0, 6.0) + vec_a = utils.Point(6.0, 6.0, 6.0) + vec_b = utils.Point(2.0, 0.0, 0.0) + result = utils.Point(0.0, 6.0, 6.0) np.testing.assert_array_equal(utils.perpendicular_vector(vec_a, vec_b), result) def test_unit_vector(): - result = utils.unit_vector(Point(5.0, 12.0, 0.0)) + result = utils.unit_vector(utils.Point(5.0, 12.0, 0.0)) np.testing.assert_allclose(result[0], 0.384615, atol=1e-6) np.testing.assert_allclose(result[1], 0.923077, atol=1e-6) np.testing.assert_array_equal(result[2], 0.0) def test_scale_vector(): - vec = Point(1.0, 2.0, -3.0) + vec = utils.Point(1.0, 2.0, -3.0) scalar = 3.0 - result = Point(3.0, 6.0, -9.0) - np.testing.assert_array_equal(utils.scale_vector(vec, scalar), result) \ No newline at end of file + result = utils.Point(3.0, 6.0, -9.0) + np.testing.assert_array_equal(utils.scale_vector(vec, scalar), result) + +def test_matrix_vec_product(): + vec_a = utils.Point(0.0, 1.0, 0.0) + vec_b = utils.Point(-1.0, 0.0, 0.0) + vec_c = utils.Point(0.0, 0.0, 1.0) + mat = utils.Matrix(vec_a, vec_b, vec_c) + vec = utils.Point(1.0, 2.0, 3.0) + + result = utils.Point(2.0, -1.0, 3.0) + np.testing.assert_array_equal(result, utils.matrix_vec_product(mat, vec)) \ No newline at end of file -- GitLab