From 34460382be7ce343642c87b3b5c4aa5024b4db61 Mon Sep 17 00:00:00 2001 From: Patrick Peglar Date: Sun, 2 Feb 2025 23:43:55 +0000 Subject: [PATCH] Add tests for vectors in Transform.from_points. --- tests/bridge/test_from_points.py | 137 +++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) diff --git a/tests/bridge/test_from_points.py b/tests/bridge/test_from_points.py index 80e9c519..605e2e32 100644 --- a/tests/bridge/test_from_points.py +++ b/tests/bridge/test_from_points.py @@ -7,6 +7,7 @@ from __future__ import annotations +import cartopy.crs as ccrs import numpy as np from pyproj import CRS, Transformer import pytest @@ -76,3 +77,139 @@ def test_scalar(): np.testing.assert_array_equal(Transform.from_points(0, [90]).points, expected) np.testing.assert_array_equal(Transform.from_points([0], 90).points, expected) np.testing.assert_array_equal(Transform.from_points([0], [90]).points, expected) + + +class TestFromPointsVectors: + """Check calculations on attached vectors. + + Mostly testing against previously-obtained results, but we do have examples proving + practically correct behaviours : see src/geovista/examples/vector_data + """ + + @pytest.fixture(autouse=True) + def setup(self): + """Set useful test constants.""" + self.lats = np.array([10.0, 80.0, -70.0, 35.0]) + self.lons = np.array([5.0, 140.0, -200.0, 73]) + self.easting = 10.0e6 * np.array([1.0, 8.0, 13.0, 25.0]) + self.northing = 10.0e6 * np.array([-2.0, 5.0, 17.0, -14.0]) + self.u = np.array([10.0, 20, 15.0, 24.0]) + self.v = np.array([20.0, -17, 0.0, 33.0]) + self.w = np.array([15.0, 25, 4.0, -55.0]) + self.crs_truelatlon = WGS84 + self.crs_rotatedlatlon = ccrs.RotatedGeodetic(130.0, 65.0).to_wkt() + self.crs_northpolar = ccrs.NorthPolarStereo().to_wkt() + + def test_basic(self): + """Check basic operation on true-latlon uvw vectors.""" + mesh = Transform.from_points( + xs=self.lons, ys=self.lats, vectors=(self.u, self.v, self.w) + ) + result = mesh["vectors"].T + expected = np.array( + [ + [10.385, -29.006, -6.416, -41.658], + [10.947, -1.769, -13.627, -54.169], + [22.301, 21.668, -3.759, -4.515], + ] + ) + assert np.allclose(result, expected, atol=0.001) + + def test_basic__2d_uv(self): + """Check operation with only 2 input component arrays (no W).""" + mesh = Transform.from_points( + xs=self.lons, ys=self.lats, vectors=(self.u, self.v) + ) + result = mesh["vectors"].T + expected = np.array( + [ + [-4.331, -25.681, -5.13, -28.485], + [9.659, -4.56, -14.095, -11.084], + [19.696, -2.952, 0.0, 27.032], + ] + ) + assert np.allclose(result, expected, atol=0.001) + + def test_crs(self): + """Check operation with alternate latlon-type CRS.""" + mesh = Transform.from_points( + xs=self.lons, + ys=self.lats, + vectors=(self.u, self.v), + crs=self.crs_rotatedlatlon, + ) + result = mesh["vectors"].T + expected = np.array( + [ + [-0.474, -17.651, -13.786, -32.429], + [15.592, 13.943, -5.499, 21.403], + [16.02, -13.529, -2.168, 12.461], + ] + ) + assert np.allclose(result, expected, atol=0.001) + + def test__nonlatlon_crs__fail(self): + """Check error when attempted with non-latlon CRS.""" + msg = "Cannot determine wind directions : Target CRS type is not supported.*" + with pytest.raises(ValueError, match=msg): + _ = Transform.from_points( + xs=self.easting, + ys=self.northing, + vectors=(self.u, self.v), + crs=self.crs_northpolar, + ) + + def test__nonlatloncrs__truelatlon__vectorscrs(self): + """Check ok with non-latlon CRS for points but latlon vectors.""" + mesh = Transform.from_points( + xs=self.easting, + ys=self.northing, + vectors=(self.u, self.v), + crs=self.crs_northpolar, + vectors_crs=self.crs_truelatlon, + ) + result = mesh["vectors"].T + expected = np.array( + [ + [4.722, -8.267, -9.112, -4.879], + [13.541, -24.508, -11.915, 40.408], + [17.156, -4.472, 0.0, 2.903], + ] + ) + assert np.allclose(result, expected, atol=0.001) + + def test__latlon__vectorscrs(self): + """Check operation with different specified CRS for vectors only.""" + mesh = Transform.from_points( + xs=self.lons, + ys=self.lats, + vectors=(self.u, self.v), + vectors_crs=self.crs_rotatedlatlon, + ) + result = mesh["vectors"].T + expected = np.array( + [ + [-4.066, 25.821, -8.955, -38.46], + [16.031, -2.79, -11.93, 1.87], + [15.049, 3.804, 1.578, 13.504], + ] + ) + assert np.allclose(result, expected, atol=0.001) + + def test__vectors_array_name(self): + """Check operation with alternate vectors array name.""" + mesh = Transform.from_points( + xs=self.lons, + ys=self.lats, + vectors=(self.u, self.v, self.w), + vectors_array_name="squiggle", + ) + result = mesh["squiggle"].T + expected = np.array( + [ + [10.385, -29.006, -6.416, -41.658], + [10.947, -1.769, -13.627, -54.169], + [22.301, 21.668, -3.759, -4.515], + ] + ) + assert np.allclose(result, expected, atol=0.001)