Module ocean_science_utilities.interpolate.geometry
Expand source code
import numpy as np
import pandas as pd
from dataclasses import dataclass
from datetime import datetime
from numbers import Number
from typing import Any, Sequence, List, Mapping, Union
from ocean_science_utilities.interpolate.general import interpolate_periodic
from ocean_science_utilities.tools.time import to_datetime64, to_datetime_utc
class _Geometry:
pass
@dataclass()
class SpatialPoint(_Geometry):
latitude: float
longitude: float
id: str
@property
def is_valid(self) -> bool:
return np.isfinite(self.latitude) and np.isfinite(self.longitude)
@dataclass()
class SpaceTimePoint(SpatialPoint):
time: datetime
@classmethod
def from_spatial_point(cls, point: SpatialPoint, time: datetime):
return cls(point.latitude, point.longitude, point.id, time)
@dataclass()
class Cluster(_Geometry):
"""
A cluster is a set of points, each identified by unique id.
"""
points: Mapping[str, SpatialPoint]
@classmethod
def from_lat_lon_arrays(cls, lats: List[float], lons: List[float]):
points = {}
for index in range(0, len(lats)):
_id = str(index)
points[_id] = SpatialPoint(lats[index], lons[index], _id)
return cls(points)
@property
def latitude(self) -> np.ndarray:
return np.array([x.latitude for x in self.points.values()])
@property
def longitude(self) -> np.ndarray:
return np.array([x.longitude for x in self.points.values()])
@property
def ids(self) -> Sequence[str]:
return [x for x in self.points.keys()]
@dataclass()
class ClusterStack(_Geometry):
"""
A cluster timestack is a stack of clusters in time, e.g. a cluster of
spotters as it evolves in time.
"""
time: np.ndarray
clusters: Sequence[Cluster]
def as_track_set(self) -> "TrackSet":
return TrackSet.from_clusters(self)
@classmethod
def from_track_set(cls, track_set: "TrackSet", time):
return track_set.as_cluster_time_stack(time)
def __len__(self):
return len(self.time)
class Track(_Geometry):
"""
A track is the drift track of a single buoy in time
"""
def __init__(self, points: List[SpaceTimePoint], id):
points = list(filter(lambda x: x.is_valid, points))
self.points = points
self.id = id
@property
def time(self) -> np.ndarray:
return to_datetime64([x.time for x in self.points]) # type: ignore
@property
def latitude(self) -> np.ndarray:
return np.array([x.latitude for x in self.points])
@property
def longitude(self) -> np.ndarray:
return np.array([x.longitude for x in self.points])
def interpolate(self, target_time) -> "Track":
target_time = to_datetime64(target_time) # type: ignore
time = self.time
latitude = interpolate_periodic(
time.astype("float64"),
self.latitude,
target_time.astype("float64"),
left=self.latitude[0],
right=self.latitude[-1],
)
longitude = interpolate_periodic(
time.astype("float64"),
self.longitude,
target_time.astype("float64"),
left=self.longitude[0],
right=self.longitude[-1],
fp_period=360,
)
return self.from_arrays(latitude, longitude, target_time, self.id)
@classmethod
def from_arrays(cls, latitude, longitude, time, id) -> "Track":
points = []
for lat, lon, t in zip(latitude, longitude, time):
time_utc = to_datetime_utc(t) # type: ignore
points.append(SpaceTimePoint(lat, lon, id, time_utc)) # type: ignore
return cls(points, id)
@classmethod
def from_spotter(cls, spotter_id, spotter):
points = []
if isinstance(spotter, pd.DataFrame):
for latitude, longitude, time in zip(
spotter["latitude"].values,
spotter["longitude"].values,
spotter["time"].values,
):
points.append(
SpaceTimePoint(
time=time, latitude=latitude, longitude=longitude, id=spotter_id
)
)
elif hasattr(spotter, "dataset"):
for latitude, longitude, time in zip(
spotter.dataset["latitude"].values,
spotter.dataset["longitude"].values,
spotter.dataset["time"].values,
):
points.append(
SpaceTimePoint(
time=time, latitude=latitude, longitude=longitude, id=spotter_id
)
)
else:
for x in spotter:
points.append(
SpaceTimePoint(
time=x.time,
latitude=x.latitude,
longitude=x.longitude,
id=spotter_id,
)
)
return cls(points, spotter_id)
def __len__(self):
return len(self.points)
@dataclass()
class TrackSet(_Geometry):
"""
A collection of tracks is a set of tracks for multiple buoys.
"""
tracks: Mapping[str, Track]
@classmethod
def from_spotters(cls, spotters: Mapping):
tracks = {}
for spotter_id, spotter in spotters.items():
tracks[spotter_id] = Track.from_spotter(spotter_id, spotter)
return cls(tracks)
@classmethod
def from_clusters(cls, cluster_time_stack: "ClusterStack"):
tracks = {}
for cluster, time in zip(cluster_time_stack.clusters, cluster_time_stack.time):
for _id, point in cluster.points.items():
space_time_point = SpaceTimePoint(
longitude=point.latitude,
latitude=point.longitude,
time=time,
id=_id,
)
if _id not in tracks:
tracks[_id] = Track([space_time_point], _id)
else:
tracks[_id].points.append(space_time_point)
return cls(tracks)
def as_cluster_time_stack(self, time):
tracks = self.interpolate(time)
clusters = [{} for x in range(len(time))]
for _id, track in tracks.tracks.items():
for ii, point in enumerate(track.points):
if point.is_valid:
index = np.searchsorted(
time, [to_datetime64(point.time)], side="left"
)[0]
clusters[index][_id] = SpatialPoint(
point.latitude, point.longitude, _id
)
clusters = [Cluster(x) for x in clusters]
return ClusterStack(time, clusters)
def interpolate(self, time) -> "TrackSet":
time = to_datetime64(time)
tracks = {}
for id, track in self.tracks.items():
tracks[id] = track.interpolate(time)
return TrackSet(tracks)
@classmethod
def from_cluster(cls, cluster: Cluster, time: np.ndarray) -> "TrackSet":
tracks = {}
for _id, point in cluster.points.items():
track_points = [SpaceTimePoint.from_spatial_point(point, t) for t in time]
tracks[point.id] = Track(points=track_points, id=point.id)
return TrackSet(tracks)
Geometry = Union[
_Geometry, Sequence[np.ndarray], Sequence[Sequence], Sequence[Number], Mapping
]
def convert_to_cluster_stack(geometry: Geometry, time: np.ndarray) -> ClusterStack:
if isinstance(geometry, TrackSet):
return geometry.as_cluster_time_stack(time)
elif isinstance(geometry, Cluster):
return ClusterStack(time, [geometry for _ in time])
elif isinstance(geometry, Track):
tracks = TrackSet({"track": geometry})
return ClusterStack.from_track_set(tracks, time)
elif isinstance(geometry, ClusterStack):
return geometry
elif isinstance(geometry, Sequence):
tracks = convert_to_track_set(geometry, time)
return ClusterStack.from_track_set(tracks, time)
elif isinstance(geometry, Mapping):
tracks = convert_to_track_set(geometry, time)
return ClusterStack.from_track_set(tracks, time)
raise ValueError("Unaccepted arguments")
def convert_to_track_set(geometry: Geometry, time: np.ndarray) -> TrackSet:
if isinstance(geometry, TrackSet):
return geometry.interpolate(time)
elif isinstance(geometry, Cluster):
return TrackSet.from_clusters(ClusterStack(time, [geometry for _ in time]))
elif isinstance(geometry, Track):
return TrackSet({"track": geometry.interpolate(time)})
elif isinstance(geometry, ClusterStack):
return geometry.as_track_set()
elif isinstance(geometry, Sequence):
if not (isinstance(geometry[0], Sequence) or isinstance(geometry[0], Sequence)):
_geometry: Sequence[Any] = [[geometry[0]], [geometry[1]]]
else:
_geometry = geometry
cluster = Cluster.from_lat_lon_arrays(_geometry[0], _geometry[1])
return TrackSet.from_cluster(cluster, time)
elif isinstance(geometry, Mapping):
return TrackSet.from_spotters(geometry).interpolate(time)
raise ValueError("Unaccepted arguments")
Functions
def convert_to_cluster_stack(geometry: Union[ocean_science_utilities.interpolate.geometry._Geometry, Sequence[numpy.ndarray], Sequence[Sequence], Sequence[numbers.Number], Mapping], time: numpy.ndarray) ‑> ClusterStack
-
Expand source code
def convert_to_cluster_stack(geometry: Geometry, time: np.ndarray) -> ClusterStack: if isinstance(geometry, TrackSet): return geometry.as_cluster_time_stack(time) elif isinstance(geometry, Cluster): return ClusterStack(time, [geometry for _ in time]) elif isinstance(geometry, Track): tracks = TrackSet({"track": geometry}) return ClusterStack.from_track_set(tracks, time) elif isinstance(geometry, ClusterStack): return geometry elif isinstance(geometry, Sequence): tracks = convert_to_track_set(geometry, time) return ClusterStack.from_track_set(tracks, time) elif isinstance(geometry, Mapping): tracks = convert_to_track_set(geometry, time) return ClusterStack.from_track_set(tracks, time) raise ValueError("Unaccepted arguments")
def convert_to_track_set(geometry: Union[ocean_science_utilities.interpolate.geometry._Geometry, Sequence[numpy.ndarray], Sequence[Sequence], Sequence[numbers.Number], Mapping], time: numpy.ndarray) ‑> TrackSet
-
Expand source code
def convert_to_track_set(geometry: Geometry, time: np.ndarray) -> TrackSet: if isinstance(geometry, TrackSet): return geometry.interpolate(time) elif isinstance(geometry, Cluster): return TrackSet.from_clusters(ClusterStack(time, [geometry for _ in time])) elif isinstance(geometry, Track): return TrackSet({"track": geometry.interpolate(time)}) elif isinstance(geometry, ClusterStack): return geometry.as_track_set() elif isinstance(geometry, Sequence): if not (isinstance(geometry[0], Sequence) or isinstance(geometry[0], Sequence)): _geometry: Sequence[Any] = [[geometry[0]], [geometry[1]]] else: _geometry = geometry cluster = Cluster.from_lat_lon_arrays(_geometry[0], _geometry[1]) return TrackSet.from_cluster(cluster, time) elif isinstance(geometry, Mapping): return TrackSet.from_spotters(geometry).interpolate(time) raise ValueError("Unaccepted arguments")
Classes
class Cluster (points: Mapping[str, SpatialPoint])
-
A cluster is a set of points, each identified by unique id.
Expand source code
@dataclass() class Cluster(_Geometry): """ A cluster is a set of points, each identified by unique id. """ points: Mapping[str, SpatialPoint] @classmethod def from_lat_lon_arrays(cls, lats: List[float], lons: List[float]): points = {} for index in range(0, len(lats)): _id = str(index) points[_id] = SpatialPoint(lats[index], lons[index], _id) return cls(points) @property def latitude(self) -> np.ndarray: return np.array([x.latitude for x in self.points.values()]) @property def longitude(self) -> np.ndarray: return np.array([x.longitude for x in self.points.values()]) @property def ids(self) -> Sequence[str]: return [x for x in self.points.keys()]
Ancestors
- ocean_science_utilities.interpolate.geometry._Geometry
Class variables
var points : Mapping[str, SpatialPoint]
Static methods
def from_lat_lon_arrays(lats: List[float], lons: List[float])
-
Expand source code
@classmethod def from_lat_lon_arrays(cls, lats: List[float], lons: List[float]): points = {} for index in range(0, len(lats)): _id = str(index) points[_id] = SpatialPoint(lats[index], lons[index], _id) return cls(points)
Instance variables
var ids : Sequence[str]
-
Expand source code
@property def ids(self) -> Sequence[str]: return [x for x in self.points.keys()]
var latitude : numpy.ndarray
-
Expand source code
@property def latitude(self) -> np.ndarray: return np.array([x.latitude for x in self.points.values()])
var longitude : numpy.ndarray
-
Expand source code
@property def longitude(self) -> np.ndarray: return np.array([x.longitude for x in self.points.values()])
class ClusterStack (time: numpy.ndarray, clusters: Sequence[Cluster])
-
A cluster timestack is a stack of clusters in time, e.g. a cluster of spotters as it evolves in time.
Expand source code
@dataclass() class ClusterStack(_Geometry): """ A cluster timestack is a stack of clusters in time, e.g. a cluster of spotters as it evolves in time. """ time: np.ndarray clusters: Sequence[Cluster] def as_track_set(self) -> "TrackSet": return TrackSet.from_clusters(self) @classmethod def from_track_set(cls, track_set: "TrackSet", time): return track_set.as_cluster_time_stack(time) def __len__(self): return len(self.time)
Ancestors
- ocean_science_utilities.interpolate.geometry._Geometry
Class variables
var clusters : Sequence[Cluster]
var time : numpy.ndarray
Static methods
def from_track_set(track_set: TrackSet, time)
-
Expand source code
@classmethod def from_track_set(cls, track_set: "TrackSet", time): return track_set.as_cluster_time_stack(time)
Methods
def as_track_set(self) ‑> TrackSet
-
Expand source code
def as_track_set(self) -> "TrackSet": return TrackSet.from_clusters(self)
class SpaceTimePoint (latitude: float, longitude: float, id: str, time: datetime.datetime)
-
SpaceTimePoint(latitude: float, longitude: float, id: str, time: datetime.datetime)
Expand source code
@dataclass() class SpaceTimePoint(SpatialPoint): time: datetime @classmethod def from_spatial_point(cls, point: SpatialPoint, time: datetime): return cls(point.latitude, point.longitude, point.id, time)
Ancestors
- SpatialPoint
- ocean_science_utilities.interpolate.geometry._Geometry
Class variables
var time : datetime.datetime
Static methods
def from_spatial_point(point: SpatialPoint, time: datetime.datetime)
-
Expand source code
@classmethod def from_spatial_point(cls, point: SpatialPoint, time: datetime): return cls(point.latitude, point.longitude, point.id, time)
class SpatialPoint (latitude: float, longitude: float, id: str)
-
SpatialPoint(latitude: float, longitude: float, id: str)
Expand source code
@dataclass() class SpatialPoint(_Geometry): latitude: float longitude: float id: str @property def is_valid(self) -> bool: return np.isfinite(self.latitude) and np.isfinite(self.longitude)
Ancestors
- ocean_science_utilities.interpolate.geometry._Geometry
Subclasses
Class variables
var id : str
var latitude : float
var longitude : float
Instance variables
var is_valid : bool
-
Expand source code
@property def is_valid(self) -> bool: return np.isfinite(self.latitude) and np.isfinite(self.longitude)
class Track (points: List[SpaceTimePoint], id)
-
A track is the drift track of a single buoy in time
Expand source code
class Track(_Geometry): """ A track is the drift track of a single buoy in time """ def __init__(self, points: List[SpaceTimePoint], id): points = list(filter(lambda x: x.is_valid, points)) self.points = points self.id = id @property def time(self) -> np.ndarray: return to_datetime64([x.time for x in self.points]) # type: ignore @property def latitude(self) -> np.ndarray: return np.array([x.latitude for x in self.points]) @property def longitude(self) -> np.ndarray: return np.array([x.longitude for x in self.points]) def interpolate(self, target_time) -> "Track": target_time = to_datetime64(target_time) # type: ignore time = self.time latitude = interpolate_periodic( time.astype("float64"), self.latitude, target_time.astype("float64"), left=self.latitude[0], right=self.latitude[-1], ) longitude = interpolate_periodic( time.astype("float64"), self.longitude, target_time.astype("float64"), left=self.longitude[0], right=self.longitude[-1], fp_period=360, ) return self.from_arrays(latitude, longitude, target_time, self.id) @classmethod def from_arrays(cls, latitude, longitude, time, id) -> "Track": points = [] for lat, lon, t in zip(latitude, longitude, time): time_utc = to_datetime_utc(t) # type: ignore points.append(SpaceTimePoint(lat, lon, id, time_utc)) # type: ignore return cls(points, id) @classmethod def from_spotter(cls, spotter_id, spotter): points = [] if isinstance(spotter, pd.DataFrame): for latitude, longitude, time in zip( spotter["latitude"].values, spotter["longitude"].values, spotter["time"].values, ): points.append( SpaceTimePoint( time=time, latitude=latitude, longitude=longitude, id=spotter_id ) ) elif hasattr(spotter, "dataset"): for latitude, longitude, time in zip( spotter.dataset["latitude"].values, spotter.dataset["longitude"].values, spotter.dataset["time"].values, ): points.append( SpaceTimePoint( time=time, latitude=latitude, longitude=longitude, id=spotter_id ) ) else: for x in spotter: points.append( SpaceTimePoint( time=x.time, latitude=x.latitude, longitude=x.longitude, id=spotter_id, ) ) return cls(points, spotter_id) def __len__(self): return len(self.points)
Ancestors
- ocean_science_utilities.interpolate.geometry._Geometry
Static methods
def from_arrays(latitude, longitude, time, id) ‑> Track
-
Expand source code
@classmethod def from_arrays(cls, latitude, longitude, time, id) -> "Track": points = [] for lat, lon, t in zip(latitude, longitude, time): time_utc = to_datetime_utc(t) # type: ignore points.append(SpaceTimePoint(lat, lon, id, time_utc)) # type: ignore return cls(points, id)
def from_spotter(spotter_id, spotter)
-
Expand source code
@classmethod def from_spotter(cls, spotter_id, spotter): points = [] if isinstance(spotter, pd.DataFrame): for latitude, longitude, time in zip( spotter["latitude"].values, spotter["longitude"].values, spotter["time"].values, ): points.append( SpaceTimePoint( time=time, latitude=latitude, longitude=longitude, id=spotter_id ) ) elif hasattr(spotter, "dataset"): for latitude, longitude, time in zip( spotter.dataset["latitude"].values, spotter.dataset["longitude"].values, spotter.dataset["time"].values, ): points.append( SpaceTimePoint( time=time, latitude=latitude, longitude=longitude, id=spotter_id ) ) else: for x in spotter: points.append( SpaceTimePoint( time=x.time, latitude=x.latitude, longitude=x.longitude, id=spotter_id, ) ) return cls(points, spotter_id)
Instance variables
var latitude : numpy.ndarray
-
Expand source code
@property def latitude(self) -> np.ndarray: return np.array([x.latitude for x in self.points])
var longitude : numpy.ndarray
-
Expand source code
@property def longitude(self) -> np.ndarray: return np.array([x.longitude for x in self.points])
var time : numpy.ndarray
-
Expand source code
@property def time(self) -> np.ndarray: return to_datetime64([x.time for x in self.points]) # type: ignore
Methods
def interpolate(self, target_time) ‑> Track
-
Expand source code
def interpolate(self, target_time) -> "Track": target_time = to_datetime64(target_time) # type: ignore time = self.time latitude = interpolate_periodic( time.astype("float64"), self.latitude, target_time.astype("float64"), left=self.latitude[0], right=self.latitude[-1], ) longitude = interpolate_periodic( time.astype("float64"), self.longitude, target_time.astype("float64"), left=self.longitude[0], right=self.longitude[-1], fp_period=360, ) return self.from_arrays(latitude, longitude, target_time, self.id)
class TrackSet (tracks: Mapping[str, Track])
-
A collection of tracks is a set of tracks for multiple buoys.
Expand source code
@dataclass() class TrackSet(_Geometry): """ A collection of tracks is a set of tracks for multiple buoys. """ tracks: Mapping[str, Track] @classmethod def from_spotters(cls, spotters: Mapping): tracks = {} for spotter_id, spotter in spotters.items(): tracks[spotter_id] = Track.from_spotter(spotter_id, spotter) return cls(tracks) @classmethod def from_clusters(cls, cluster_time_stack: "ClusterStack"): tracks = {} for cluster, time in zip(cluster_time_stack.clusters, cluster_time_stack.time): for _id, point in cluster.points.items(): space_time_point = SpaceTimePoint( longitude=point.latitude, latitude=point.longitude, time=time, id=_id, ) if _id not in tracks: tracks[_id] = Track([space_time_point], _id) else: tracks[_id].points.append(space_time_point) return cls(tracks) def as_cluster_time_stack(self, time): tracks = self.interpolate(time) clusters = [{} for x in range(len(time))] for _id, track in tracks.tracks.items(): for ii, point in enumerate(track.points): if point.is_valid: index = np.searchsorted( time, [to_datetime64(point.time)], side="left" )[0] clusters[index][_id] = SpatialPoint( point.latitude, point.longitude, _id ) clusters = [Cluster(x) for x in clusters] return ClusterStack(time, clusters) def interpolate(self, time) -> "TrackSet": time = to_datetime64(time) tracks = {} for id, track in self.tracks.items(): tracks[id] = track.interpolate(time) return TrackSet(tracks) @classmethod def from_cluster(cls, cluster: Cluster, time: np.ndarray) -> "TrackSet": tracks = {} for _id, point in cluster.points.items(): track_points = [SpaceTimePoint.from_spatial_point(point, t) for t in time] tracks[point.id] = Track(points=track_points, id=point.id) return TrackSet(tracks)
Ancestors
- ocean_science_utilities.interpolate.geometry._Geometry
Class variables
var tracks : Mapping[str, Track]
Static methods
def from_cluster(cluster: Cluster, time: numpy.ndarray) ‑> TrackSet
-
Expand source code
@classmethod def from_cluster(cls, cluster: Cluster, time: np.ndarray) -> "TrackSet": tracks = {} for _id, point in cluster.points.items(): track_points = [SpaceTimePoint.from_spatial_point(point, t) for t in time] tracks[point.id] = Track(points=track_points, id=point.id) return TrackSet(tracks)
def from_clusters(cluster_time_stack: ClusterStack)
-
Expand source code
@classmethod def from_clusters(cls, cluster_time_stack: "ClusterStack"): tracks = {} for cluster, time in zip(cluster_time_stack.clusters, cluster_time_stack.time): for _id, point in cluster.points.items(): space_time_point = SpaceTimePoint( longitude=point.latitude, latitude=point.longitude, time=time, id=_id, ) if _id not in tracks: tracks[_id] = Track([space_time_point], _id) else: tracks[_id].points.append(space_time_point) return cls(tracks)
def from_spotters(spotters: Mapping)
-
Expand source code
@classmethod def from_spotters(cls, spotters: Mapping): tracks = {} for spotter_id, spotter in spotters.items(): tracks[spotter_id] = Track.from_spotter(spotter_id, spotter) return cls(tracks)
Methods
def as_cluster_time_stack(self, time)
-
Expand source code
def as_cluster_time_stack(self, time): tracks = self.interpolate(time) clusters = [{} for x in range(len(time))] for _id, track in tracks.tracks.items(): for ii, point in enumerate(track.points): if point.is_valid: index = np.searchsorted( time, [to_datetime64(point.time)], side="left" )[0] clusters[index][_id] = SpatialPoint( point.latitude, point.longitude, _id ) clusters = [Cluster(x) for x in clusters] return ClusterStack(time, clusters)
def interpolate(self, time) ‑> TrackSet
-
Expand source code
def interpolate(self, time) -> "TrackSet": time = to_datetime64(time) tracks = {} for id, track in self.tracks.items(): tracks[id] = track.interpolate(time) return TrackSet(tracks)