Example #1
0
    def test_choose_leaf_uses_least_area_enlargement_for_higher_levels(
            self, least_area_enlargement_mock, least_overlap_enlargement_mock):
        """
        Ensure that the choose subtree strategy uses the least area enlargement strategy when picking a subtree at
        levels higher than the one just above the leaf level.
        """
        # Arrange
        tree = RStarTree()
        leaf = RTreeNode(tree, is_leaf=True)
        intermediate = RTreeNode(
            tree,
            is_leaf=False,
            entries=[RTreeEntry(Rect(0, 0, 0, 0), child=leaf)])
        intermediate_entry = RTreeEntry(Rect(0, 0, 0, 0), child=intermediate)
        root = RTreeNode(tree, is_leaf=False, entries=[intermediate_entry])
        tree.root = root
        e = RTreeEntry(Rect(0, 0, 0, 0))
        least_area_enlargement_mock.return_value = intermediate_entry

        # Act
        rstar_choose_leaf(tree, e)

        # Assert
        least_area_enlargement_mock.assert_called_once_with(
            root.entries, e.rect)
        least_overlap_enlargement_mock.assert_called_once_with(
            intermediate.entries, e.rect)
Example #2
0
    def test_get_rstar_stat_same_distribution_for_all_4_sort_types(self):
        """
        Tests get_rstar_stat when all 4 sort types (min_x, max_x, min_y, and max_y) result in the same distribution
        of entries.
        """
        # Arrange
        a = RTreeEntry(data='a', rect=Rect(0, 0, 1, 1))
        b = RTreeEntry(data='b', rect=Rect(1, 1, 2, 2))
        c = RTreeEntry(data='c', rect=Rect(2, 2, 3, 3))
        d = RTreeEntry(data='d', rect=Rect(3, 3, 4, 4))

        # Act
        stat = get_rstar_stat([a, b, c, d], 1, 3)

        # Assert
        unique_distributions = [
            EntryDistribution(([a], [b, c, d])),
            EntryDistribution(([a, b], [c, d])),
            EntryDistribution(([a, b, c], [d]))
        ]
        self.assertCountEqual(unique_distributions, stat.unique_distributions)
        self.assertCountEqual(unique_distributions,
                              stat.get_axis_unique_distributions('x'))
        self.assertCountEqual(unique_distributions,
                              stat.get_axis_unique_distributions('y'))
        self.assertEqual(96, stat.get_axis_perimeter('x'))
        self.assertEqual(96, stat.get_axis_perimeter('y'))
Example #3
0
    def test_rstar_insert_empty(self):
        """Tests inserting into an empty tree"""
        # Arrange
        tree = RStarTree(min_entries=1, max_entries=3)

        # Act
        tree.insert('a', Rect(0, 0, 5, 5))

        # Assert
        # Ensure root entry has the correct data and bounding box
        self.assertEqual(1, len(tree.root.entries))
        e = tree.root.entries[0]
        self.assertEqual('a', e.data)
        self.assertEqual(Rect(0, 0, 5, 5), e.rect)
        self.assertIsNone(e.child)
        # Ensure root node has correct structure
        node = tree.root
        self.assertTrue(node.is_root)
        self.assertTrue(node.is_leaf)
        self.assertEqual(Rect(0, 0, 5, 5), tree.root.get_bounding_rect())
        # Ensure root entry has correct structure
        self.assertTrue(e.is_leaf)
        self.assertIsNone(e.child)
        # Ensure there is only 1 level and 1 node in the tree
        self.assertEqual(1, len(tree.get_levels()))
        self.assertEqual(1, len(list(tree.get_nodes())))
