import unittest import numpy as np from mapgen import noise as N from mapgen import sphere as S class SphereTest(unittest.TestCase): def test_roundtrip(self): lat = np.array([0.0, 45.0, -60.0, 89.0]) lon = np.array([0.0, 120.0, -170.0, 10.0]) la, lo = S.xyz_to_latlon(S.latlon_to_xyz(lat, lon)) np.testing.assert_allclose(la, lat, atol=1e-9) np.testing.assert_allclose(lo, lon, atol=1e-9) def test_east_north_at_pole(self): p = S.latlon_to_xyz(np.array([90.0, -90.0, 0.0]), np.array([0.0, 0.0, 0.0])) e, n = S.east_north(p) self.assertTrue(np.all(np.isfinite(e)) and np.all(np.isfinite(n))) np.testing.assert_allclose(np.linalg.norm(e, axis=1), 1.0) np.testing.assert_allclose(np.sum(e * p, axis=1), 0.0, atol=1e-12) # at the equator/prime meridian east = +y, north = +z np.testing.assert_allclose(e[2], [0, 1, 0], atol=1e-12) np.testing.assert_allclose(n[2], [0, 0, 1], atol=1e-12) def test_motion_velocity_matches_request(self): R = 12742.0 om = S.motion_to_omega(10.0, 20.0, 90.0, 5.0, R) # due east, 5 cm/yr p = S.latlon_to_xyz(np.array([10.0]), np.array([20.0])) v = S.velocity(p, om, R)[0] e, n = S.east_north(p) self.assertAlmostEqual(float(np.dot(v, e[0])), 0.05, places=6) self.assertAlmostEqual(float(np.dot(v, n[0])), 0.0, places=6) def test_zero_speed_is_zero_velocity(self): om = S.motion_to_omega(0.0, 0.0, 45.0, 0.0, 12742.0) self.assertTrue(np.allclose(om, 0.0)) def test_rotate_and_azimuth(self): p = S.latlon_to_xyz(np.array([0.0]), np.array([0.0])) e, n = S.east_north(p) r = S.rotate_about(p, e, np.pi / 2) # CCW seen from outside: east -> north np.testing.assert_allclose(r, n, atol=1e-12) q = S.latlon_to_xyz(np.array([0.0, 10.0]), np.array([10.0, 0.0])) np.testing.assert_allclose(S.azimuth_deg(p[0], q), [90.0, 0.0], atol=1e-9) def test_great_circle_point(self): p0 = S.latlon_to_xyz(np.array([0.0]), np.array([0.0]))[0] e, _ = S.east_north(p0[None]) R = 12742.0 q = S.great_circle_point(p0, e[0], np.pi * R / 2, R) # quarter turn east np.testing.assert_allclose(q, [0, 1, 0], atol=1e-12) class NoiseTest(unittest.TestCase): def test_deterministic_and_bounded(self): rng = np.random.default_rng(0) p = rng.normal(size=(5000, 3)) p /= np.linalg.norm(p, axis=1, keepdims=True) a = N.fbm(p, 42) b = N.fbm(p, 42) c = N.fbm(p, 43) np.testing.assert_array_equal(a, b) self.assertFalse(np.allclose(a, c)) self.assertTrue(np.all(np.abs(a) <= 1.0)) self.assertGreater(a.std(), 0.05) r = N.ridged(p, 1) self.assertTrue(np.all((r >= 0) & (r <= 1))) def test_continuity(self): p = np.array([[0.3, 0.4, 0.5]]) d = N.fbm(p + 1e-6, 5) - N.fbm(p, 5) self.assertLess(abs(d[0]), 1e-3) class CompiledNoiseTest(unittest.TestCase): def test_compiled_value_noise_matches_numpy(self): from mapgen import noise as N if N._noise_jit is None: self.skipTest("numba not installed") rng = np.random.default_rng(0) for scale in (1.0, 37.0, 1e4, 1e7): for seed in (0, 4242, 123456789, -5, 2 ** 40): p = rng.normal(0, scale, (2000, 3)) self.assertTrue(np.array_equal(N.value_noise(p, seed), N._value_noise(p, seed)), (scale, seed))