Skip to content

Commit

Permalink
refactor: move extention to accessor
Browse files Browse the repository at this point in the history
  • Loading branch information
12rambau committed Nov 10, 2023
1 parent 12b7436 commit ca3a6bb
Show file tree
Hide file tree
Showing 17 changed files with 371 additions and 340 deletions.
3 changes: 2 additions & 1 deletion geetools/Array/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def __init__(self, obj: ee.Array):
"""Initialize the Array class."""
self._obj = obj

@classmethod
# -- alternative constructor -----------------------------------------------
def full(
self,
width: Union[int, float, ee.Number],
Expand Down Expand Up @@ -50,6 +50,7 @@ def full(
width, height = ee.Number(width).toInt(), ee.Number(height).toInt()
return ee.Array(ee.List.repeat(ee.List.repeat(value, width), height))

# -- data maniputlation ----------------------------------------------------
def set(
self,
x: Union[int, ee.number],
Expand Down
26 changes: 7 additions & 19 deletions geetools/ComputedObject/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,11 @@

import ee

from geetools.accessors import geetools_extend

def extend(cls):
"""Extends the cls class.

This is only used on the ``ComputedObject`` as it's the parent class of all.
Using the regular accessor would lead to a duplicate member and undesired behavior.
Parameters:
cls: Class to extend.
Returns:
Decorator for extending classes.
"""
return lambda f: (setattr(cls, f.__name__, f) or f)


@extend(ee.ComputedObject)
# -- types management ----------------------------------------------------------
@geetools_extend(ee.ComputedObject)
def isInstance(self, klass: Type) -> ee.Number:
"""Return 1 if the element is the passed type or 0 if not.
Expand All @@ -46,7 +34,8 @@ def isInstance(self, klass: Type) -> ee.Number:
return ee.Algorithms.ObjectType(self).compareTo(klass.__name__).eq(0)


@extend(ee.ComputedObject)
# -- .gee files ----------------------------------------------------------------
@geetools_extend(ee.ComputedObject)
def save(self, path: Union[str, Path]) -> Path:
"""Save a ``ComputedObject`` to a .gee file.
Expand Down Expand Up @@ -78,7 +67,7 @@ def save(self, path: Union[str, Path]) -> Path:
return path


@extend(ee.ComputedObject)
@geetools_extend(ee.ComputedObject)
@classmethod
def open(cls, path: Union[str, Path]) -> ee.ComputedObject:
"""Open a .gee file as a ComputedObject.
Expand Down Expand Up @@ -108,5 +97,4 @@ def open(cls, path: Union[str, Path]) -> ee.ComputedObject:
if (path := Path(path)).suffix != ".gee":
raise ValueError("File must be a .gee file")

computedObject = ee.deserializer.decode(json.loads(path.read_text()))
return cls(computedObject)
return ee.deserializer.decode(json.loads(path.read_text()))
78 changes: 41 additions & 37 deletions geetools/Date/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,17 @@ def __init__(self, obj: ee.Date):
"""Initialize the Date class."""
self._obj = obj

def toDatetime(self) -> datetime:
"""Convert a ``ee.Date`` to a ``datetime.datetime``.
# -- alternative constructor -----------------------------------------------
@classmethod
def fromEpoch(cls, number: int, unit: str = "day") -> ee.Date:
"""Set an the number of units since epoch (1970-01-01).
Parameters:
number: The number of units since the epoch.
unit: The unit to return the number of. One of: ``second``, ``minute``, ``hour``, ``day``, ``month``, ``year``.
Returns:
The ``datetime.datetime`` representation of the ``ee.Date``.
The date as a ``ee.Date`` object.
Examples:
.. jupyter-execute::
Expand All @@ -31,20 +37,22 @@ def toDatetime(self) -> datetime:
ee.Initialize()
d = ee.Date('2020-01-01').geetools.toDatetime()
d.strftime('%Y-%m-%d')
d = ee.Date.geetools.fromEpoch(49, 'year')
d.getInfo()
"""
return datetime.fromtimestamp(self._obj.millis().getInfo() / 1000.0)
cls.check_unit(unit)
return ee.Date(EE_EPOCH.isoformat()).advance(number, unit)

def getUnitSinceEpoch(self, unit: str = "day") -> ee.Number:
"""Get the number of units since epoch (1970-01-01).
@classmethod
def fromDOY(cls, doy: int, year: int) -> ee.Date:
"""Create a date from a day of year and a year.
Parameters:
unit: The unit to return the number of. One of: ``second``, ``minute``, ``hour``, ``day``, ``month``, ``year``.
doy: The day of year.
year: The year.
Returns:
The number of units since the epoch.
The date as a ``ee.Date`` object.
Examples:
.. jupyter-execute::
Expand All @@ -53,22 +61,18 @@ def getUnitSinceEpoch(self, unit: str = "day") -> ee.Number:
ee.Initialize()
d = ee.Date('2020-01-01').geetools.getUnitSinceEpoch('year')
d = ee.Date.geetools.fromDOY(1, 2020)
d.getInfo()
"""
self._check_unit(unit)
return self._obj.difference(EE_EPOCH, unit).toInt()

@classmethod
def fromEpoch(cls, number: int, unit: str = "day") -> ee.Date:
"""Set an the number of units since epoch (1970-01-01).
doy, year = ee.Number(doy).toInt(), ee.Number(year).toInt()
return ee.Date.fromYMD(year, 1, 1).advance(doy.subtract(1), "day")

Parameters:
number: The number of units since the epoch.
unit: The unit to return the number of. One of: ``second``, ``minute``, ``hour``, ``day``, ``month``, ``year``.
# -- export date -----------------------------------------------------------
def to_datetime(self) -> datetime:
"""Convert a ``ee.Date`` to a ``datetime.datetime``.
Returns:
The date as a ``ee.Date`` object.
The ``datetime.datetime`` representation of the ``ee.Date``.
Examples:
.. jupyter-execute::
Expand All @@ -77,22 +81,21 @@ def fromEpoch(cls, number: int, unit: str = "day") -> ee.Date:
ee.Initialize()
d = ee.Date.geetools.fromEpoch(49, 'year')
d.getInfo()
d = ee.Date('2020-01-01').geetools.toDatetime()
d.strftime('%Y-%m-%d')
"""
cls._check_unit(unit)
return ee.Date(EE_EPOCH.isoformat()).advance(number, unit)
return datetime.fromtimestamp(self._obj.millis().getInfo() / 1000.0)

@classmethod
def fromDOY(cls, doy: int, year: int) -> ee.Date:
"""Create a date from a day of year and a year.
# -- date operations -------------------------------------------------------
def getUnitSinceEpoch(self, unit: str = "day") -> ee.Number:
"""Get the number of units since epoch (1970-01-01).
Parameters:
doy: The day of year.
year: The year.
unit: The unit to return the number of. One of: ``second``, ``minute``, ``hour``, ``day``, ``month``, ``year``.
Returns:
The date as a ``ee.Date`` object.
The number of units since the epoch.
Examples:
.. jupyter-execute::
Expand All @@ -101,11 +104,11 @@ def fromDOY(cls, doy: int, year: int) -> ee.Date:
ee.Initialize()
d = ee.Date.geetools.fromDOY(1, 2020)
d = ee.Date('2020-01-01').geetools.getUnitSinceEpoch('year')
d.getInfo()
"""
doy, year = ee.Number(doy).toInt(), ee.Number(year).toInt()
return ee.Date.fromYMD(year, 1, 1).advance(doy.subtract(1), "day")
self.check_unit(unit)
return self._obj.difference(EE_EPOCH, unit).toInt()

def isLeap(self) -> ee.Number:
"""Check if the year of the date is a leap year.
Expand Down Expand Up @@ -133,8 +136,9 @@ def isLeap(self) -> ee.Number:

return isLeap.toInt()

@classmethod
def _check_unit(cls, unit: str) -> None:
# -- helper methods --------------------------------------------------------
@staticmethod
def check_unit(unit: str) -> None:
"""Check if the unit is valid."""
if unit not in (units := ["second", "minute", "hour", "day", "month", "year"]):
raise ValueError(f"unit must be one of: {','.join(units)}")
20 changes: 9 additions & 11 deletions geetools/DateRange/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,8 @@ def __init__(self, obj: ee.DateRange):
"""Initialize the DateRange class."""
self._obj = obj

def split(
self,
interval: Union[int, ee.Number],
unit: str = "day",
) -> ee.List:
# -- date range operations -------------------------------------------------
def split(self, interval: Union[int, ee.Number], unit: str = "day") -> ee.List:
"""Convert a ``ee.DateRange`` to a list of ``ee.DateRange``.
The DateRange will be split in multiple DateRanges of the specified interval and Unit.
Expand All @@ -43,7 +40,8 @@ def split(
d = ee.DateRange('2020-01-01', '2020-01-31').geetools.split(1, 'day')
d.getInfo()
"""
interval = ee.Number(interval).toInt().multiply(self._unitMillis(unit))
self.check_unit(unit)
interval = ee.Number(interval).toInt().multiply(self.unitMillis(unit))
start, end = self._obj.start().millis(), self._obj.end().millis()

timestampList = ee.List.sequence(start, end, interval)
Expand All @@ -56,16 +54,16 @@ def split(
)
)

@classmethod
def _check_unit(cls, unit: str) -> None:
# -- utils -----------------------------------------------------------------
@staticmethod
def check_unit(unit: str) -> None:
"""Check if the unit is valid."""
if unit not in (units := ["second", "minute", "hour", "day", "month", "year"]):
raise ValueError(f"unit must be one of: {','.join(units)}")

@classmethod
def _unitMillis(cls, unit: str) -> ee.Number:
@staticmethod
def unitMillis(unit: str) -> ee.Number:
"""Get the milliseconds of a unit."""
cls._check_unit(unit)
millis = {
"second": 1000,
"minute": 1000 * 60,
Expand Down
3 changes: 2 additions & 1 deletion geetools/Dictionary/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def __init__(self, obj: ee.Dictionary):
"""Initialize the Dictionary class."""
self._obj = obj

@classmethod
# -- alternative constructor -----------------------------------------------
def fromPairs(self, list: Union[list, ee.List]) -> ee.Dictionary:
"""Create a dictionary from a list of [[key, value], ...]] pairs.
Expand All @@ -41,6 +41,7 @@ def fromPairs(self, list: Union[list, ee.List]) -> ee.Dictionary:
values = list.map(lambda pair: ee.List(pair).get(1))
return ee.Dictionary.fromLists(keys, values)

# -- dictionary operations -------------------------------------------------
def sort(self) -> ee.Dictionary:
"""Sort the dictionary by keys in ascending order.
Expand Down
2 changes: 1 addition & 1 deletion geetools/Filter/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def __init__(self, obj: ee.Filter):
"""Initialize the Filter class."""
self._obj = obj

@classmethod
# -- date filters ----------------------------------------------------------
def dateRange(self, range: ee.DateRange) -> Any:
"""Filter by daterange.
Expand Down
6 changes: 6 additions & 0 deletions geetools/Float/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
"""Placeholder Float class to be used in the isInstance method."""
from __future__ import annotations

import ee

from geetools.accessors import geetools_extend


@geetools_extend(ee)
class Float:
"""Placeholder Float class to be used in the isInstance method."""

Expand Down
Loading

0 comments on commit ca3a6bb

Please sign in to comment.