Example #4
0
    def test_get_rstar_stat_sorts_entries_by_both_min_and_max(self):
        """
        List of possible divisions should be based on entries sorted by both the minimum as well as maximum coordinate.
        In the example below, when the entries are sorted by either minx, miny, or maxy, the sort order is always
        (a,b,c), but when sorted by maxx, the order is (b,a,c). This ordering enables the [(b), (a,c)] division (which
        turns out to be optimal).
        """
        # Arrange
        a = RTreeEntry(data='a', rect=Rect(0, 0, 7, 2))
        b = RTreeEntry(data='b', rect=Rect(1, 1, 2, 3))
        c = RTreeEntry(data='c', rect=Rect(2, 2, 8, 4))

        # Act
        stat = get_rstar_stat([a, b, c], 1, 2)

        # Assert
        self.assertCountEqual([
            EntryDistribution(([a], [b, c])),
            EntryDistribution(([a, b], [c])),
            EntryDistribution(([b], [a, c]))
        ], stat.unique_distributions)
        self.assertCountEqual([
            EntryDistribution(([a], [b, c])),
            EntryDistribution(([a, b], [c])),
            EntryDistribution(([b], [a, c]))
        ], stat.get_axis_unique_distributions('x'))
        self.assertCountEqual([
            EntryDistribution(([a], [b, c])),
            EntryDistribution(([a, b], [c]))
        ], stat.get_axis_unique_distributions('y'))
        self.assertEqual(140, stat.get_axis_perimeter('x'))
        self.assertEqual(148, stat.get_axis_perimeter('y'))
Example #5
0
    def test_rstar_insert_no_split(self):
        """Tests multiple inserts which do not require a node split"""
        # Arrange
        tree = RStarTree(min_entries=1, max_entries=2)

        # Act
        tree.insert('a', Rect(0, 0, 5, 2))
        tree.insert('b', Rect(2, 3, 4, 7))

        # Assert
        # Root node
        self.assertTrue(tree.root.is_root)
        self.assertTrue(tree.root.is_leaf)
        self.assertEqual(2, len(tree.root.entries))
        self.assertEqual(Rect(0, 0, 5, 7), tree.root.get_bounding_rect())
        # Entry 'a'
        entry_a = next((e for e in tree.root.entries if e.data == 'a'))
        self.assertEqual(Rect(0, 0, 5, 2), entry_a.rect)
        self.assertTrue(entry_a.is_leaf)
        self.assertIsNone(entry_a.child)
        # Entry 'b'
        entry_b = next((e for e in tree.root.entries if e.data == 'b'))
        self.assertEqual(Rect(2, 3, 4, 7), entry_b.rect)
        self.assertTrue(entry_b.is_leaf)
        self.assertIsNone(entry_b.child)
Example #6
0
 def test_union_disjoint(self):
     """Tests union of two disjoint (non-intersecting) rectanges"""
     # Arrange
     r1 = Rect(min_x=0, min_y=-1, max_x=3, max_y=3)
     r2 = Rect(min_x=2, min_y=5, max_x=6, max_y=8)
     # Act
     rect = r1.union(r2)
     # Assert
     self.assertEqual(Rect(min_x=0, min_y=-1, max_x=6, max_y=8), rect)
Example #7
0
 def test_union_intersecting(self):
     """Tests union of two intersecting rectanges"""
     # Arrange
     r1 = Rect(min_x=0, min_y=0, max_x=3, max_y=3)
     r2 = Rect(min_x=-2, min_y=-2, max_x=2, max_y=2)
     # Act
     rect = r1.union(r2)
     # Assert
     self.assertEqual(Rect(min_x=-2, min_y=-2, max_x=3, max_y=3), rect)
Example #8
0
 def test_rect_centroid(self):
     """Tests calculating the centroid of a rectangle"""
     # Arrange
     r = Rect(2, 2, 6, 5)
     # Act
     centroid = r.centroid()
     # Assert
     self.assertTrue(isclose(4, centroid[0], rel_tol=EPSILON))
     self.assertTrue(isclose(3.5, centroid[1], rel_tol=EPSILON))
Example #9
0
 def test_intersection_area(self):
     """Tests getting the intersection area of two intersecting rectangles"""
     # Arrange
     r1 = Rect(min_x=0, min_y=0, max_x=4, max_y=4)
     r2 = Rect(min_x=2, min_y=2, max_x=5, max_y=5)
     # Act
     area = r1.get_intersection_area(r2)
     # Assert
     self.assertEqual(4, area)
Example #10
0
 def test_union_contains(self):
     """Tests union of two rectanges where one contains the other"""
     # Arrange
     r1 = Rect(min_x=1, min_y=1, max_x=3, max_y=3)
     r2 = Rect(min_x=-2, min_y=-2, max_x=5, max_y=5)
     # Act
     rect = r1.union(r2)
     # Assert
     self.assertEqual(Rect(min_x=-2, min_y=-2, max_x=5, max_y=5), rect)
