Commit 762dfafa authored by Joseph Siddons's avatar Joseph Siddons
Browse files

refactor(Rectangle)!: Fix wrapping at -180, 180. Rewrite as dataclass.

Adds north, east, south, west attributes
from datetime import datetime, timedelta from dataclasses import dataclass
import datetime import datetime
from .distance_metrics import haversine, destination from .distance_metrics import haversine, destination
from .utils import LatitudeError from .utils import LatitudeError
...@@ -83,6 +83,7 @@ class SpaceTimeRecords(list[SpaceTimeRecord]): ...@@ -83,6 +83,7 @@ class SpaceTimeRecords(list[SpaceTimeRecord]):
"""List of SpaceTimeRecords""" """List of SpaceTimeRecords"""
@dataclass
class SpaceTimeRectangle: class SpaceTimeRectangle:
""" """
A simple Space Time SpaceTimeRectangle class. A simple Space Time SpaceTimeRectangle class.
...@@ -104,73 +105,134 @@ class SpaceTimeRectangle: ...@@ -104,73 +105,134 @@ class SpaceTimeRectangle:
Horizontal centre of the rectangle (longitude). Horizontal centre of the rectangle (longitude).
lat : float lat : float
Vertical centre of the rectangle (latitude). Vertical centre of the rectangle (latitude).
datetime : datetime datetime : datetime.datetime
Datetime centre of the rectangle. Datetime centre of the rectangle.
w : float w : float
Width of the rectangle (longitude range). Width of the rectangle (longitude range).
h : float h : float
Height of the rectangle (latitude range). Height of the rectangle (latitude range).
dt : timedelta dt : datetime.timedelta
time extent of the rectangle. time extent of the rectangle.
""" """
def __init__( lon: float
self, lat: float
lon: float, date: datetime.datetime
lat: float, lon_range: float
datetime: datetime, lat_range: float
lon_range: float, dt: datetime.timedelta
lat_range: float,
dt: timedelta, def __post_init__(self):
) -> None: if self.lon > 180:
self.lon = lon self.lon -= 360
self.lat = lat if self.lat > 90 or self.lat < -90:
self.lon_range = lon_range raise LatitudeError(
self.lat_range = lat_range f"Central latitude value out of range {self.lat}, "
self.datetime = datetime + "should be between -90, 90 degrees"
self.dt = dt )
def __str__(self) -> str: @property
return f"SpaceTimeRectangle(x = {self.lon}, y = {self.lat}, w = {self.lon_range}, h = {self.lat_range}, t = {self.datetime}, dt = {self.dt})" def west(self) -> float:
"""Western boundary of the Rectangle"""
return (((self.lon - self.lon_range / 2) + 540) % 360) - 180
@property
def east(self) -> float:
"""Eastern boundary of the Rectangle"""
return (((self.lon + self.lon_range / 2) + 540) % 360) - 180
@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 __eq__(self, other: object) -> bool: @property
return ( def south(self) -> float:
isinstance(other, SpaceTimeRectangle) """Southern boundary of the Rectangle"""
and self.lon == other.lon south = self.lat - self.lat_range / 2
and self.lat == other.lat if south < -90:
and self.lon_range == other.lon_range raise LatitudeError(
and self.lat_range == other.lat_range "Rectangle crosses south pole - Use two Rectangles"
and self.datetime == other.datetime )
and self.dt == other.dt return south
@property
def start(self) -> datetime.datetime:
"""Start date of the Rectangle"""
return self.date - self.dt / 2
@property
def end(self) -> datetime.datetime:
"""End date of the Rectangle"""
return self.date + self.dt / 2
@property
def edge_dist(self) -> float:
"""Approximate maximum distance from the centre to an edge"""
corner_dist = max(
haversine(self.lon, self.lat, self.east, self.north),
haversine(self.lon, self.lat, self.east, self.south),
) )
if self.east * self.west < 0:
corner_dist = max(
corner_dist,
haversine(self.lon, self.lat, self.east, 0),
)
return corner_dist
def _test_east_west(self, lon: float) -> bool:
if self.lon_range >= 360:
# Rectangle encircles earth
return True
if self.east > self.lon and self.west < self.lon:
return lon <= self.east and lon >= self.west
if self.east < self.lon:
return not (lon > self.east and lon < self.west)
if self.west > self.lon:
return not (lon < self.east and lon > self.west)
return False
def _test_north_south(self, lat: float) -> bool:
return lat <= self.north and lat >= self.south
def contains(self, point: SpaceTimeRecord) -> bool: def contains(self, point: SpaceTimeRecord) -> bool:
"""Test if a point is contained within the SpaceTimeRectangle""" """Test if a point is contained within the SpaceTimeRectangle"""
return ( if point.datetime > self.end or point.datetime < self.start:
point.lon <= self.lon + self.lon_range / 2 return False
and point.lon >= self.lon - self.lon_range / 2 return self._test_north_south(point.lat) and self._test_east_west(
and point.lat <= self.lat + self.lat_range / 2 point.lon
and point.lat >= self.lat - self.lat_range / 2
and point.datetime <= self.datetime + self.dt / 2
and point.datetime >= self.datetime - self.dt / 2
) )
def intersects(self, other: object) -> bool: def intersects(self, other: object) -> bool:
"""Test if another Rectangle object intersects this Rectangle""" """Test if another Rectangle object intersects this Rectangle"""
return isinstance(other, SpaceTimeRectangle) and not ( if not isinstance(other, SpaceTimeRectangle):
self.lon - self.lon_range / 2 > other.lon + other.lon_range / 2 raise TypeError(
or self.lon + self.lon_range / 2 < other.lon - other.lon_range / 2 f"other must be a Rectangle class, got {type(other)}"
or self.lat - self.lat_range / 2 > other.lat + other.lat_range / 2 )
or self.lat + self.lat_range / 2 < other.lat - other.lat_range / 2 if other.end < self.start or other.start > self.end:
or self.datetime - self.dt / 2 > other.datetime + other.dt / 2 # Not in the same time range
or self.datetime + self.dt / 2 < other.datetime - other.dt / 2 return False
if other.south > self.north:
# Other is fully north of self
return False
if other.north < self.south:
# Other is fully south of self
return False
# Handle east / west edges
return self._test_east_west(other.west) or self._test_east_west(
other.east
) )
def nearby( def nearby(
self, self,
point: SpaceTimeRecord, point: SpaceTimeRecord,
dist: float, dist: float,
t_dist: timedelta, t_dist: datetime.timedelta,
) -> bool: ) -> bool:
""" """
Check if point is nearby the Rectangle Check if point is nearby the Rectangle
...@@ -192,42 +254,21 @@ class SpaceTimeRectangle: ...@@ -192,42 +254,21 @@ class SpaceTimeRectangle:
---------- ----------
point : SpaceTimeRecord point : SpaceTimeRecord
dist : float, dist : float,
t_dist : timedelta t_dist : datetime.timedelta
Returns Returns
------- -------
bool : True if the point is <= dist + max(dist(centre, corners)) bool : True if the point is <= dist + max(dist(centre, corners))
""" """
if ( if (
point.datetime - t_dist > self.datetime + self.dt / 2 point.datetime - t_dist > self.date + self.dt / 2
or point.datetime + t_dist < self.datetime - self.dt / 2 or point.datetime + t_dist < self.date - self.dt / 2
): ):
return False return False
# QUESTION: Is this sufficient? Possibly it is overkill # QUESTION: Is this sufficient? Possibly it is overkill
corner_dist = max(
haversine(
self.lon,
self.lat,
self.lon + self.lon_range / 2,
self.lat + self.lat_range / 2,
),
haversine(
self.lon,
self.lat,
self.lon + self.lon_range / 2,
self.lat - self.lat_range / 2,
),
)
if (self.lat + self.lat_range / 2) * (
self.lat - self.lat_range / 2
) < 0:
corner_dist = max(
corner_dist,
haversine(self.lon, self.lat, self.lon + self.lon_range / 2, 0),
)
return ( return (
haversine(self.lon, self.lat, point.lon, point.lat) haversine(self.lon, self.lat, point.lon, point.lat)
<= dist + corner_dist <= dist + self.edge_dist
) )
...@@ -241,7 +282,7 @@ class SpaceTimeEllipse: ...@@ -241,7 +282,7 @@ class SpaceTimeEllipse:
Horizontal centre of the ellipse Horizontal centre of the ellipse
lat : float lat : float
Vertical centre of the ellipse Vertical centre of the ellipse
datetime : datetime datetime : datetime.datetime
Datetime centre of the ellipse. Datetime centre of the ellipse.
a : float a : float
Length of the semi-major axis Length of the semi-major axis
...@@ -249,7 +290,7 @@ class SpaceTimeEllipse: ...@@ -249,7 +290,7 @@ class SpaceTimeEllipse:
Length of the semi-minor axis Length of the semi-minor axis
theta : float theta : float
Angle of the semi-major axis from horizontal anti-clockwise in radians Angle of the semi-major axis from horizontal anti-clockwise in radians
dt : timedelta dt : datetime.timedelta
(full) time extent of the ellipse. (full) time extent of the ellipse.
""" """
...@@ -257,11 +298,11 @@ class SpaceTimeEllipse: ...@@ -257,11 +298,11 @@ class SpaceTimeEllipse:
self, self,
lon: float, lon: float,
lat: float, lat: float,
datetime: datetime, datetime: datetime.datetime,
a: float, a: float,
b: float, b: float,
theta: float, theta: float,
dt: timedelta, dt: datetime.timedelta,
) -> None: ) -> None:
self.a = a self.a = a
self.b = b self.b = b
...@@ -290,53 +331,28 @@ class SpaceTimeEllipse: ...@@ -290,53 +331,28 @@ class SpaceTimeEllipse:
(self.bearing - 180) % 360, (self.bearing - 180) % 360,
self.c, self.c,
) )
self.start = self.datetime - self.dt / 2
self.end = self.datetime + self.dt / 2
def contains(self, point: SpaceTimeRecord) -> bool: def contains(self, point: SpaceTimeRecord) -> bool:
"""Test if a point is contained within the Ellipse""" """Test if a point is contained within the Ellipse"""
if point.datetime > self.end or point.datetime < self.start:
return False
return ( return (
( haversine(self.p1_lon, self.p1_lat, point.lon, point.lat)
haversine(self.p1_lon, self.p1_lat, point.lon, point.lat) + haversine(self.p2_lon, self.p2_lat, point.lon, point.lat)
+ haversine(self.p2_lon, self.p2_lat, point.lon, point.lat) ) <= 2 * self.a
)
<= 2 * self.a
and point.datetime <= self.datetime + self.dt / 2
and point.datetime >= self.datetime - self.dt / 2
)
def nearby_rect(self, rect: SpaceTimeRectangle) -> bool: def nearby_rect(self, rect: SpaceTimeRectangle) -> bool:
"""Test if a rectangle is near to the Ellipse""" """Test if a rectangle is near to the Ellipse"""
if ( if rect.start > self.end or rect.end < self.start:
rect.datetime - rect.dt / 2 > self.datetime + self.dt / 2
or rect.datetime + rect.dt / 2 < self.datetime - self.dt / 2
):
return False return False
# TODO: Check corners, and 0 lat # TODO: Check corners, and 0 lat
corner_dist = max(
haversine(
rect.lon,
rect.lat,
rect.lon + rect.lon_range / 2,
rect.lat + rect.lat_range / 2,
),
haversine(
rect.lon,
rect.lat,
rect.lon + rect.lon_range / 2,
rect.lat - rect.lat_range / 2,
),
)
if (rect.lat + rect.lat_range / 2) * (
rect.lat - rect.lat_range / 2
) < 0:
corner_dist = max(
corner_dist,
haversine(rect.lon, rect.lat, rect.lon + rect.lon_range / 2, 0),
)
return ( return (
haversine(self.p1_lon, self.p1_lat, rect.lon, rect.lat) haversine(self.p1_lon, self.p1_lat, rect.lon, rect.lat)
<= corner_dist + self.a <= rect.edge_dist + self.a
and haversine(self.p2_lon, self.p2_lat, rect.lon, rect.lat) and haversine(self.p2_lon, self.p2_lat, rect.lon, rect.lat)
<= corner_dist + self.a <= rect.edge_dist + self.a
) )
...@@ -419,7 +435,7 @@ class OctTree: ...@@ -419,7 +435,7 @@ class OctTree:
SpaceTimeRectangle( SpaceTimeRectangle(
self.boundary.lon - self.boundary.lon_range / 4, self.boundary.lon - self.boundary.lon_range / 4,
self.boundary.lat + self.boundary.lat_range / 4, self.boundary.lat + self.boundary.lat_range / 4,
self.boundary.datetime + self.boundary.dt / 4, self.boundary.date + self.boundary.dt / 4,
self.boundary.lon_range / 2, self.boundary.lon_range / 2,
self.boundary.lat_range / 2, self.boundary.lat_range / 2,
self.boundary.dt / 2, self.boundary.dt / 2,
...@@ -432,7 +448,7 @@ class OctTree: ...@@ -432,7 +448,7 @@ class OctTree:
SpaceTimeRectangle( SpaceTimeRectangle(
self.boundary.lon + self.boundary.lon_range / 4, self.boundary.lon + self.boundary.lon_range / 4,
self.boundary.lat + self.boundary.lat_range / 4, self.boundary.lat + self.boundary.lat_range / 4,
self.boundary.datetime + self.boundary.dt / 4, self.boundary.date + self.boundary.dt / 4,
self.boundary.lon_range / 2, self.boundary.lon_range / 2,
self.boundary.lat_range / 2, self.boundary.lat_range / 2,
self.boundary.dt / 2, self.boundary.dt / 2,
...@@ -445,7 +461,7 @@ class OctTree: ...@@ -445,7 +461,7 @@ class OctTree:
SpaceTimeRectangle( SpaceTimeRectangle(
self.boundary.lon - self.boundary.lon_range / 4, self.boundary.lon - self.boundary.lon_range / 4,
self.boundary.lat - self.boundary.lat_range / 4, self.boundary.lat - self.boundary.lat_range / 4,
self.boundary.datetime + self.boundary.dt / 4, self.boundary.date + self.boundary.dt / 4,
self.boundary.lon_range / 2, self.boundary.lon_range / 2,
self.boundary.lat_range / 2, self.boundary.lat_range / 2,
self.boundary.dt / 2, self.boundary.dt / 2,
...@@ -458,7 +474,7 @@ class OctTree: ...@@ -458,7 +474,7 @@ class OctTree:
SpaceTimeRectangle( SpaceTimeRectangle(
self.boundary.lon + self.boundary.lon_range / 4, self.boundary.lon + self.boundary.lon_range / 4,
self.boundary.lat - self.boundary.lat_range / 4, self.boundary.lat - self.boundary.lat_range / 4,
self.boundary.datetime + self.boundary.dt / 4, self.boundary.date + self.boundary.dt / 4,
self.boundary.lon_range / 2, self.boundary.lon_range / 2,
self.boundary.lat_range / 2, self.boundary.lat_range / 2,
self.boundary.dt / 2, self.boundary.dt / 2,
...@@ -471,7 +487,7 @@ class OctTree: ...@@ -471,7 +487,7 @@ class OctTree:
SpaceTimeRectangle( SpaceTimeRectangle(
self.boundary.lon - self.boundary.lon_range / 4, self.boundary.lon - self.boundary.lon_range / 4,
self.boundary.lat + self.boundary.lat_range / 4, self.boundary.lat + self.boundary.lat_range / 4,
self.boundary.datetime - self.boundary.dt / 4, self.boundary.date - self.boundary.dt / 4,
self.boundary.lon_range / 2, self.boundary.lon_range / 2,
self.boundary.lat_range / 2, self.boundary.lat_range / 2,
self.boundary.dt / 2, self.boundary.dt / 2,
...@@ -484,7 +500,7 @@ class OctTree: ...@@ -484,7 +500,7 @@ class OctTree:
SpaceTimeRectangle( SpaceTimeRectangle(
self.boundary.lon + self.boundary.lon_range / 4, self.boundary.lon + self.boundary.lon_range / 4,
self.boundary.lat + self.boundary.lat_range / 4, self.boundary.lat + self.boundary.lat_range / 4,
self.boundary.datetime - self.boundary.dt / 4, self.boundary.date - self.boundary.dt / 4,
self.boundary.lon_range / 2, self.boundary.lon_range / 2,
self.boundary.lat_range / 2, self.boundary.lat_range / 2,
self.boundary.dt / 2, self.boundary.dt / 2,
...@@ -497,7 +513,7 @@ class OctTree: ...@@ -497,7 +513,7 @@ class OctTree:
SpaceTimeRectangle( SpaceTimeRectangle(
self.boundary.lon - self.boundary.lon_range / 4, self.boundary.lon - self.boundary.lon_range / 4,
self.boundary.lat - self.boundary.lat_range / 4, self.boundary.lat - self.boundary.lat_range / 4,
self.boundary.datetime - self.boundary.dt / 4, self.boundary.date - self.boundary.dt / 4,
self.boundary.lon_range / 2, self.boundary.lon_range / 2,
self.boundary.lat_range / 2, self.boundary.lat_range / 2,
self.boundary.dt / 2, self.boundary.dt / 2,
...@@ -510,7 +526,7 @@ class OctTree: ...@@ -510,7 +526,7 @@ class OctTree:
SpaceTimeRectangle( SpaceTimeRectangle(
self.boundary.lon + self.boundary.lon_range / 4, self.boundary.lon + self.boundary.lon_range / 4,
self.boundary.lat - self.boundary.lat_range / 4, self.boundary.lat - self.boundary.lat_range / 4,
self.boundary.datetime - self.boundary.dt / 4, self.boundary.date - self.boundary.dt / 4,
self.boundary.lon_range / 2, self.boundary.lon_range / 2,
self.boundary.lat_range / 2, self.boundary.lat_range / 2,
self.boundary.dt / 2, self.boundary.dt / 2,
...@@ -522,7 +538,7 @@ class OctTree: ...@@ -522,7 +538,7 @@ class OctTree:
self.divided = True self.divided = True
def _datetime_is_numeric(self) -> bool: def _datetime_is_numeric(self) -> bool:
return not isinstance(self.boundary.datetime, datetime) return not isinstance(self.boundary.date, datetime)
def insert(self, point: SpaceTimeRecord) -> bool: def insert(self, point: SpaceTimeRecord) -> bool:
""" """
...@@ -618,7 +634,7 @@ class OctTree: ...@@ -618,7 +634,7 @@ class OctTree:
self, self,
point: SpaceTimeRecord, point: SpaceTimeRecord,
dist: float, dist: float,
t_dist: timedelta, t_dist: datetime.timedelta,
points: SpaceTimeRecords | None = None, points: SpaceTimeRecords | None = None,
) -> SpaceTimeRecords: ) -> SpaceTimeRecords:
""" """
...@@ -637,7 +653,7 @@ class OctTree: ...@@ -637,7 +653,7 @@ class OctTree:
The distance for comparison. Note that Haversine distance is used The distance for comparison. Note that Haversine distance is used
as the distance metric as the query SpaceTimeRecord and OctTree are as the distance metric as the query SpaceTimeRecord and OctTree are
assumed to lie on the surface of Earth. assumed to lie on the surface of Earth.
t_dist : timedelta t_dist : datetime.timedelta
Max time gap between SpaceTimeRecords within the OctTree and the Max time gap between SpaceTimeRecords within the OctTree and the
query SpaceTimeRecord. Can be numeric if the OctTree boundaries, query SpaceTimeRecord. Can be numeric if the OctTree boundaries,
SpaceTimeRecords, and query SpaceTimeRecord have numeric datetime SpaceTimeRecords, and query SpaceTimeRecord have numeric datetime
......
...@@ -3,6 +3,7 @@ Constuctors for QuadTree classes that can decrease the number of comparisons ...@@ -3,6 +3,7 @@ Constuctors for QuadTree classes that can decrease the number of comparisons
for detecting nearby records for example for detecting nearby records for example
""" """
from dataclasses import dataclass
from datetime import datetime from datetime import datetime
from .distance_metrics import haversine, destination from .distance_metrics import haversine, destination
from .utils import LatitudeError from .utils import LatitudeError
...@@ -82,6 +83,7 @@ class Record: ...@@ -82,6 +83,7 @@ class Record:
return haversine(self.lon, self.lat, other.lon, other.lat) return haversine(self.lon, self.lat, other.lon, other.lat)
@dataclass
class Rectangle: class Rectangle:
""" """
A simple Rectangle class A simple Rectangle class
...@@ -98,46 +100,100 @@ class Rectangle: ...@@ -98,46 +100,100 @@ class Rectangle:
Height of the rectangle Height of the rectangle
""" """
def __init__( lon: float
self, lat: float
lon: float, lon_range: float
lat: float, lat_range: float
lon_range: float,
lat_range: float,
) -> None:
self.lon = lon
self.lat = lat
self.lon_range = lon_range
self.lat_range = lat_range
def __str__(self) -> str: def __post_init__(self):
return f"Rectangle(x = {self.lon}, y = {self.lat}, w = {self.lon_range}, h = {self.lat_range})" if self.lon > 180:
self.lon -= 360
if self.lat > 90 or self.lat < -90:
raise LatitudeError(
f"Central latitude value out of range {self.lat}, "
+ "should be between -90, 90 degrees"
)
def __eq__(self, other: object) -> bool: @property
return ( def west(self) -> float:
isinstance(other, Rectangle) """Western boundary of the Rectangle"""
and self.lon == other.lon return (((self.lon - self.lon_range / 2) + 540) % 360) - 180
and self.lat == other.lat
and self.lon_range == other.lon_range @property
and self.lat_range == other.lat_range def east(self) -> float:
"""Eastern boundary of the Rectangle"""
return (((self.lon + self.lon_range / 2) + 540) % 360) - 180
@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
@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 edge_dist(self) -> float:
"""Approximate maximum distance from the centre to an edge"""
corner_dist = max(
haversine(self.lon, self.lat, self.east, self.north),
haversine(self.lon, self.lat, self.east, self.south),
) )
if self.east * self.west < 0:
corner_dist = max(
corner_dist,
haversine(self.lon, self.lat, self.east, 0),
)
return corner_dist
def _test_east_west(self, lon: float) -> bool:
if self.lon_range >= 360:
# Rectangle encircles earth
return True
if self.east > self.lon and self.west < self.lon:
return lon <= self.east and lon >= self.west
if self.east < self.lon:
return not (lon > self.east and lon < self.west)
if self.west > self.lon:
return not (lon < self.east and lon > self.west)
return False
def _test_north_south(self, lat: float) -> bool:
return lat <= self.north and lat >= self.south
def contains(self, point: Record) -> bool: def contains(self, point: Record) -> bool:
"""Test if a point is contained within the Rectangle""" """Test if a point is contained within the Rectangle"""
return ( return self._test_north_south(point.lat) and self._test_east_west(
point.lon <= self.lon + self.lon_range / 2 point.lon
and point.lon >= self.lon - self.lon_range / 2
and point.lat <= self.lat + self.lat_range / 2
and point.lat >= self.lat - self.lat_range / 2
) )
def intersects(self, other: object) -> bool: def intersects(self, other: object) -> bool:
"""Test if another Rectangle object intersects this Rectangle""" """Test if another Rectangle object intersects this Rectangle"""
return isinstance(other, Rectangle) and not ( if not isinstance(other, Rectangle):
self.lon - self.lon_range / 2 > other.lon + other.lon_range / 2 raise TypeError(
or self.lon + self.lon_range / 2 < other.lon - other.lon_range / 2 f"other must be a Rectangle class, got {type(other)}"
or self.lat - self.lat_range / 2 > other.lat + other.lat_range / 2 )
or self.lat + self.lat_range / 2 < other.lat - other.lat_range / 2 if other.south > self.north:
# Other is fully north of self
return False
if other.north < self.south:
# Other is fully south of self
return False
# Handle east / west edges
return self._test_east_west(other.west) or self._test_east_west(
other.east
) )
def nearby( def nearby(
...@@ -147,30 +203,9 @@ class Rectangle: ...@@ -147,30 +203,9 @@ class Rectangle:
) -> bool: ) -> bool:
"""Check if point is nearby the Rectangle""" """Check if point is nearby the Rectangle"""
# QUESTION: Is this sufficient? Possibly it is overkill # QUESTION: Is this sufficient? Possibly it is overkill
corner_dist = max(
haversine(
self.lon,
self.lat,
self.lon + self.lon_range / 2,
self.lat + self.lat_range / 2,
),
haversine(
self.lon,
self.lat,
self.lon + self.lon_range / 2,
self.lat - self.lat_range / 2,
),
)
if (self.lat + self.lat_range / 2) * (
self.lat - self.lat_range / 2
) < 0:
corner_dist = max(
corner_dist,
haversine(self.lon, self.lat, self.lon + self.lon_range / 2, 0),
)
return ( return (
haversine(self.lon, self.lat, point.lon, point.lat) haversine(self.lon, self.lat, point.lon, point.lat)
<= dist + corner_dist <= dist + self.edge_dist
) )
...@@ -235,33 +270,11 @@ class Ellipse: ...@@ -235,33 +270,11 @@ class Ellipse:
def nearby_rect(self, rect: Rectangle) -> bool: def nearby_rect(self, rect: Rectangle) -> bool:
"""Test if a rectangle is near to the Ellipse""" """Test if a rectangle is near to the Ellipse"""
# TODO: Check corners, and 0 lat
corner_dist = max(
haversine(
rect.lon,
rect.lat,
rect.lon + rect.lon_range / 2,
rect.lat + rect.lat_range / 2,
),
haversine(
rect.lon,
rect.lat,
rect.lon + rect.lon_range / 2,
rect.lat - rect.lat_range / 2,
),
)
if (rect.lat + rect.lat_range / 2) * (
rect.lat - rect.lat_range / 2
) < 0:
corner_dist = max(
corner_dist,
haversine(rect.lon, rect.lat, rect.lon + rect.lon_range / 2, 0),
)
return ( return (
haversine(self.p1_lon, self.p1_lat, rect.lon, rect.lat) haversine(self.p1_lon, self.p1_lat, rect.lon, rect.lat)
<= corner_dist + self.a <= rect.edge_dist + self.a
and haversine(self.p2_lon, self.p2_lat, rect.lon, rect.lat) and haversine(self.p2_lon, self.p2_lat, rect.lon, rect.lat)
<= corner_dist + self.a <= rect.edge_dist + self.a
) )
......
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