test_quadtree.py 6.71 KB
Newer Older
1
import random
Joseph Siddons's avatar
Joseph Siddons committed
2
import unittest
3

4 5
from GeoSpatialTools import haversine
from GeoSpatialTools.quadtree import QuadTree, Record, Rectangle, Ellipse
Joseph Siddons's avatar
Joseph Siddons committed
6 7 8 9


class TestRect(unittest.TestCase):
    def test_contains(self):
10
        rect = Rectangle(0, 20, 0, 10)
Joseph Siddons's avatar
Joseph Siddons committed
11 12 13 14 15 16 17 18 19 20 21 22
        points: list[Record] = [
            Record(10, 5),
            Record(20, 10),
            Record(0, 0),
            Record(12.8, 2.1),
            Record(-2, -9.2),
        ]
        expected = [True, True, True, True, False]
        res = list(map(rect.contains, points))
        assert res == expected

    def test_intersection(self):
23
        rect = Rectangle(0, 20, 0, 10)
Joseph Siddons's avatar
Joseph Siddons committed
24
        test_rects: list[Rectangle] = [
25 26 27
            Rectangle(1, 19, 1, 9),
            Rectangle(20.5, 29.5, -1, 11),
            Rectangle(9, 21, 4.5, 11.5),
Joseph Siddons's avatar
Joseph Siddons committed
28 29 30 31 32
        ]
        expected = [True, False, True]
        res = list(map(rect.intersects, test_rects))
        assert res == expected

33
    def test_wrap(self):
34 35 36
        rect = Rectangle(80, -100, 35, 55)
        assert rect.lon == 170
        assert rect.lat == 45
37 38 39 40 41 42 43 44 45
        test_points: list[Record] = [
            Record(-140, 40),
            Record(0, 50),
            Record(100, 45),
        ]
        expected = [True, False, True]
        res = list(map(rect.contains, test_points))
        assert res == expected

46
        test_rect = Rectangle(-140, -60, 20, 60)
47 48
        assert rect.intersects(test_rect)

49 50 51 52 53 54 55 56
    def test_inside(self):
        # TEST: rectangle fully inside another
        outer = Rectangle(-10, 10, -10, 10)
        inner = Rectangle(-5, 5, -5, 5)

        assert outer.intersects(inner)
        assert inner.intersects(outer)

Joseph Siddons's avatar
Joseph Siddons committed
57 58 59

class TestQuadTree(unittest.TestCase):
    def test_divides(self):
60
        boundary = Rectangle(0, 20, 0, 8)
Joseph Siddons's avatar
Joseph Siddons committed
61 62
        qtree = QuadTree(boundary)
        expected: list[Rectangle] = [
63 64 65 66
            Rectangle(0, 10, 4, 8),
            Rectangle(10, 20, 4, 8),
            Rectangle(0, 10, 0, 4),
            Rectangle(10, 20, 0, 4),
Joseph Siddons's avatar
Joseph Siddons committed
67 68 69 70 71 72 73 74 75 76 77
        ]
        qtree.divide()
        res = [
            qtree.northwest.boundary,
            qtree.northeast.boundary,
            qtree.southwest.boundary,
            qtree.southeast.boundary,
        ]
        assert res == expected

    def test_insert(self):
78
        boundary = Rectangle(0, 20, 0, 8)
Joseph Siddons's avatar
Joseph Siddons committed
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
        qtree = QuadTree(boundary, capacity=3)
        points: list[Record] = [
            Record(10, 5),
            Record(19, 1),
            Record(0, 0),
            Record(-2, -9.2),
            Record(12.8, 2.1),
        ]
        expected = [
            points[:3],
            [],
            [],
            [],
            [points[-1]],
        ]
        for point in points:
            qtree.insert(point)
        assert qtree.divided
        res = [
            qtree.points,
            qtree.northwest.points,
            qtree.northeast.points,
            qtree.southwest.points,
            qtree.southeast.points,
        ]
        assert res == expected

106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
    def test_remove(self):
        boundary = Rectangle(0, 20, 0, 8)
        qtree = QuadTree(boundary, capacity=3)
        points: list[Record] = [
            Record(10, 5),
            Record(19, 1),
            Record(0, 0),
            Record(-2, -9.2),
            Record(12.8, 2.1),
        ]
        to_remove = points[2]
        for point in points:
            qtree.insert(point)
        q_res = qtree.nearby_points(to_remove, dist=0.1)

        # TEST: get the point
        assert len(q_res) == 1

        # TEST: Point is removed
        assert qtree.remove(to_remove)
        q_res = qtree.nearby_points(to_remove, dist=0.1)
        assert len(q_res) == 0