Example #11
0
    def test_insert_creates_entry(self):
        """Basic test ensuring an insert creates an entry."""
        # Arrange
        t = RTree()

        # Act
        e = t.insert('foo', Rect(0, 0, 1, 1))

        # Assert
        self.assertEqual('foo', e.data)
        self.assertEqual(Rect(0, 0, 1, 1), e.rect)
Example #12
0
    def test_intersects_disjoint(self):
        """Ensures intersects returns False when two rectangles are completely disjoint."""
        # Arrange
        r1 = Rect(min_x=0, min_y=0, max_x=5, max_y=2)
        r2 = Rect(min_x=1, min_y=5, max_x=3, max_y=9)

        # Act
        result = r1.intersects(r2)

        # Assert
        self.assertFalse(result)
Example #13
0
    def test_intersects(self):
        """Ensures intersects returns True when two rectangles intersect."""
        # Arrange
        r1 = Rect(min_x=0, min_y=0, max_x=5, max_y=2)
        r2 = Rect(min_x=2, min_y=1, max_x=4, max_y=3)

        # Act
        result = r1.intersects(r2)

        # Assert
        self.assertTrue(result)
Example #14
0
 def test_intersection_area_disjoint(self):
     """
     Tests getting the intersection area of two completely disjoint (non-intersecting) rectangles with no overlap in
     either dimension.
     """
     # Arrange
     r1 = Rect(min_x=0, min_y=0, max_x=3, max_y=3)
     r2 = Rect(min_x=5, min_y=5, max_x=8, max_y=8)
     # Act
     area = r1.get_intersection_area(r2)
     # Assert
     self.assertEqual(0, area)
Example #15
0
 def test_intersection_area_disjoint_y_overlap(self):
     """
     Tests getting the intersection area of two disjoint rectangles where there is overlap in the y-dimension but not
     in the x-dimension.
     """
     # Arrange
     r1 = Rect(min_x=0, min_y=0, max_x=2, max_y=4)
     r2 = Rect(min_x=2, min_y=2, max_x=3, max_y=3)
     # Act
     area = r1.get_intersection_area(r2)
     # Assert
     self.assertEqual(0, area)
Example #16
0
    def test_least_overlap_enlargement_tie(self):
        """Ensure least area enlargement is used as a tie-breaker when overlap enlargements are equal."""
        # Arrange
        a = RTreeEntry(data='a', rect=Rect(0, 0, 4, 5))
        b = RTreeEntry(data='b', rect=Rect(3, 4, 5, 6))
        rect = Rect(2, 5, 3, 6)

        # Act
        entry = least_overlap_enlargement([a, b], rect)

        # Assert
        self.assertEqual(b, entry)
Example #17
0
    def test_intersects_touches(self):
        """
        Ensures intersects returns False when two rectangles merely touch along a border but do not have any interior
        intersection area.
        """
        # Arrange
        r1 = Rect(min_x=0, min_y=0, max_x=5, max_y=2)
        r2 = Rect(min_x=5, min_y=0, max_x=7, max_y=2)

        # Act
        result = r1.intersects(r2)

        # Assert
        self.assertFalse(result)
Example #18
0
    def test_least_overlap_enlargement(self):
        """
        Basic test of least overlap enlargement helper method. This test demonstrates a scenario where least area
        enlargement would favor one entry, but least overlap enlargement favors another.
        """
        # Arrange
        a = RTreeEntry(data='a', rect=Rect(0, 0, 4, 5))
        b = RTreeEntry(data='b', rect=Rect(2, 4, 5, 6))
        rect = Rect(4, 3, 5, 4)

        # Act
        entry = least_overlap_enlargement([a, b], rect)

        # Assert
        self.assertEqual(a, entry)
Example #19
0
    def test_least_area_enlargement(self):
        """
        Ensure the node whose bounding box needs least enlargement is chosen for a new entry in the case where there is
        a clear winner.
        """
        # Arrange
        a = RTreeEntry(data='a', rect=Rect(0, 0, 3, 3))
        b = RTreeEntry(data='b', rect=Rect(9, 9, 10, 10))
        rect = Rect(2, 2, 4, 4)

        # Act
        entry = least_area_enlargement([a, b], rect)

        # Assert
        self.assertEqual(a, entry)
