def test_rectangle_area(self): rectangle = Rectangle.enclosing(make_coordinates(5)) self.assertTrue(rectangle.area() > 0) sw = Coordinates(-21, -61) ne = Coordinates(21, 61) large_rectangle = Rectangle(sw, ne) self.assertTrue(large_rectangle.area() > rectangle.area())
def _best_axis_bisection(self, axis: Collection, mapping=None) -> Tuple[List, List, float]: """Heuristic for determining a good split for the node. This method is meant to be used by the actual splitting algorithm, as several 'axes' must be considered for the choice of a good split. An axis consists in the elements of the node sorted in a certain way, regarding their geographical position. """ mapping = mapping or self.coords_of min_split_size = ceil(0.4 * self.capacity) best_self, best_other = None, None best_sum = float('inf') # Iterate over all possible ways of bisecting the axis that respect # the 'min_split_size' condition. Choose the one that will give # the lowest area sum. for i in range(min_split_size, self.capacity - min_split_size + 1): candidate_self = axis[:i] self_coords = [mapping(x) for x in candidate_self] candidate_other = axis[i:] other_coords = [mapping(x) for x in candidate_other] self_area = Rectangle.enclosing(self_coords).area() other_area = Rectangle.enclosing(other_coords).area() candidate_sum = self_area + other_area if candidate_sum < best_sum: best_self, best_other = candidate_self, candidate_other best_sum = candidate_sum return best_self, best_other, best_sum
def test_rectangle_contains(self): points = make_coordinates(5) rectangle = Rectangle.enclosing(points) self.assertTrue(all(rectangle.contains(p) for p in points)) self.assertTrue(rectangle.contains(rectangle.sw)) self.assertTrue(rectangle.contains(rectangle.ne)) self.assertFalse(rectangle.contains(Coordinates(80, 120)))
def _calculate_bounds(self) -> Rectangle: south = west = float('inf') north = east = float('-inf') for x in self.children: north = max(north, x.bounds.ne.lat) east = max(east, x.bounds.ne.lng) south = min(south, x.bounds.sw.lat) west = min(west, x.bounds.sw.lng) return Rectangle(Coordinates(south, west), Coordinates(north, east))
def _update_bounds(self): """Update the bounding box for this node and its ancestors.""" if self._get_load() == 0: self.bounds = Rectangle(Coordinates(0, 0), Coordinates(0, 0)) return self.bounds = self._calculate_bounds() if not self._is_root(): self.parent._update_bounds()
def get_area_increase(self, candidate) -> float: """Calculate the increase in area for this node if the passed candidate is inserted. """ if self.contains(candidate): return 0 new_point = self.coords_of(candidate) new_sw = Coordinates(min(self.bounds.sw.lat, new_point.lat), min(self.bounds.sw.lng, new_point.lng)) new_ne = Coordinates(max(self.bounds.ne.lat, new_point.lat), max(self.bounds.ne.lng, new_point.lng)) new_area = Rectangle(new_sw, new_ne).area() curr_area = self.bounds.area() return new_area - curr_area
def test_rectangle_distance(self): interior = make_coordinates(5) rect = Rectangle.enclosing(interior) point = make_coordinates(1)[0] self.assertTrue(all(rect.distance_to_point(p) == 0 for p in interior)) # Compare distances in 'km' because of rounding and approximation errors. self.assertLessEqual( rect.distance_to_point(point) // 1000, rect.sw.distance_to(point) // 1000) self.assertLessEqual( rect.distance_to_point(point) // 1000, rect.ne.distance_to(point) // 1000)
def _calculate_bounds(self) -> Rectangle: return Rectangle.enclosing([self.coords_of(x) for x in self.elements])
def test_rectangle_enclosing(self): sw = Coordinates(0, 0) ne = Coordinates(1, 1) rectangle = Rectangle.enclosing([sw, ne]) self.assertEqual(sw, rectangle.sw) self.assertEqual(ne, rectangle.ne)