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))