Example #20
0
    def test_bounding_rect_multiple_inserts_without_split(self):
        """
        Ensure root note bounding rect encompasses the bounding rect of all entries after multiple inserts (without
        forcing a split)
        """
        # Arrange
        t = RTree(max_entries=5)

        # Act
        t.insert('a', Rect(0, 0, 5, 5))
        t.insert('b', Rect(1, 1, 3, 3))
        t.insert('c', Rect(4, 4, 6, 6))

        # Assert
        rect = t.root.get_bounding_rect()
        self.assertEqual(Rect(0, 0, 6, 6), rect)
Example #21
0
    def test_least_area_enlargement_tie(self):
        """
        When two nodes need to be enlarged by the same amount, the strategy should pick the node having the smallest
        area as a tie-breaker.
        """
        # Arrange
        a = RTreeEntry(data='a', rect=Rect(0, 0, 4, 2))
        b = RTreeEntry(data='b', rect=Rect(5, 1, 7, 3))
        c = RTreeEntry(data='c', rect=Rect(0, 4, 1, 5))
        rect = Rect(4, 1, 5, 2)

        # Act
        entry = least_area_enlargement([a, b, c], rect)

        # Assert
        self.assertEqual(b, entry)
Example #22
0
 def test_init(self):
     """Basic test ensuring a Rect can be instantiated"""
     r = Rect(0, 1, 5, 9)
     self.assertEqual(0, r.min_x)
     self.assertEqual(1, r.min_y)
     self.assertEqual(5, r.max_x)
     self.assertEqual(9, r.max_y)
Example #23
0
def _add_electoral_wards(dataset_id):
    areas_to_add = []

    for feature in geojson.loads(wd20_filepath.read_text())["features"]:
        ward_code = feature["properties"]["wd20cd"]
        ward_name = feature["properties"]["wd20nm"]
        ward_id = "wd20-" + ward_code

        print()  # noqa: T001
        print(ward_name)  # noqa: T001

        try:
            la_id = "lad20-" + ward_code_to_la_id_mapping[ward_code]

            feature, simple_feature = (polygons_and_simplified_polygons(
                feature["geometry"]))

            if feature:
                rtree_index.insert(ward_id, Rect(*Polygons(feature).bounds))

            areas_to_add.append([
                ward_id,
                ward_name,
                dataset_id,
                la_id,
                feature,
                simple_feature,
                estimate_number_of_smartphones_in_area(ward_code),
            ])

        except KeyError:
            print("Skipping", ward_code, ward_name)  # noqa: T001

    rtree_index_path.open('wb').write(pickle.dumps(rtree_index))
    repo.insert_broadcast_areas(areas_to_add, keep_old_polygons)
 def overlapping_areas(self):
     if not self.polygons:
         return []
     return broadcast_area_libraries.get_areas([
         overlap.data
         for overlap in rtree_index.query(Rect(*self.polygons.bounds))
     ])
Example #25
0
    def test_multiple_inserts_without_split(self):
        """
        Ensure multiple inserts work (all original entries are returned) without a split (fewer entries than
        max_entries)
        """
        # Arrange
        t = RTree(max_entries=5)

        # Act
        t.insert('a', Rect(0, 0, 5, 5))
        t.insert('b', Rect(1, 1, 3, 3))
        t.insert('c', Rect(4, 4, 6, 6))

        # Assert
        entries = list(t.get_leaf_entries())
        self.assertCountEqual(['a', 'b', 'c'],
                              [entry.data for entry in entries])
Example #26
0
    def test_choose_split_axis(self):
        """
        Ensure split axis is chosen based on smallest overall perimeter of all possible divisions of a list of entries.
        In the below scenario, there is a clear winner with the best division being ([a, b, c], [d]).
        """
        # Arrange
        a = RTreeEntry(data='a', rect=Rect(0, 0, 1, 1))
        b = RTreeEntry(data='b', rect=Rect(1, 0, 2, 1))
        c = RTreeEntry(data='c', rect=Rect(2, 0, 3, 1))
        d = RTreeEntry(data='d', rect=Rect(1, 7, 2, 8))
        stat = get_rstar_stat([a, b, c, d], 1, 3)

        # Act
        result = choose_split_axis(stat)

        # Assert
        self.assertEqual('y', result)
