Commit 0f8df82e authored by Joseph Siddons's avatar Joseph Siddons
Browse files

Merge branch 'bounding_rect' into 'main'

Define Rectangle and SpaceTimeRectangle by bounding box

Closes #4

See merge request !9
parents 9a529acd 188f0db1
from dataclasses import dataclass
import datetime
from .distance_metrics import haversine, destination
from .utils import LatitudeError
from .utils import LatitudeError, DateWarning
from math import degrees, sqrt
from warnings import warn
class SpaceTimeRecord:
......@@ -101,75 +102,64 @@ class SpaceTimeRectangle:
Parameters
----------
lon : float
Horizontal centre of the rectangle (longitude).
lat : float
Vertical centre of the rectangle (latitude).
datetime : datetime.datetime
Datetime centre of the rectangle.
w : float
Width of the rectangle (longitude range).
h : float
Height of the rectangle (latitude range).
dt : datetime.timedelta
time extent of the rectangle.
west : float
Western boundary of the Rectangle
east : float
Eastern boundary of the Rectangle
south : float
Southern boundary of the Rectangle
north : float
Northern boundary of the Rectangle
start : datetime.datetime
Start datetime of the Rectangle
end : datetime.datetime
End datetime of the Rectangle
"""
lon: float
lat: float
date: datetime.datetime
lon_range: float
lat_range: float
dt: datetime.timedelta
west: float
east: float
south: float
north: float
start: datetime.datetime
end: datetime.datetime
def __post_init__(self):
if self.lon > 180:
self.lon -= 360
if self.lat > 90 or self.lat < -90:
if self.east > 180 or self.east < -180:
self.east = ((self.east + 540) % 360) - 180
if self.west > 180 or self.west < -180:
self.west = ((self.west + 540) % 360) - 180
if self.north > 90 or self.south < -90:
raise LatitudeError(
f"Central latitude value out of range {self.lat}, "
+ "should be between -90, 90 degrees"
"Latitude bounds are out of bounds. "
+ f"{self.north = }, {self.south = }"
)
if self.end < self.start:
warn("End date is before start date. Swapping", DateWarning)
self.start, self.end = self.end, self.start
@property
def west(self) -> float:
"""Western boundary of the Rectangle"""
return (((self.lon - self.lon_range / 2) + 540) % 360) - 180
def lat_range(self) -> float:
"""Latitude range of the Rectangle"""
return self.north - self.south
@property
def east(self) -> float:
"""Eastern boundary of the Rectangle"""
return (((self.lon + self.lon_range / 2) + 540) % 360) - 180
def lat(self) -> float:
"""Centre latitude of the Rectangle"""
return self.south + self.lat_range / 2
@property
def north(self) -> float:
"""Northern boundary of the Rectangle"""
north = self.lat + self.lat_range / 2
if north > 90:
raise LatitudeError(
"Rectangle crosses north pole - Use two Rectangles"
)
return north
def lon_range(self) -> float:
"""Longitude range of the Rectangle"""
if self.east < self.west:
return self.east - self.west + 360
@property
def south(self) -> float:
"""Southern boundary of the Rectangle"""
south = self.lat - self.lat_range / 2
if south < -90:
raise LatitudeError(
"Rectangle crosses south pole - Use two Rectangles"
)
return south
@property
def start(self) -> datetime.datetime:
"""Start date of the Rectangle"""
return self.date - self.dt / 2
return self.east - self.west
@property
def end(self) -> datetime.datetime:
"""End date of the Rectangle"""
return self.date + self.dt / 2
def lon(self) -> float:
"""Centre longitude of the Rectangle"""
lon = self.west + self.lon_range / 2
return ((lon + 540) % 360) - 180
@property
def edge_dist(self) -> float:
......@@ -185,6 +175,14 @@ class SpaceTimeRectangle:
)
return corner_dist
@property
def time_range(self) -> datetime.timedelta:
return self.end - self.start
@property
def centre_datetime(self) -> datetime.datetime:
return self.start + (self.end - self.start) / 2
def _test_east_west(self, lon: float) -> bool:
if self.lon_range >= 360:
# Rectangle encircles earth
......@@ -267,8 +265,8 @@ class SpaceTimeRectangle:
bool : True if the point is <= dist + max(dist(centre, corners))
"""
if (
point.datetime - t_dist > self.date + self.dt / 2
or point.datetime + t_dist < self.date - self.dt / 2
point.datetime - t_dist > self.end
or point.datetime + t_dist < self.start
):
return False
# QUESTION: Is this sufficient? Possibly it is overkill
......@@ -288,34 +286,40 @@ class SpaceTimeEllipse:
Horizontal centre of the ellipse
lat : float
Vertical centre of the ellipse
datetime : datetime.datetime
Datetime centre of the ellipse.
a : float
Length of the semi-major axis
b : float
Length of the semi-minor axis
theta : float
Angle of the semi-major axis from horizontal anti-clockwise in radians
dt : datetime.timedelta
(full) time extent of the ellipse.
start : datetime.datetime
Start date of the Ellipse
end : datetime.datetime
Send date of the Ellipse
"""
def __init__(
self,
lon: float,
lat: float,
datetime: datetime.datetime,
a: float,
b: float,
theta: float,
dt: datetime.timedelta,
start: datetime.datetime,
end: datetime.datetime,
) -> None:
self.a = a
self.b = b
self.lon = lon
if self.lon > 180:
self.lon = ((self.lon + 540) % 360) - 180
self.lat = lat
self.datetime = datetime
self.dt = dt
self.start = start
self.end = end
if self.end < self.start:
warn("End date is before start date. Swapping")
self.start, self.end = self.end, self.start
# theta is anti-clockwise angle from horizontal in radians
self.theta = theta
# bearing is angle clockwise from north in degrees
......@@ -337,8 +341,6 @@ class SpaceTimeEllipse:
(self.bearing - 180) % 360,
self.c,
)
self.start = self.datetime - self.dt / 2
self.end = self.datetime + self.dt / 2
def contains(self, point: SpaceTimeRecord) -> bool:
"""Test if a point is contained within the Ellipse"""
......@@ -439,12 +441,12 @@ class OctTree:
"""Divide the QuadTree"""
self.northwestfwd = OctTree(
SpaceTimeRectangle(
self.boundary.lon - self.boundary.lon_range / 4,
self.boundary.lat + self.boundary.lat_range / 4,
self.boundary.date + self.boundary.dt / 4,
self.boundary.lon_range / 2,
self.boundary.lat_range / 2,
self.boundary.dt / 2,
self.boundary.west,
self.boundary.lon,
self.boundary.lat,
self.boundary.north,
self.boundary.centre_datetime,
self.boundary.end,
),
capacity=self.capacity,
depth=self.depth + 1,
......@@ -452,12 +454,12 @@ class OctTree:
)
self.northeastfwd = OctTree(
SpaceTimeRectangle(
self.boundary.lon + self.boundary.lon_range / 4,
self.boundary.lat + self.boundary.lat_range / 4,
self.boundary.date + self.boundary.dt / 4,
self.boundary.lon_range / 2,
self.boundary.lat_range / 2,
self.boundary.dt / 2,
self.boundary.lon,
self.boundary.east,
self.boundary.lat,
self.boundary.north,
self.boundary.centre_datetime,
self.boundary.end,
),
capacity=self.capacity,
depth=self.depth + 1,
......@@ -465,12 +467,12 @@ class OctTree:
)
self.southwestfwd = OctTree(
SpaceTimeRectangle(
self.boundary.lon - self.boundary.lon_range / 4,
self.boundary.lat - self.boundary.lat_range / 4,
self.boundary.date + self.boundary.dt / 4,
self.boundary.lon_range / 2,
self.boundary.lat_range / 2,
self.boundary.dt / 2,
self.boundary.west,
self.boundary.lon,
self.boundary.south,
self.boundary.lat,
self.boundary.centre_datetime,
self.boundary.end,
),
capacity=self.capacity,
depth=self.depth + 1,
......@@ -478,12 +480,12 @@ class OctTree:
)
self.southeastfwd = OctTree(
SpaceTimeRectangle(
self.boundary.lon + self.boundary.lon_range / 4,
self.boundary.lat - self.boundary.lat_range / 4,
self.boundary.date + self.boundary.dt / 4,
self.boundary.lon_range / 2,
self.boundary.lat_range / 2,
self.boundary.dt / 2,
self.boundary.lon,
self.boundary.east,
self.boundary.south,
self.boundary.lat,
self.boundary.centre_datetime,
self.boundary.end,
),
capacity=self.capacity,
depth=self.depth + 1,
......@@ -491,12 +493,12 @@ class OctTree:
)
self.northwestback = OctTree(
SpaceTimeRectangle(
self.boundary.lon - self.boundary.lon_range / 4,
self.boundary.lat + self.boundary.lat_range / 4,
self.boundary.date - self.boundary.dt / 4,
self.boundary.lon_range / 2,
self.boundary.lat_range / 2,
self.boundary.dt / 2,
self.boundary.west,
self.boundary.lon,
self.boundary.lat,
self.boundary.north,
self.boundary.start,
self.boundary.centre_datetime,
),
capacity=self.capacity,
depth=self.depth + 1,
......@@ -504,12 +506,12 @@ class OctTree:
)
self.northeastback = OctTree(
SpaceTimeRectangle(
self.boundary.lon + self.boundary.lon_range / 4,
self.boundary.lat + self.boundary.lat_range / 4,
self.boundary.date - self.boundary.dt / 4,
self.boundary.lon_range / 2,
self.boundary.lat_range / 2,
self.boundary.dt / 2,
self.boundary.lon,
self.boundary.east,
self.boundary.lat,
self.boundary.north,
self.boundary.start,
self.boundary.centre_datetime,
),
capacity=self.capacity,
depth=self.depth + 1,
......@@ -517,12 +519,12 @@ class OctTree:
)
self.southwestback = OctTree(
SpaceTimeRectangle(
self.boundary.lon - self.boundary.lon_range / 4,
self.boundary.lat - self.boundary.lat_range / 4,
self.boundary.date - self.boundary.dt / 4,
self.boundary.lon_range / 2,
self.boundary.lat_range / 2,
self.boundary.dt / 2,
self.boundary.west,
self.boundary.lon,
self.boundary.south,
self.boundary.lat,
self.boundary.start,
self.boundary.centre_datetime,
),
capacity=self.capacity,
depth=self.depth + 1,
......@@ -530,12 +532,12 @@ class OctTree:
)
self.southeastback = OctTree(
SpaceTimeRectangle(
self.boundary.lon + self.boundary.lon_range / 4,
self.boundary.lat - self.boundary.lat_range / 4,
self.boundary.date - self.boundary.dt / 4,
self.boundary.lon_range / 2,
self.boundary.lat_range / 2,
self.boundary.dt / 2,
self.boundary.lon,
self.boundary.east,
self.boundary.south,
self.boundary.lat,
self.boundary.start,
self.boundary.centre_datetime,
),
capacity=self.capacity,
depth=self.depth + 1,
......@@ -543,9 +545,6 @@ class OctTree:
)
self.divided = True
def _datetime_is_numeric(self) -> bool:
return not isinstance(self.boundary.date, datetime)
def insert(self, point: SpaceTimeRecord) -> bool:
"""
Insert a SpaceTimeRecord into the QuadTree.
......
......@@ -90,59 +90,55 @@ class Rectangle:
Parameters
----------
lon : float
Horizontal centre of the rectangle
lat : float
Vertical centre of the rectangle
lon_range : float
Width of the rectangle
lat_range : float
Height of the rectangle
west : float
Western boundary of the Rectangle
east : float
Eastern boundary of the Rectangle
south : float
Southern boundary of the Rectangle
north : float
Northern boundary of the Rectangle
"""
lon: float
lat: float
lon_range: float
lat_range: float
west: float
east: float
south: float
north: float
def __post_init__(self):
if self.lon > 180:
self.lon -= 360
if self.lat > 90 or self.lat < -90:
if self.east > 180 or self.east < -180:
self.east = ((self.east + 540) % 360) - 180
if self.west > 180 or self.west < -180:
self.west = ((self.west + 540) % 360) - 180
if self.north > 90 or self.south < -90:
raise LatitudeError(
f"Central latitude value out of range {self.lat}, "
+ "should be between -90, 90 degrees"
"Latitude bounds are out of bounds. "
+ f"{self.north = }, {self.south = }"
)
@property
def west(self) -> float:
"""Western boundary of the Rectangle"""
return (((self.lon - self.lon_range / 2) + 540) % 360) - 180
def lat_range(self) -> float:
"""Latitude range of the Rectangle"""
return self.north - self.south
@property
def east(self) -> float:
"""Eastern boundary of the Rectangle"""
return (((self.lon + self.lon_range / 2) + 540) % 360) - 180
def lat(self) -> float:
"""Centre latitude of the Rectangle"""
return self.south + self.lat_range / 2
@property
def north(self) -> float:
"""Northern boundary of the Rectangle"""
north = self.lat + self.lat_range / 2
if north > 90:
raise LatitudeError(
"Rectangle crosses north pole - Use two Rectangles"
)
return north
def lon_range(self) -> float:
"""Longitude range of the Rectangle"""
if self.east < self.west:
return self.east - self.west + 360
return self.east - self.west
@property
def south(self) -> float:
"""Southern boundary of the Rectangle"""
south = self.lat - self.lat_range / 2
if south < -90:
raise LatitudeError(
"Rectangle crosses south pole - Use two Rectangles"
)
return south
def lon(self) -> float:
"""Centre longitude of the Rectangle"""
lon = self.west + self.lon_range / 2
return ((lon + 540) % 360) - 180
@property
def edge_dist(self) -> float:
......@@ -244,6 +240,8 @@ class Ellipse:
self.a = a
self.b = b
self.lon = lon
if self.lon > 180:
self.lon = ((self.lon + 540) % 360) - 180
self.lat = lat
# theta is anti-clockwise angle from horizontal in radians
self.theta = theta
......@@ -338,10 +336,10 @@ class QuadTree:
"""Divide the QuadTree"""
self.northwest = QuadTree(
Rectangle(
self.boundary.lon - self.boundary.lon_range / 4,
self.boundary.lat + self.boundary.lat_range / 4,
self.boundary.lon_range / 2,
self.boundary.lat_range / 2,
self.boundary.west,
self.boundary.lon,
self.boundary.lat,
self.boundary.north,
),
capacity=self.capacity,
depth=self.depth + 1,
......@@ -349,10 +347,10 @@ class QuadTree:
)
self.northeast = QuadTree(
Rectangle(
self.boundary.lon + self.boundary.lon_range / 4,
self.boundary.lat + self.boundary.lat_range / 4,
self.boundary.lon_range / 2,
self.boundary.lat_range / 2,
self.boundary.lon,
self.boundary.east,
self.boundary.lat,
self.boundary.north,
),
capacity=self.capacity,
depth=self.depth + 1,
......@@ -360,10 +358,10 @@ class QuadTree:
)
self.southwest = QuadTree(
Rectangle(
self.boundary.lon - self.boundary.lon_range / 4,
self.boundary.lat - self.boundary.lat_range / 4,
self.boundary.lon_range / 2,
self.boundary.lat_range / 2,
self.boundary.west,
self.boundary.lon,
self.boundary.south,
self.boundary.lat,
),
capacity=self.capacity,
depth=self.depth + 1,
......@@ -371,10 +369,10 @@ class QuadTree:
)
self.southeast = QuadTree(
Rectangle(
self.boundary.lon + self.boundary.lon_range / 4,
self.boundary.lat - self.boundary.lat_range / 4,
self.boundary.lon_range / 2,
self.boundary.lat_range / 2,
self.boundary.lon,
self.boundary.east,
self.boundary.south,
self.boundary.lat,
),
capacity=self.capacity,
depth=self.depth + 1,
......
......@@ -2,3 +2,9 @@ class LatitudeError(ValueError):
"""Error for invalid Latitude Value"""
pass
class DateWarning(Warning):
"""Warnning for Datetime Value"""
pass
......@@ -101,15 +101,22 @@ QuadTree(
max_depth: int,
depth: int = 0 # Internal parameter
)
```
The boundary is defined by a `Rectangle` class, which is defined by the bounding box.
```python
Rectangle(
lon: float, # Centre longitude
lat: float, # Centre latitude
lon_range: float, # Full width of rectangle (degrees)
lat_range: float, # Full height of rectangle (degrees)
west: float, # Western edge
east: float, # Eastern edge
south: float, # Southern edge
north: float, # Northern edge
)
```
The `Rectangle` class will raise an error if the northern or southern boundary go beyond the north
or south pole.
```python
from GeoSpatialTools import QuadTree, Record, Rectangle
from random import choice
......@@ -119,7 +126,7 @@ lat_range = list(range(-90, 90))
N_samples = 1000
# Construct Tree
boundary = Rectangle(0, 0, 360, 180) # Full domain
boundary = Rectangle(-180, 180, -90, 90) # Full domain
qt = QuadTree(boundary)
records: list[Record] = [Record(choice(lon_range), choice(lat_range)) for _ in range(N_samples)]
......@@ -134,7 +141,10 @@ neighbours: list[Record] = qt.nearby_points(test_value, dist)
#### OctTree - 3d QuadTree
Adds `SpaceTimeRecord`, `SpaceTimeRectangle` and `OctTree` classes.
Adds `SpaceTimeRecord`, `SpaceTimeRectangle` and `OctTree` classes. This allows for querying in a
third dimension, specifically this adds a time dimension. Typically the time dimension is assumed
to be of `datetime.datetime` type, however this is expected to work with numeric values, for example
pentad, day of year. However, this non-datetime behaviour is not intended.
```python
SpaceTimeRecord(
......@@ -144,14 +154,19 @@ SpaceTimeRecord(
uid: str | None,
**data
)
```
As with the `Rectangle` class for the `QuadTree`, the `SpaceTimeRectangle` defines the boundary of
an `OctTree` class, and is defined by the space-time bounding box.
```python
SpaceTimeRectangle(
lon: float, # Centre longitude
lat: float, # Centre latitude
datetime: datetime, # Central datetime
w: float, # Full width of rectangle (degrees)
h: float, # Full height of rectangle (degrees)
dt: timedelta, # Time extent of rectangle
west: float, # Western edge
east: float, # Eastern edge
south: float, # Southern edge
north: float, # Northern edge
start: datetime.datetime, # Start datetime
end: datetime.datetime, # End datetime
)
```
......@@ -175,7 +190,7 @@ dates = date_range(
N_samples = 1000
# Construct Tree
boundary = SpaceTimeRectangle(0, 0, datetime(2009, 1, 15, 12), 360, 180, timedelta(days=31)) # Full domain
boundary = SpaceTimeRectangle(-180, 180, -90, 90, datetime(2009, 1, 1, 0), datetime(2009, 1, 2, 23)) # Full domain
ot = OctTree(boundary)
records: list[SpaceTimeRecord] = [
......
......@@ -120,7 +120,7 @@
"metadata": {},
"outputs": [],
"source": [
"otree = OctTree(Rectangle(0, 0, datetime(1900, 1, 16), 360, 180, timedelta(days=32)), capacity = 10, max_depth = 25)"
"otree = OctTree(Rectangle(-180, 180, -90, 90, datetime(1900, 1, 1, 0), datetime(1900, 1, 31, 23)), capacity = 10, max_depth = 25)"
]
},
{
......@@ -133,8 +133,8 @@
"name": "stdout",
"output_type": "stream",
"text": [
"CPU times: user 213 ms, sys: 5.74 ms, total: 219 ms\n",
"Wall time: 218 ms\n"
"CPU times: user 106 ms, sys: 3.98 ms, total: 110 ms\n",
"Wall time: 109 ms\n"
]
}
],
......@@ -157,105 +157,105 @@
"output_type": "stream",
"text": [
"OctTree:\n",
"- boundary: SpaceTimeRectangle(lon=0, lat=0, date=datetime.datetime(1900, 1, 16, 0, 0), lon_range=360, lat_range=180, dt=datetime.timedelta(days=32))\n",
"- boundary: SpaceTimeRectangle(west=-180, east=180, south=-90, north=90, start=datetime.datetime(1900, 1, 1, 0, 0), end=datetime.datetime(1900, 1, 31, 23, 0))\n",
"- capacity: 10\n",
"- depth: 0\n",
"- max_depth: 25\n",
"- contents:\n",
"- number of elements: 10\n",
" * SpaceTimeRecord(x = -12, y = 89, datetime = 1900-01-03 18:00:00, uid = 70Fv8aPq)\n",
" * SpaceTimeRecord(x = -157, y = 21, datetime = 1900-01-29 16:00:00, uid = j2xUkgob)\n",
" * SpaceTimeRecord(x = -179, y = -25, datetime = 1900-01-30 19:00:00, uid = Nmw6lJSS)\n",
" * SpaceTimeRecord(x = -23, y = 89, datetime = 1900-01-14 15:00:00, uid = ILOWgH6u)\n",
" * SpaceTimeRecord(x = 145, y = 41, datetime = 1900-01-26 03:00:00, uid = Cii4cemG)\n",
" * SpaceTimeRecord(x = -139, y = -10, datetime = 1900-01-06 21:00:00, uid = 3QFhOlsW)\n",
" * SpaceTimeRecord(x = -146, y = -51, datetime = 1900-01-18 04:00:00, uid = IHZEXm2l)\n",
" * SpaceTimeRecord(x = -96, y = 86, datetime = 1900-01-14 22:00:00, uid = DtwMgmpH)\n",
" * SpaceTimeRecord(x = 113, y = 11, datetime = 1900-01-19 00:00:00, uid = OcFalcj8)\n",
" * SpaceTimeRecord(x = -141, y = -26, datetime = 1900-01-08 11:00:00, uid = qoJquq8j)\n",
" * SpaceTimeRecord(x = 92, y = 15, datetime = 1900-01-17 08:00:00, uid = HRF401hH)\n",
" * SpaceTimeRecord(x = -35, y = 37, datetime = 1900-01-04 08:00:00, uid = CXZaSOdh)\n",
" * SpaceTimeRecord(x = 84, y = -7, datetime = 1900-01-07 16:00:00, uid = 2aEjxGwG)\n",
" * SpaceTimeRecord(x = 68, y = 73, datetime = 1900-01-18 17:00:00, uid = Ah7lanWB)\n",
" * SpaceTimeRecord(x = -179, y = 40, datetime = 1900-01-01 11:00:00, uid = HGxSJzf4)\n",
" * SpaceTimeRecord(x = -73, y = 23, datetime = 1900-01-09 12:00:00, uid = qHQ8opO9)\n",
" * SpaceTimeRecord(x = 117, y = -23, datetime = 1900-01-31 06:00:00, uid = ctvs56Fq)\n",
" * SpaceTimeRecord(x = 109, y = 55, datetime = 1900-01-13 14:00:00, uid = C2xXIglD)\n",
" * SpaceTimeRecord(x = 104, y = -10, datetime = 1900-01-06 16:00:00, uid = WEpQKIOV)\n",
" * SpaceTimeRecord(x = 45, y = -71, datetime = 1900-01-29 00:00:00, uid = 7r1UeXRi)\n",
"- with children:\n",
" OctTree:\n",
" - boundary: SpaceTimeRectangle(lon=-90.0, lat=45.0, date=datetime.datetime(1900, 1, 8, 0, 0), lon_range=180.0, lat_range=90.0, dt=datetime.timedelta(days=16))\n",
" - boundary: SpaceTimeRectangle(west=-180, east=0.0, south=0.0, north=90, start=datetime.datetime(1900, 1, 1, 0, 0), end=datetime.datetime(1900, 1, 16, 11, 30))\n",
" - capacity: 10\n",
" - depth: 1\n",
" - max_depth: 25\n",
" - contents:\n",
" - number of elements: 10\n",
" * SpaceTimeRecord(x = -89, y = 62, datetime = 1900-01-02 15:00:00, uid = zo7Thw1L)\n",
" * SpaceTimeRecord(x = -34, y = 62, datetime = 1900-01-13 19:00:00, uid = faiiSA9Y)\n",
" * SpaceTimeRecord(x = -61, y = 54, datetime = 1900-01-07 17:00:00, uid = HwzmXILd)\n",
" * SpaceTimeRecord(x = -154, y = 21, datetime = 1900-01-08 21:00:00, uid = lA7DANvC)\n",
" * SpaceTimeRecord(x = -59, y = 87, datetime = 1900-01-02 23:00:00, uid = GSzuXTLF)\n",
" * SpaceTimeRecord(x = -109, y = 27, datetime = 1900-01-01 21:00:00, uid = qy7npkH7)\n",
" * SpaceTimeRecord(x = -86, y = 46, datetime = 1900-01-10 19:00:00, uid = RJBRR7Rl)\n",
" * SpaceTimeRecord(x = -114, y = 46, datetime = 1900-01-03 05:00:00, uid = Eop56HgI)\n",
" * SpaceTimeRecord(x = -148, y = 49, datetime = 1900-01-08 13:00:00, uid = 3bavQs9B)\n",
" * SpaceTimeRecord(x = -109, y = 61, datetime = 1900-01-06 06:00:00, uid = zaTZk8xi)\n",
" * SpaceTimeRecord(x = -84, y = 38, datetime = 1900-01-15 10:00:00, uid = 63mpq3Kx)\n",
" * SpaceTimeRecord(x = -78, y = 60, datetime = 1900-01-10 01:00:00, uid = vZ8HLu5t)\n",
" * SpaceTimeRecord(x = -89, y = 24, datetime = 1900-01-12 17:00:00, uid = gn2o9tYQ)\n",
" * SpaceTimeRecord(x = -149, y = 7, datetime = 1900-01-08 11:00:00, uid = 2ODnGJO6)\n",
" * SpaceTimeRecord(x = -37, y = 54, datetime = 1900-01-12 13:00:00, uid = 11cApOwm)\n",
" * SpaceTimeRecord(x = -34, y = 88, datetime = 1900-01-03 05:00:00, uid = 8SN6zPWh)\n",
" * SpaceTimeRecord(x = -36, y = 13, datetime = 1900-01-14 13:00:00, uid = ijfjmp8E)\n",
" * SpaceTimeRecord(x = -168, y = 62, datetime = 1900-01-03 09:00:00, uid = Cc4m1azR)\n",
" * SpaceTimeRecord(x = -76, y = 67, datetime = 1900-01-06 04:00:00, uid = 4WeWpZUz)\n",
" * SpaceTimeRecord(x = -156, y = 39, datetime = 1900-01-13 10:00:00, uid = dZXAMaXq)\n",
" - with children:\n",
" OctTree:\n",
" - boundary: SpaceTimeRectangle(lon=-135.0, lat=67.5, date=datetime.datetime(1900, 1, 4, 0, 0), lon_range=90.0, lat_range=45.0, dt=datetime.timedelta(days=8))\n",
" - boundary: SpaceTimeRectangle(west=-180, east=-90.0, south=45.0, north=90, start=datetime.datetime(1900, 1, 1, 0, 0), end=datetime.datetime(1900, 1, 8, 17, 45))\n",
" - capacity: 10\n",
" - depth: 2\n",
" - max_depth: 25\n",
" - contents:\n",
" - number of elements: 10\n",
" * SpaceTimeRecord(x = -93, y = 49, datetime = 1900-01-07 05:00:00, uid = Vq4kU1vN)\n",
" * SpaceTimeRecord(x = -151, y = 84, datetime = 1900-01-03 03:00:00, uid = 1pz9w5ZR)\n",
" * SpaceTimeRecord(x = -154, y = 74, datetime = 1900-01-03 17:00:00, uid = m1jkOrRF)\n",
" * SpaceTimeRecord(x = -121, y = 81, datetime = 1900-01-02 13:00:00, uid = IcrUGFud)\n",
" * SpaceTimeRecord(x = -169, y = 52, datetime = 1900-01-03 12:00:00, uid = Rmytp4VV)\n",
" * SpaceTimeRecord(x = -115, y = 70, datetime = 1900-01-07 17:00:00, uid = XYiNarG0)\n",
" * SpaceTimeRecord(x = -104, y = 51, datetime = 1900-01-02 06:00:00, uid = je7IfMVs)\n",
" * SpaceTimeRecord(x = -161, y = 58, datetime = 1900-01-01 19:00:00, uid = szpJUEjI)\n",
" * SpaceTimeRecord(x = -133, y = 46, datetime = 1900-01-03 07:00:00, uid = vD2DhqWZ)\n",
" * SpaceTimeRecord(x = -125, y = 67, datetime = 1900-01-07 12:00:00, uid = rOJwKqKs)\n",
" * SpaceTimeRecord(x = -141, y = 79, datetime = 1900-01-03 05:00:00, uid = mN1Mg7Vn)\n",
" * SpaceTimeRecord(x = -172, y = 80, datetime = 1900-01-01 14:00:00, uid = NBBZ3bCW)\n",
" * SpaceTimeRecord(x = -93, y = 53, datetime = 1900-01-06 07:00:00, uid = jX8HZPJT)\n",
" * SpaceTimeRecord(x = -168, y = 82, datetime = 1900-01-03 08:00:00, uid = dlxpN1Ew)\n",
" * SpaceTimeRecord(x = -111, y = 83, datetime = 1900-01-02 12:00:00, uid = GXLopHH0)\n",
" * SpaceTimeRecord(x = -178, y = 61, datetime = 1900-01-02 00:00:00, uid = 0ut6CLe5)\n",
" * SpaceTimeRecord(x = -148, y = 74, datetime = 1900-01-07 23:00:00, uid = xUySW1tx)\n",
" * SpaceTimeRecord(x = -174, y = 63, datetime = 1900-01-06 22:00:00, uid = 8sI94Lt6)\n",
" * SpaceTimeRecord(x = -114, y = 84, datetime = 1900-01-05 15:00:00, uid = OoY9mEkQ)\n",
" * SpaceTimeRecord(x = -102, y = 82, datetime = 1900-01-02 15:00:00, uid = bd4sLang)\n",
" - with children:\n",
" OctTree:\n",
" - boundary: SpaceTimeRectangle(lon=-157.5, lat=78.75, date=datetime.datetime(1900, 1, 2, 0, 0), lon_range=45.0, lat_range=22.5, dt=datetime.timedelta(days=4))\n",
" - boundary: SpaceTimeRectangle(west=-180, east=-135.0, south=67.5, north=90, start=datetime.datetime(1900, 1, 1, 0, 0), end=datetime.datetime(1900, 1, 4, 20, 52, 30))\n",
" - capacity: 10\n",
" - depth: 3\n",
" - max_depth: 25\n",
" - contents:\n",
" - number of elements: 10\n",
" * SpaceTimeRecord(x = -141, y = 82, datetime = 1900-01-01 13:00:00, uid = B8slJoTY)\n",
" * SpaceTimeRecord(x = -147, y = 70, datetime = 1900-01-02 00:00:00, uid = bcnBHvsx)\n",
" * SpaceTimeRecord(x = -170, y = 86, datetime = 1900-01-02 17:00:00, uid = jEUfXIsD)\n",
" * SpaceTimeRecord(x = -180, y = 78, datetime = 1900-01-02 23:00:00, uid = TYebBlyX)\n",
" * SpaceTimeRecord(x = -135, y = 68, datetime = 1900-01-01 11:00:00, uid = UtzHGKY0)\n",
" * SpaceTimeRecord(x = -136, y = 85, datetime = 1900-01-01 02:00:00, uid = HxwAfFf7)\n",
" * SpaceTimeRecord(x = -169, y = 71, datetime = 1900-01-03 12:00:00, uid = kGOcDjS4)\n",
" * SpaceTimeRecord(x = -164, y = 79, datetime = 1900-01-03 11:00:00, uid = IFXekuK1)\n",
" * SpaceTimeRecord(x = -138, y = 76, datetime = 1900-01-01 09:00:00, uid = Qs0TDXtf)\n",
" * SpaceTimeRecord(x = -154, y = 71, datetime = 1900-01-01 16:00:00, uid = E6eJ2eiF)\n",
" * SpaceTimeRecord(x = -148, y = 79, datetime = 1900-01-03 21:00:00, uid = kNWm70rm)\n",
" * SpaceTimeRecord(x = -157, y = 80, datetime = 1900-01-03 05:00:00, uid = 471X27tA)\n",
" * SpaceTimeRecord(x = -152, y = 85, datetime = 1900-01-03 01:00:00, uid = cjTyQn7E)\n",
" * SpaceTimeRecord(x = -154, y = 88, datetime = 1900-01-03 15:00:00, uid = JTnjCJZN)\n",
" * SpaceTimeRecord(x = -139, y = 83, datetime = 1900-01-01 21:00:00, uid = kZ28j8I5)\n",
" * SpaceTimeRecord(x = -161, y = 73, datetime = 1900-01-03 02:00:00, uid = wsHJBLLC)\n",
" * SpaceTimeRecord(x = -140, y = 71, datetime = 1900-01-02 07:00:00, uid = 4bTg1N2k)\n",
" * SpaceTimeRecord(x = -141, y = 74, datetime = 1900-01-04 09:00:00, uid = I6M8kuue)\n",
" * SpaceTimeRecord(x = -144, y = 72, datetime = 1900-01-04 17:00:00, uid = 0fPvYOC9)\n",
" * SpaceTimeRecord(x = -157, y = 78, datetime = 1900-01-03 16:00:00, uid = yAL3OeaK)\n",
" - with children:\n",
" OctTree:\n",
" - boundary: SpaceTimeRectangle(lon=-168.75, lat=84.375, date=datetime.datetime(1900, 1, 1, 0, 0), lon_range=22.5, lat_range=11.25, dt=datetime.timedelta(days=2))\n",
" - capacity: 10\n",
" - depth: 4\n",
" - max_depth: 25\n",
" OctTree:\n",
" - boundary: SpaceTimeRectangle(lon=-146.25, lat=84.375, date=datetime.datetime(1900, 1, 1, 0, 0), lon_range=22.5, lat_range=11.25, dt=datetime.timedelta(days=2))\n",
" - boundary: SpaceTimeRectangle(west=-180, east=-157.5, south=78.75, north=90, start=datetime.datetime(1900, 1, 1, 0, 0), end=datetime.datetime(1900, 1, 2, 22, 26, 15))\n",
" - capacity: 10\n",
" - depth: 4\n",
" - max_depth: 25\n",
" - contents:\n",
" - number of elements: 1\n",
" * SpaceTimeRecord(x = -135, y = 89, datetime = 1900-01-01 04:00:00, uid = MUrEpv1f)\n",
" - number of elements: 4\n",
" * SpaceTimeRecord(x = -180, y = 88, datetime = 1900-01-02 12:00:00, uid = CXeAd3y4)\n",
" * SpaceTimeRecord(x = -180, y = 87, datetime = 1900-01-01 16:00:00, uid = TB2xKFgK)\n",
" * SpaceTimeRecord(x = -171, y = 79, datetime = 1900-01-02 04:00:00, uid = pIU8qvxT)\n",
" * SpaceTimeRecord(x = -168, y = 85, datetime = 1900-01-01 22:00:00, uid = 7zL4gz8K)\n",
" OctTree:\n",
" - boundary: SpaceTimeRectangle(lon=-168.75, lat=73.125, date=datetime.datetime(1900, 1, 1, 0, 0), lon_range=22.5, lat_range=11.25, dt=datetime.timedelta(days=2))\n",
" - boundary: SpaceTimeRectangle(west=-157.5, east=-135.0, south=78.75, north=90, start=datetime.datetime(1900, 1, 1, 0, 0), end=datetime.datetime(1900, 1, 2, 22, 26, 15))\n",
" - capacity: 10\n",
" - depth: 4\n",
" - max_depth: 25\n",
" - contents:\n",
" - number of elements: 2\n",
" * SpaceTimeRecord(x = -149, y = 82, datetime = 1900-01-01 20:00:00, uid = xTYMs6Xp)\n",
" * SpaceTimeRecord(x = -154, y = 84, datetime = 1900-01-02 21:00:00, uid = JSEaGBsn)\n",
" OctTree:\n",
" - boundary: SpaceTimeRectangle(lon=-146.25, lat=73.125, date=datetime.datetime(1900, 1, 1, 0, 0), lon_range=22.5, lat_range=11.25, dt=datetime.timedelta(days=2))\n",
" - boundary: SpaceTimeRectangle(west=-180, east=-157.5, south=67.5, north=78.75, start=datetime.datetime(1900, 1, 1, 0, 0), end=datetime.datetime(1900, 1, 2, 22, 26, 15))\n",
" - capacity: 10\n",
" - depth: 4\n",
" - max_depth: 25\n",
" OctTree:\n",
" - boundary: SpaceTimeRectangle(lon=-168.75, lat=84.375, date=datetime.datetime(1900, 1, 3, 0, 0), lon_range=22.5, lat_range=11.25, dt=datetime.timedelta(days=2))\n",
" - capacity: 10\n",
" - depth: 4\n",
" - max_depth: 25\n"
" - contents:\n",
" - number of elements: 3\n",
" * SpaceTimeRecord(x = -173, y = 75, datetime = 1900-01-01 06:00:00, uid = M4N3amQ3)\n"
]
}
],
......@@ -301,7 +301,7 @@
"name": "stdout",
"output_type": "stream",
"text": [
"224 μs ± 6.15 μs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)\n"
"207 μs ± 6.25 μs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)\n"
]
}
],
......@@ -601,7 +601,7 @@
"name": "stdout",
"output_type": "stream",
"text": [
"5.4 ms ± 19.5 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n"
"5.36 ms ± 164 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n"
]
}
],
......@@ -631,8 +631,8 @@
"name": "stdout",
"output_type": "stream",
"text": [
"CPU times: user 2.5 s, sys: 228 ms, total: 2.73 s\n",
"Wall time: 2.65 s\n"
"CPU times: user 2.52 s, sys: 253 ms, total: 2.78 s\n",
"Wall time: 2.66 s\n"
]
}
],
......
......@@ -9,13 +9,16 @@ from GeoSpatialTools.octtree import (
SpaceTimeRectangle as Rectangle,
SpaceTimeEllipse as Ellipse,
)
from GeoSpatialTools.utils import DateWarning
class TestRect(unittest.TestCase):
def test_contains(self):
d = datetime(2009, 1, 1, 0, 0)
dt = timedelta(days=14)
rect = Rectangle(10, 5, d, 20, 10, dt)
dt = timedelta(days=7)
start = d - dt
end = d + dt
rect = Rectangle(0, 20, 0, 10, start, end)
points: list[Record] = [
Record(10, 5, d),
Record(20, 10, d + timedelta(hours=4)),
......@@ -30,15 +33,34 @@ class TestRect(unittest.TestCase):
def test_intersection(self):
d = datetime(2009, 1, 1, 0, 0)
dt = timedelta(days=14)
rect = Rectangle(10, 5, d, 20, 10, dt)
dt = timedelta(days=7)
start = d - dt
end = d + dt
rect = Rectangle(0, 20, 0, 10, start, end)
test_rects: list[Rectangle] = [
Rectangle(10, 5, d + timedelta(days=2), 18, 8, dt),
Rectangle(25, 5, d, 9, 12, timedelta(hours=7)),
Rectangle(
15, 8, d - timedelta(hours=18), 12, 7, timedelta(hours=4)
1, 19, 1, 9, d - timedelta(days=1), d + timedelta(days=1)
),
Rectangle(
20.5,
29.5,
-1,
11,
d - timedelta(hours=7),
d + timedelta(hours=7),
),
Rectangle(
9,
21,
4.5,
11.5,
d - timedelta(hours=20),
d - timedelta(hours=16),
),
Rectangle(
9, 21, 4.5, 11.5, d + timedelta(days=25), d + timedelta(days=27)
),
Rectangle(15, 8, d + timedelta(days=25), 12, 7, dt),
]
expected = [True, False, True, False]
res = list(map(rect.intersects, test_rects))
......@@ -46,10 +68,14 @@ class TestRect(unittest.TestCase):
def test_wrap(self):
d = datetime(2009, 1, 1, 0, 0)
dt = timedelta(days=14)
rect = Rectangle(170, 45, d, 180, 20, dt)
assert rect.east < 0
assert rect.west > 0
dt = timedelta(days=7)
start = d - dt
end = d + dt
rect = Rectangle(80, -100, 35, 55, start, end)
assert rect.lon == 170
assert rect.lat == 45
test_points: list[Record] = [
Record(-140, 40, d),
Record(0, 50, d),
......@@ -61,23 +87,49 @@ class TestRect(unittest.TestCase):
assert res == expected
test_rect = Rectangle(
-100, 40, d + timedelta(days=3), 80, 40, timedelta(days=2)
-140,
60,
20,
60,
d + timedelta(days=2),
d + timedelta(days=4),
)
assert test_rect.east < rect.west
assert rect.intersects(test_rect)
# TEST: spatially match, time fail
test_rect = Rectangle(
-100, 40, d + timedelta(days=13), 80, 40, timedelta(days=2)
-140,
60,
20,
60,
d + timedelta(days=12),
d + timedelta(days=14),
)
assert not rect.intersects(test_rect)
def test_swap_date(self):
d = datetime(2009, 1, 1, 0, 0)
dt = timedelta(days=7)
start = d - dt
end = d + dt
with self.assertWarns(DateWarning):
rect = Rectangle(80, -100, 35, 55, end, start)
assert rect.start == start
assert rect.end == end
def test_inside(self):
# TEST: rectangle fully inside another
d = datetime(1978, 5, 17, 2, 33)
dt = timedelta(days=4, hours=7)
outer = Rectangle(-10, 10, d, -10, 10, dt)
inner = Rectangle(-5, 5, d, -5, 5, timedelta(days=1, hours=3))
d = datetime(2009, 1, 1, 0, 0)
dt = timedelta(days=7)
start = d - dt
end = d + dt
outer = Rectangle(-10, 10, -10, 10, start, end)
inner = Rectangle(
-5, 5, -5, 5, start + timedelta(days=1), end - timedelta(days=1)
)
assert outer.intersects(inner)
assert inner.intersects(outer)
......@@ -87,22 +139,31 @@ class TestOctTree(unittest.TestCase):
def test_divides(self):
d = datetime(2023, 3, 24, 12, 0)
dt = timedelta(days=1)
start = d - dt / 2
end = d + dt / 2
# TEST: Could construct start and ends for OctTree children using
# the start and end of the boundary, but I want to verify that
# the values are what I expect
d1 = datetime(2023, 3, 24, 6, 0)
d2 = datetime(2023, 3, 24, 18, 0)
dt2 = timedelta(hours=12)
start1 = d1 - dt2 / 2
end1 = d1 + dt2 / 2
start2 = d2 - dt2 / 2
end2 = d2 + dt2 / 2
boundary = Rectangle(10, 4, d, 20, 8, dt)
boundary = Rectangle(0, 20, 0, 8, start, end)
otree = OctTree(boundary)
expected: list[Rectangle] = [
Rectangle(5, 6, d1, 10, 4, dt2),
Rectangle(15, 6, d1, 10, 4, dt2),
Rectangle(5, 2, d1, 10, 4, dt2),
Rectangle(15, 2, d1, 10, 4, dt2),
Rectangle(5, 6, d2, 10, 4, dt2),
Rectangle(15, 6, d2, 10, 4, dt2),
Rectangle(5, 2, d2, 10, 4, dt2),
Rectangle(15, 2, d2, 10, 4, dt2),
Rectangle(0, 10, 4, 8, start1, end1),
Rectangle(10, 20, 4, 8, start1, end1),
Rectangle(0, 10, 0, 4, start1, end1),
Rectangle(10, 20, 0, 4, start1, end1),
Rectangle(0, 10, 4, 8, start2, end2),
Rectangle(10, 20, 4, 8, start2, end2),
Rectangle(0, 10, 0, 4, start2, end2),
Rectangle(10, 20, 0, 4, start2, end2),
]
otree.divide()
res = [
......@@ -120,7 +181,10 @@ class TestOctTree(unittest.TestCase):
def test_insert(self):
d = datetime(2023, 3, 24, 12, 0)
dt = timedelta(days=10)
boundary = Rectangle(10, 4, d, 20, 8, dt)
start = d - dt
end = d + dt
boundary = Rectangle(0, 20, 0, 8, start, end)
otree = OctTree(boundary, capacity=3)
points: list[Record] = [
Record(10, 4, d, "main"),
......@@ -162,7 +226,9 @@ class TestOctTree(unittest.TestCase):
def test_query(self):
d = datetime(2023, 3, 24, 12, 0)
dt = timedelta(days=10)
boundary = Rectangle(10, 4, d, 20, 8, dt)
start = d - dt
end = d + dt
boundary = Rectangle(0, 20, 0, 8, start, end)
otree = OctTree(boundary, capacity=3)
points: list[Record] = [
Record(10, 4, d, "main"),
......@@ -189,12 +255,12 @@ class TestOctTree(unittest.TestCase):
N = 100
d = datetime(2023, 3, 24, 12, 0)
dt = timedelta(days=10)
boundary = Rectangle(0, 0, d, 360, 180, dt)
start = d - dt
end = d + dt
boundary = Rectangle(-180, 180, -90, 90, start, end)
ot = OctTree(boundary, capacity=3)
quert_rect = Rectangle(
170, 45, d + timedelta(days=4), 60, 10, timedelta(days=8)
)
quert_rect = Rectangle(140, -160, 40, 50, d, d + timedelta(days=8))
points_want: list[Record] = [
Record(175, 43, d + timedelta(days=2)),
Record(-172, 49, d + timedelta(days=4)),
......@@ -222,11 +288,20 @@ class TestOctTree(unittest.TestCase):
d = datetime(2023, 3, 24, 12, 0)
dt = timedelta(days=10)
start = d - dt
end = d + dt
test_datetime = d + timedelta(hours=4)
test_timedelta = timedelta(hours=5)
ellipse = Ellipse(
12.5, 2.5, test_datetime, d1, d2, theta, test_timedelta
12.5,
2.5,
d1,
d2,
theta,
test_datetime - test_timedelta / 2,
test_datetime + test_timedelta / 2,
)
# TEST: distint locii
assert (ellipse.p1_lon, ellipse.p1_lat) != (
......@@ -244,7 +319,7 @@ class TestOctTree(unittest.TestCase):
assert not ellipse.contains(Record(12.5, 3.01, test_datetime))
assert not ellipse.contains(Record(12.5, 1.99, test_datetime))
boundary = Rectangle(10, 4, d, 20, 8, dt)
boundary = Rectangle(0, 20, 0, 8, start, end)
otree = OctTree(boundary, capacity=3)
points: list[Record] = [
......
......@@ -7,7 +7,7 @@ from GeoSpatialTools.quadtree import QuadTree, Record, Rectangle, Ellipse
class TestRect(unittest.TestCase):
def test_contains(self):
rect = Rectangle(10, 5, 20, 10)
rect = Rectangle(0, 20, 0, 10)
points: list[Record] = [
Record(10, 5),
Record(20, 10),
......@@ -20,20 +20,20 @@ class TestRect(unittest.TestCase):
assert res == expected
def test_intersection(self):
rect = Rectangle(10, 5, 20, 10)
rect = Rectangle(0, 20, 0, 10)
test_rects: list[Rectangle] = [
Rectangle(10, 5, 18, 8),
Rectangle(25, 5, 9, 12),
Rectangle(15, 8, 12, 7),
Rectangle(1, 19, 1, 9),
Rectangle(20.5, 29.5, -1, 11),
Rectangle(9, 21, 4.5, 11.5),
]
expected = [True, False, True]
res = list(map(rect.intersects, test_rects))
assert res == expected
def test_wrap(self):
rect = Rectangle(170, 45, 180, 20)
assert rect.east < 0
assert rect.west > 0
rect = Rectangle(80, -100, 35, 55)
assert rect.lon == 170
assert rect.lat == 45
test_points: list[Record] = [
Record(-140, 40),
Record(0, 50),
......@@ -43,8 +43,7 @@ class TestRect(unittest.TestCase):
res = list(map(rect.contains, test_points))
assert res == expected
test_rect = Rectangle(-100, 40, 80, 40)
assert test_rect.east < rect.west
test_rect = Rectangle(-140, -60, 20, 60)
assert rect.intersects(test_rect)
def test_inside(self):
......@@ -58,13 +57,13 @@ class TestRect(unittest.TestCase):
class TestQuadTree(unittest.TestCase):
def test_divides(self):
boundary = Rectangle(10, 4, 20, 8)
boundary = Rectangle(0, 20, 0, 8)
qtree = QuadTree(boundary)
expected: list[Rectangle] = [
Rectangle(5, 6, 10, 4),
Rectangle(15, 6, 10, 4),
Rectangle(5, 2, 10, 4),
Rectangle(15, 2, 10, 4),
Rectangle(0, 10, 4, 8),
Rectangle(10, 20, 4, 8),
Rectangle(0, 10, 0, 4),
Rectangle(10, 20, 0, 4),
]
qtree.divide()
res = [
......@@ -76,7 +75,7 @@ class TestQuadTree(unittest.TestCase):
assert res == expected
def test_insert(self):
boundary = Rectangle(10, 4, 20, 8)
boundary = Rectangle(0, 20, 0, 8)
qtree = QuadTree(boundary, capacity=3)
points: list[Record] = [
Record(10, 5),
......@@ -105,7 +104,7 @@ class TestQuadTree(unittest.TestCase):
assert res == expected
def test_query(self):
boundary = Rectangle(10, 4, 20, 8)
boundary = Rectangle(0, 20, 0, 8)
qtree = QuadTree(boundary, capacity=3)
points: list[Record] = [
Record(10, 5),
......@@ -114,7 +113,7 @@ class TestQuadTree(unittest.TestCase):
Record(-2, -9.2),
Record(12.8, 2.1),
]
test_rect = Rectangle(12.5, 2.5, 1, 1)
test_rect = Rectangle(12, 13, 2, 3)
expected = [Record(12.8, 2.1)]
for point in points:
......@@ -126,10 +125,20 @@ class TestQuadTree(unittest.TestCase):
def test_wrap_query(self):
N = 100
qt_boundary = Rectangle(0, 0, 360, 180)
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
qt = QuadTree(qt_boundary, capacity=3)
quert_rect = Rectangle(170, 45, 60, 10)
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
points_want: list[Record] = [
Record(175, 43),
Record(-172, 49),
......@@ -171,7 +180,7 @@ class TestQuadTree(unittest.TestCase):
assert not ellipse.contains(Record(12.5, 3.01))
assert not ellipse.contains(Record(12.5, 1.99))
boundary = Rectangle(10, 4, 20, 8)
boundary = Rectangle(0, 20, 0, 8)
qtree = QuadTree(boundary, capacity=3)
points: list[Record] = [
Record(10, 5),
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment