aboutsummaryrefslogtreecommitdiffziptar.gz
path: root/tests/test_geo.py
diff options
context:
space:
mode:
authorgodosa <godosa@godosa.eu>2026-10-06 23:52:03 +0200
committergodosa <godosa@godosa.eu>2026-10-06 23:52:03 +0200
commit346b1c5195bffc71ceaa9262453e3c189656400b (patch)
tree01ac0d31e2724cd6abcc689a5a228e2cbea2f6cf /tests/test_geo.py
downloadworldgen-346b1c5195bffc71ceaa9262453e3c189656400b.tar.gz
worldgen-346b1c5195bffc71ceaa9262453e3c189656400b.zip
worldgen: initial public history
Diffstat (limited to 'tests/test_geo.py')
-rw-r--r--tests/test_geo.py84
1 files changed, 84 insertions, 0 deletions
diff --git a/tests/test_geo.py b/tests/test_geo.py
new file mode 100644
index 0000000..d438299
--- /dev/null
+++ b/tests/test_geo.py
@@ -0,0 +1,84 @@
+import unittest
+from types import SimpleNamespace
+
+import numpy as np
+
+from mapgen import geo as GE
+from mapgen.plates import CONV
+from tests.helpers import small_grid
+
+
+def _area(ring):
+ a = np.asarray(ring, dtype=float)
+ return 0.5 * float(np.sum(a[:-1, 0] * a[1:, 1] - a[1:, 0] * a[:-1, 1]))
+
+
+class GeoTest(unittest.TestCase):
+ def test_split_antimeridian(self):
+ parts = GE.split_antimeridian([[178.0, 0.0], [179.5, 1.0], [-179.5, 1.0], [-178.0, 0.0]])
+ self.assertEqual(len(parts), 2)
+ self.assertEqual(parts[0][-1], [179.5, 1.0])
+
+ def test_river_split_at_antimeridian(self):
+ g = SimpleNamespace(n=4, lat=np.zeros(4), lon=np.array([170.0, 178.0, -178.0, -170.0]))
+ recv = np.array([1, 2, 3, 3])
+ feats = GE.river_lines(g, recv, np.array([True, True, True, False]), np.array([1, 1, 2, 0], np.int8),
+ np.array([1.0, 2.0, 3.0, 0.0]))
+ self.assertEqual(len(feats), 2)
+ for f in feats:
+ xs = [c[0] for c in f["geometry"]["coordinates"]]
+ self.assertLess(max(xs) - min(xs), 180)
+
+ def test_contour_square(self):
+ m = np.zeros((10, 20), bool)
+ m[3:7, 5:9] = True
+ rings = GE.contours(m)
+ self.assertEqual(len(rings), 1)
+ r = np.array(rings[0])
+ self.assertEqual(tuple(r[0]), tuple(r[-1]))
+ self.assertTrue(4 <= r[:, 0].min() and r[:, 0].max() <= 9 and 2 <= r[:, 1].min() and r[:, 1].max() <= 7)
+
+ def test_contour_ring_split(self):
+ m = np.zeros((10, 20), bool)
+ m[3:7, :3] = True
+ m[3:7, 17:] = True # one island wrapping the antimeridian
+ feats = GE.contour_features(m, "land")
+ self.assertEqual(len(feats), 1)
+ geom = feats[0]["geometry"]
+ self.assertEqual(geom["type"], "MultiPolygon") # fillable, split at ±180
+ for poly in geom["coordinates"]:
+ ring = poly[0]
+ self.assertEqual(ring[0], ring[-1])
+ xs = [c[0] for c in ring]
+ self.assertTrue(-180 <= min(xs) and max(xs) <= 180 and max(xs) - min(xs) < 180)
+ self.assertGreater(_area(ring), 0) # counter-clockwise exterior
+
+ def test_polygon_orientation_and_holes(self):
+ m = np.zeros((20, 40), bool)
+ m[4:16, 8:24] = True
+ m[8:12, 13:19] = False # a lake-shaped hole
+ feats = GE.contour_features(m, "land")
+ self.assertEqual(len(feats), 1)
+ geom = feats[0]["geometry"]
+ self.assertEqual(geom["type"], "Polygon")
+ self.assertEqual(len(geom["coordinates"]), 2)
+ self.assertGreater(_area(geom["coordinates"][0]), 0) # exterior CCW
+ self.assertLess(_area(geom["coordinates"][1]), 0) # hole CW
+
+ def test_river_features_have_single_order(self):
+ g = SimpleNamespace(n=6, lat=np.arange(6.0), lon=np.zeros(6))
+ recv = np.array([1, 2, 3, 4, 5, 5])
+ order = np.array([1, 1, 2, 2, 3, 0], np.int8)
+ feats = GE.river_lines(g, recv, np.array([True] * 5 + [False]), order, np.arange(6.0))
+ self.assertEqual([f["properties"]["order"] for f in feats], [1, 2, 3])
+ for f in feats:
+ self.assertGreaterEqual(len(f["geometry"]["coordinates"]), 2)
+
+ def test_plate_boundaries(self):
+ g = small_grid(1)
+ plate = (g.lon > 0).astype(np.int16)
+ bt = np.full(g.n, CONV, np.int8)
+ feats = GE.boundary_features(g, plate, bt)
+ self.assertEqual(len(feats), 1)
+ self.assertEqual(feats[0]["properties"]["type"], "convergent")
+ self.assertGreater(len(feats[0]["geometry"]["coordinates"]), 5)