Example #27
0
    def test_quadratic_split(self):
        """Ensures that a split results in the smallest total area."""
        # Arrange
        t = RTreeGuttman(max_entries=4)
        t.insert('a', Rect(2, 8, 5, 9))
        t.insert('b', Rect(4, 0, 5, 10))
        t.insert('c', Rect(5, 0, 6, 10))
        t.insert('d', Rect(5, 7, 8, 8))

        # Act
        split_node = quadratic_split(t, t.root)

        # Assert
        group1 = [e.data for e in t.root.entries]
        group2 = [e.data for e in split_node.entries]
        self.assertCountEqual(['a', 'd'], group1)
        self.assertCountEqual(['b', 'c'], group2)
Example #28
0
    def test_choose_split_index(self):
        """Ensures best split index is chosen based on minimum overlap."""
        # Arrange
        a = RTreeEntry(data='a', rect=Rect(0, 1, 4, 5))
        b = RTreeEntry(data='b', rect=Rect(3, 5, 6, 8))
        c = RTreeEntry(data='c', rect=Rect(7, 0, 9, 4))
        d = RTreeEntry(data='d', rect=Rect(8, 7, 10, 9))
        distributions = [
            EntryDistribution(([a], [b, c, d])),
            EntryDistribution(([a, b], [c, d])),
            EntryDistribution(([a, b, c], [d]))
        ]

        # Act
        i = choose_split_index(distributions)

        # Assert
        self.assertEqual(1, i)
Example #29
0
    def test_choose_split_index_tie(self):
        """When multiple divisions have the same overlap, ensure split index is chosen based on minimum area."""
        # Arrange
        a = RTreeEntry(data='a', rect=Rect(0, 0, 2, 1))
        b = RTreeEntry(data='b', rect=Rect(1, 0, 3, 2))
        c = RTreeEntry(data='c', rect=Rect(2, 2, 4, 3))
        d = RTreeEntry(data='d', rect=Rect(9, 9, 10, 10))
        distributions = [
            EntryDistribution(([a], [b, c, d])),
            EntryDistribution(([a, b], [c, d])),
            EntryDistribution(([a, b, c], [d]))
        ]

        # Act
        i = choose_split_index(distributions)

        # Assert
        self.assertEqual(2, i)
Example #30
0
    def test_rstar_overflow_split_root(self):
        """
        When the root node overflows, the root node should be split and the tree should grow a level. Forced reinsert
        should not occur at the root level.
        """
        # Arrange
        t = RStarTree(max_entries=3)
        r1 = Rect(0, 0, 3, 2)
        r2 = Rect(7, 7, 10, 9)
        r3 = Rect(2, 1, 5, 3)
        entry_a = RTreeEntry(r1, data='a')
        entry_b = RTreeEntry(r2, data='b')
        entry_c = RTreeEntry(r3, data='c')
        t.root.entries = [entry_a, entry_b, entry_c]
        # Arrange entry being inserted. Since the root node is at max capacity, this entry should cause the root
        # to overflow.
        r4 = Rect(6, 6, 8, 8)

        # Act
        entry_d = t.insert('d', r4)

        # Assert
        # Root node should no longer be a leaf node (but should still be root)
        self.assertFalse(t.root.is_leaf)
        self.assertTrue(t.root.is_root)
        # Root node bounding box should encompass all entries
        self.assertEqual(Rect(0, 0, 10, 9), t.root.get_bounding_rect())
        # Root node should have 2 child entries
        self.assertEqual(2, len(t.root.entries))
        e1 = t.root.entries[0]
        e2 = t.root.entries[1]
        # e1 bounding box should encompass entries [a, c]
        self.assertEqual(Rect(0, 0, 5, 3), e1.rect)
        # e2 bounding box should encompass entries [b ,d]
        self.assertEqual(Rect(6, 6, 10, 9), e2.rect)
        # Ensure children nodes of e1 and e2 are leaf nodes
        leaf_node_1 = e1.child
        leaf_node_2 = e2.child
        self.assertIsNotNone(leaf_node_1)
        self.assertIsNotNone(leaf_node_2)
        self.assertTrue(leaf_node_1.is_leaf)
        self.assertTrue(leaf_node_2.is_leaf)
        # Leaf node 1 should contain entries [a, c]
        self.assertEqual(Rect(0, 0, 5, 3), leaf_node_1.get_bounding_rect())
        self.assertCountEqual([entry_a, entry_c], leaf_node_1.entries)
        # Leaf node 2 should contain entries [b, d]
        self.assertEqual(Rect(6, 6, 10, 9), leaf_node_2.get_bounding_rect())
        self.assertCountEqual([entry_b, entry_d], leaf_node_2.entries)