-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathheatmap_grid.py
More file actions
executable file
·88 lines (69 loc) · 2.91 KB
/
heatmap_grid.py
File metadata and controls
executable file
·88 lines (69 loc) · 2.91 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
"""Models representing various spatial coordinate systems."""
from datetime import datetime, timedelta
from typing import TYPE_CHECKING, List, Union
from pydantic import BaseModel, confloat
if TYPE_CHECKING:
longitude_type = float
latitude_type = float
else:
# Constrained types in pydantic currently raise an invalid type error from MyPy,
# likely related to this issue:
# https://github.com/samuelcolvin/pydantic/issues/3080
longitude_type = confloat(ge=-180, lt=180)
latitude_type = confloat(gt=-90, lt=90)
def from_to_by(start: float, stop: float, step: float, digits: int = 8) -> List[float]:
"""Sequence between start and stop (inclusive) by step, rounded to digits."""
length = round((stop - start) / step)
return [round(start + step * x, digits) for x in range(length + 1)]
class Point(BaseModel):
x: longitude_type
y: latitude_type
class GridExtent(BaseModel):
xmin: longitude_type
xmax: longitude_type
ymin: latitude_type
ymax: latitude_type
def contains(self, other: Union["GridTimeExtent", "GridExtent"]) -> bool:
"""Check if the calling instance contains another extent."""
return (
self.xmin <= other.xmin
and self.xmax >= other.xmax
and self.ymin <= other.ymin
and self.ymax >= other.ymax
)
def __contains__(self, other: Union["GridTimeExtent", "GridExtent"]) -> bool:
return self.contains(other)
class GridResolution(BaseModel):
xres: float
yres: float
class Grid(GridExtent, GridResolution):
def to_points(self) -> List[Point]:
"""Returns points placed on grid vertices."""
xs = from_to_by(self.xmin, self.xmax, self.xres)
ys = from_to_by(self.ymin, self.ymax, self.yres)
return [Point(x=x, y=y) for x in xs for y in ys]
class GridTimeExtent(GridExtent):
tmin: datetime
tmax: datetime
def expand(self, dx: float, dy: float, dt: timedelta) -> "GridTimeExtent":
"""Returns an expanded GridTimeExtent without modifying the existing object."""
tmin = min(self.tmin, self.tmin + dt)
tmax = max(self.tmax, self.tmax + dt)
return GridTimeExtent(
xmin=self.xmin - dx,
xmax=self.xmax + dx,
ymin=self.ymin - dy,
ymax=self.ymax + dy,
tmin=tmin,
tmax=tmax,
)
def contains(self, other: Union["GridTimeExtent", GridExtent]) -> bool:
"""Check if the calling instance contains another extent."""
contains_spatial_extent = super().__contains__(other)
if isinstance(other, GridExtent):
return contains_spatial_extent
else:
contains_time_extent = self.tmin <= other.tmin and self.tmax >= other.tmax
return contains_spatial_extent and contains_time_extent
def __contains__(self, other: Union["GridTimeExtent", GridExtent]) -> bool:
return self.contains(other)