Joseph Siddons's avatar
Joseph Siddons committed
129
    def test_query(self):
130
        boundary = Rectangle(0, 20, 0, 8)
Joseph Siddons's avatar
Joseph Siddons committed
131 132 133 134 135 136 137 138
        qtree = QuadTree(boundary, capacity=3)
        points: list[Record] = [
            Record(10, 5),
            Record(19, 1),
            Record(0, 0),
            Record(-2, -9.2),
            Record(12.8, 2.1),
        ]
139
        test_rect = Rectangle(12, 13, 2, 3)
Joseph Siddons's avatar
Joseph Siddons committed
140 141 142 143 144 145 146 147 148
        expected = [Record(12.8, 2.1)]

        for point in points:
            qtree.insert(point)

        res = qtree.query(test_rect)

        assert res == expected

149 150
    def test_wrap_query(self):
        N = 100
151 152 153 154 155 156
        qt_boundary = Rectangle(-180, 180, -90, 90)
        assert qt_boundary.lon == 0
        assert qt_boundary.lon_range == 360
        assert qt_boundary.lat == 0
        assert qt_boundary.lat_range == 180

157 158
        qt = QuadTree(qt_boundary, capacity=3)

159 160 161 162 163 164
        quert_rect = Rectangle(140, -160, 40, 50)
        assert quert_rect.lon == 170
        assert quert_rect.lon_range == 60
        assert quert_rect.lat == 45
        assert quert_rect.lat_range == 10

165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
        points_want: list[Record] = [
            Record(175, 43),
            Record(-172, 49),
        ]
        points: list[Record] = [
            Record(
                random.choice(range(-150, 130)),
                random.choice(range(-90, 91)),
            )
            for _ in range(N)
        ]
        points.extend(points_want)
        for p in points:
            qt.insert(p)

        res = qt.query(quert_rect)
        assert len(res) == len(points_want)
        assert all([p in res for p in points_want])

184 185 186 187 188 189 190 191 192 193 194
    def test_ellipse_query(self):
        d1 = haversine(0, 2.5, 1, 2.5)
        d2 = haversine(0, 2.5, 0, 3.0)
        theta = 0

        ellipse = Ellipse(12.5, 2.5, d1, d2, theta)
        # TEST: distint locii
        assert (ellipse.p1_lon, ellipse.p1_lat) != (
            ellipse.p2_lon,
            ellipse.p2_lat,
        )
195 196 197 198 199 200 201 202 203 204 205

        # TEST: Near Boundary Points
        assert ellipse.contains(Record(13.49, 2.5))
        assert ellipse.contains(Record(11.51, 2.5))
        assert ellipse.contains(Record(12.5, 2.99))
        assert ellipse.contains(Record(12.5, 2.01))
        assert not ellipse.contains(Record(13.51, 2.5))
        assert not ellipse.contains(Record(11.49, 2.5))
        assert not ellipse.contains(Record(12.5, 3.01))
        assert not ellipse.contains(Record(12.5, 1.99))

206
        boundary = Rectangle(0, 20, 0, 8)
207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
        qtree = QuadTree(boundary, capacity=3)
        points: list[Record] = [
            Record(10, 5),
            Record(19, 1),
            Record(0, 0),
            Record(-2, -9.2),
            Record(13.5, 2.6),  # Just North of Eastern edge
            Record(12.6, 3.0),  # Just East of Northern edge
            Record(12.8, 2.1),
            # Locii
            Record(ellipse.p1_lon, ellipse.p1_lat),
            Record(ellipse.p2_lon, ellipse.p2_lat),
        ]
        expected = [
            Record(12.8, 2.1),
            Record(ellipse.p1_lon, ellipse.p1_lat),
            Record(ellipse.p2_lon, ellipse.p2_lat),
        ]

        for point in points:
            qtree.insert(point)

        res = qtree.query_ellipse(ellipse)
        assert res == expected

Joseph Siddons's avatar
Joseph Siddons committed
232 233 234

if __name__ == "__main__":
    unittest.main()